├── README.md ├── wipe_towers_v01.pl └── wipe_towers_v02.pl /README.md: -------------------------------------------------------------------------------- 1 | # Multi-Extrusion post-processing scripts for Slic3r 2 | useful postprocessing scripts for Slic3r for adding wipe towers and other multi-extrusion features. Written in Perl. 3 | ![example g-code](http://i.imgur.com/lOzlO5L.png) 4 | ## Available scripts 5 | ### wipe_towers_tc.pl 6 | - adding wipe towers 7 | - resorting print order to a sequential build for finest results 8 | - highly configurable 9 | 10 | ## Changelog 11 | ### to do 12 | - [ ] add compatibility for relative coordinate mode 13 | - [ ] add compatibility for absolute extrusion mode 14 | - [ ] Slic3r environment variables cannot be read, therefore settings and parameters have to be manually declared within the custom "Start G-code" 15 | - [ ] add workaround for Slic3rs lack of retraction on print start for inactive extruders 16 | 17 | ### v1 18 | - [x] scripts are tested and working 19 | - [x] printing results are spotless and really awesome 20 | - [x] compatible to absolute coordinate mode 21 | - [x] compatible to relative extrusion mode 22 | 23 | ### v2 24 | - [x] some bugfixes, including support for >2 extruders, suggested by PxT (thx!!) 25 | - [x] added optional parameter "forceToolChanges", defaults to true 26 | - [x] travelLifts now happen in both directions 27 | 28 | ## How to use 29 | In order to get the scripts working properly, I suggest creating print and printer settings in slic3r exclusively for use with those scripts, and modifying these settings as described below. 30 | 31 | ### Installation 32 | Copy the scripts to a directory of your choice, note that directory. 33 | 34 | ### In the print settings: 35 | Add the full path to the script as noted above in the _Print Settings -> Output options -> Post-processing scripts_ field 36 | I suggest only using one post-processing Script at a time. 37 | 38 | ### In the printer settings: 39 | 1. Tick "Use relative E distances" in Printer Settings -> General 40 | 41 | 2. In _Printer Settings -> Custom G-Code_, add the following to the very beginning of your "Start G-code", your own custom Start G-code can follow after that: 42 | ``` 43 | ; WIPE TOWER PARAMS 44 | ; forceToolChanges=true 45 | ; nozzleDiameter=[nozzle_diameter] 46 | ; filamentDiameter=[filament_diameter] 47 | ; extrusionWidth=[extrusion_width] 48 | ; layerHeight=[layer_height] 49 | ; firstLayerHeight=[first_layer_height] 50 | ; extrusionMultiplier=[extrusion_multiplier] 51 | ; firstLayerExtrusionMultiplier=4 52 | ; retractionLength=[retract_length] 53 | ; toolChangeRetractionLength=[retract_length_toolchange] 54 | ; bedWidth=[bed_size_X] 55 | ; bedDepth=[bed_size_Y] 56 | ; extruders=2 57 | ; wipeTowerX=80 58 | ; wipeTowerY=155 59 | ; wipeTowerW=10 60 | ; wipeTowerH=10 61 | ; wipeTowerSpacing=20 62 | ; wipeTowerLoops=5 63 | ; wipeTowerBrimLoops=7 64 | ; wipeOffset=2 65 | ; purgeOffset=1.33 66 | ; wipeLift=5 67 | ; travelLift=1 68 | ; purgeAmount=0.5 69 | ; retractionFeedrate=[retract_speed] 70 | ; travelFeedrate=[travel_speed] 71 | ; printFeedrate=[perimeter_speed] 72 | ; extrusionFeedrate=25 73 | ``` 74 | 75 | 3. Also, add the following line to the very beginning of your "End G-Code", your own custom End G-Code can follow 76 | ``` 77 | ; end of g-code 78 | ``` 79 | 80 | 4. The "After layer change G-code" should consist of exactly that line: 81 | ``` 82 | ; next layer 83 | ``` 84 | 85 | 5. The "Tool change G-code" should consist of exactly that line: 86 | ``` 87 | ; tool change 88 | ``` 89 | 90 | ## Disclaimer / License 91 | I've never written a line of Perl before this project. I'm still learning, but also had to make this work. Any suggestions are heavily welcome. 92 | All scripts in this repository are licensed under the GPLv3 with me, Moritz Walter, as the author. 93 | -------------------------------------------------------------------------------- /wipe_towers_v01.pl: -------------------------------------------------------------------------------- 1 | # WIPE TOWERS v01 2 | # PERL POSTPROCESSOR FOR ADDING WIPE TOWERS TO SLIC3R 3 | # YUNOMAKE.COM 4 | # AUTHOR: Moritz Walter 2015 5 | # LICENSED UNDER GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html 6 | 7 | #!/usr/bin/perl -i 8 | use strict; 9 | use warnings; 10 | use Math::Round; 11 | use POSIX qw[ceil floor]; 12 | use List::Util qw[min max]; 13 | use constant PI => 4 * atan2(1, 1); 14 | 15 | # printer parameters with default values 16 | 17 | my $nozzleDiameter=0.4; 18 | my $filamentDiameter=1.75; 19 | my $extrusionMultiplier=1.0; 20 | my $firstLayerExtrusionMultiplier=4.0; 21 | my $extrusionWidth=$nozzleDiameter; 22 | my $layerHeight=0.2; 23 | my $firstLayerHeight=0.1; 24 | my $retractionLength=5; 25 | my $toolChangeRetractionLength=5; 26 | 27 | my $bedWidth=160; 28 | my $bedDepth=165; 29 | my $extruders=2; 30 | 31 | # other params 32 | my $travelLift=1; 33 | 34 | # wipe tower parameters with default values 35 | 36 | my $wipeTowerX=80; 37 | my $wipeTowerY=155; 38 | my $wipeTowerW=10; 39 | my $wipeTowerH=10; 40 | my $wipeTowerSpacing=20; 41 | my $wipeTowerLoops=5; 42 | my $wipeTowerBrimLoops=7; 43 | 44 | # wipe parameters with default values 45 | 46 | my $wipeOffset=2; 47 | my $purgeOffset=2; 48 | my $wipeLift=5; 49 | my $purgeAmount=3; 50 | 51 | # printing parameters with default parameters, feedrates are multiplied by 60 on import for converting them from mm/s to mm/min 52 | 53 | my $retractionFeedrate=75*60; 54 | my $travelFeedrate=150*60; 55 | my $printFeedrate=30*60; 56 | my $extrusionFeedrate=25*60; 57 | 58 | # state variables, keeping track of whats happening inside the G-code 59 | 60 | my @extruderUsed=(0,0,0,0); # counts how often an extruder is used 61 | 62 | my $gcodeF=4500; 63 | my $gcodeActiveExtruder=0; 64 | my @gcodeX=(); 65 | my @gcodeY=(); 66 | my $gcodeZ=0; 67 | my $lastGcodeZ=0; 68 | my @gcodeE=(); 69 | my @gcodeRetraction=(); 70 | my $gcodeAbsolutePositioning=0; 71 | 72 | # state variables, keeping track of what we're doing 73 | 74 | my $currentF=4500; 75 | my $currentE=0; 76 | my $currentX=0; 77 | my $currentY=0; 78 | 79 | my $absolutePositioning=1; 80 | my $absoluteExtrusion=0; 81 | 82 | my $line=1; 83 | my $towerLayer=0; 84 | my $wipe=[0,0]; 85 | 86 | # processing variables 87 | 88 | my $layer = 0; 89 | my $start = 0; 90 | my $end = 0; 91 | 92 | my @linesByExtruder=(); 93 | my @endOfLayerLines=(); 94 | 95 | my $bypass=0; 96 | 97 | for(my $i=0;$i<$extruders;$i++){ 98 | $linesByExtruder[$i]=(); 99 | $gcodeX[$i]=0; 100 | $gcodeY[$i]=0; 101 | $gcodeE[$i]=0; 102 | $gcodeRetraction[$i]=0; 103 | } 104 | 105 | ########## 106 | # MAIN LOOP 107 | ########## 108 | 109 | while (<>) { 110 | if($start==0){ 111 | readParams($_); 112 | evaluateLine($_); 113 | print; 114 | }elsif($end==1){ 115 | print; # just print out everything after the end code marker 116 | }elsif (/^T(\d)/){ 117 | evaluateLine($_); 118 | }elsif(/^; next layer/){ 119 | # do nothing, strips line by not printing it back 120 | }elsif(/^; tool change/){ 121 | # do nothing, strips line by not printing it back 122 | }elsif(/^M204/){ 123 | push(@{$linesByExtruder[$gcodeActiveExtruder]},$_); # acceleration changes are sorted into extruder arrays 124 | }elsif(/^G[01]( X(-?\d*\.?\d*))?( Y(-?\d*\.?\d*))?( Z(-?\d*\.?\d*))?( E(-?\d*\.?\d*))?/){ # regular move 125 | if($6){ # move contains z-move, interpreted as layer change if not happening before the start code marker 126 | insertSortedLayer(); # inserts all moves of the current layer, this also inserts the wipe towers on tool chage 127 | evaluateLine($_); # keeps the g-code tracker aligned 128 | print; # copying z-move 129 | print("; next layer\n"); # insert next layer marker because having stripped it before 130 | $layer++; # count layer 131 | }else{ 132 | push(@{$linesByExtruder[$gcodeActiveExtruder]},$_); # moves that do not contain z-moves are sorted into the extruder arrays 133 | } 134 | }elsif(/^; end of g-code/){ 135 | $end=1; 136 | insertSortedLayer(); # the last layer is not followed by a layer change, thats why we have to insert it here 137 | print; 138 | }else{ # all the other gcodes, such as temperature changes, fan on/off, the config summary, etc.. are shoved to the end of a layer 139 | push(@endOfLayerLines,$_); 140 | } 141 | } 142 | 143 | ########## 144 | # PRINT TOWER 145 | ########## 146 | 147 | sub squareTowerEL{ # returns the gcode for printing a wipe tower 148 | my $e=$_[0]; 149 | my $l=$_[1]; 150 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 151 | my $y=$wipeTowerY; 152 | my $gcode=""; 153 | 154 | $gcode.=comment("printing square tower with layer height $l"); 155 | my $travelPoints=generatePreTravelPointsEN($e,$layer); 156 | 157 | #$gcode.=lift($travelLift); 158 | $gcode.=travelToXYF($travelPoints->[0]->[0],$travelPoints->[0]->[1],$travelFeedrate); 159 | #$gcode.=lower($travelLift); 160 | $gcode.=travelToXYF($travelPoints->[1]->[0],$travelPoints->[1]->[1],$travelFeedrate); 161 | 162 | if($layer==0){ 163 | $gcode.=comment("printing brim"); 164 | for(my $loop=0;$loop<$wipeTowerBrimLoops;$loop++){ 165 | my $brimPoints=baseCornerBrimPointsELN($e,$loop,$layer); 166 | $gcode.=travelToXYF($brimPoints->[0]->[0],$brimPoints->[0]->[1],$travelFeedrate); 167 | if($loop==0){ 168 | $gcode.=extrudeEF(-$gcodeRetraction[$e], $retractionFeedrate); #$retractionLength 169 | } 170 | for(my $p=1;$p<5;$p++){ 171 | $gcode.=extrudeToXYFL($brimPoints->[$p]->[0],$brimPoints->[$p]->[1],$printFeedrate,$l); 172 | } 173 | if($loop==$wipeTowerBrimLoops-1){ 174 | $gcode.=extrudeEF($gcodeRetraction[$e], $retractionFeedrate); #-$retractionLength 175 | } 176 | } 177 | } 178 | 179 | $gcode.=comment("printing loops"); 180 | for(my $loop=0;$loop<$wipeTowerLoops;$loop++){ 181 | my $printPoints=baseCornerPointsELN($e,$loop,$layer); 182 | $gcode.=travelToXYF($printPoints->[0]->[0],$printPoints->[0]->[1],$travelFeedrate); 183 | if($loop==0){ 184 | $gcode.=extrudeEF(-$gcodeRetraction[$e], $retractionFeedrate); #$retractionLength 185 | } 186 | for(my $p=1;$p<5;$p++){ 187 | $gcode.=extrudeToXYFL($printPoints->[$p]->[0],$printPoints->[$p]->[1],$printFeedrate,$l); 188 | } 189 | if($loop==$wipeTowerLoops-1){ 190 | $gcode.=extrudeEF($gcodeRetraction[$e], $retractionFeedrate); #-$retractionLength 191 | } 192 | } 193 | return $gcode; 194 | } 195 | 196 | ########## 197 | # MATH 198 | ########## 199 | 200 | sub digitize { # cut floats to size 201 | my $num=$_[0]; 202 | my $digits=$_[1]; 203 | my $factor=10**$digits; 204 | return (round($num*$factor))/$factor; 205 | } 206 | 207 | sub dist{ # calculate distances between 2d points 208 | my $x1=$_[0]; 209 | my $y1=$_[1]; 210 | my $x2=$_[2]; 211 | my $y2=$_[3]; 212 | return sqrt(($x2-$x1)**2+($y2-$y1)**2); 213 | } 214 | 215 | sub extrusionXYXY{ # calculate the extrusion length for a move from (x1,y1) to (x2,y2) 216 | my $x1=$_[0]; 217 | my $y1=$_[1]; 218 | my $x2=$_[2]; 219 | my $y2=$_[3]; 220 | my $filamentArea=$filamentDiameter*$filamentDiameter/4*PI; 221 | my $lineLength=dist($x1,$y1,$x2,$y2); 222 | my $eDist=$lineLength*$extrusionWidth/$filamentArea; 223 | if($layer==0){ 224 | $eDist*=$firstLayerHeight; 225 | $eDist*=$firstLayerExtrusionMultiplier; 226 | }else{ 227 | $eDist*=$layerHeight; 228 | $eDist*=$extrusionMultiplier; 229 | } 230 | return digitize($eDist,4); 231 | } 232 | 233 | sub extrusionXYXYL{ # calculate the extrusion length for a move from (x1,y1) to (x2,y2) 234 | my $x1=$_[0]; 235 | my $y1=$_[1]; 236 | my $x2=$_[2]; 237 | my $y2=$_[3]; 238 | my $l=$_[4]; 239 | my $filamentArea=$filamentDiameter*$filamentDiameter/4*PI; 240 | my $lineLength=dist($x1,$y1,$x2,$y2); 241 | my $eDist=$lineLength*$extrusionWidth/$filamentArea; 242 | $eDist*=$l; 243 | if($layer==0){ 244 | $eDist*=$firstLayerExtrusionMultiplier; 245 | }else{ 246 | $eDist*=$extrusionMultiplier; 247 | } 248 | return digitize($eDist,4); 249 | } 250 | 251 | sub extrusionXY { # calculate the extrusion length for a move from the current extruder position to (x,y) 252 | my $x=$_[0]; 253 | my $y=$_[1]; 254 | if($absolutePositioning){ 255 | return extrusionXYXY($currentX, $currentY, $x, $y); 256 | }else{ 257 | return extrusionXYXY(0, 0, $x, $y); 258 | } 259 | } 260 | sub extrusionXYL { # calculate the extrusion length for a move from the current extruder position to (x,y) taking a layer height 261 | my $x=$_[0]; 262 | my $y=$_[1]; 263 | my $l=$_[2]; 264 | if($absolutePositioning){ 265 | return extrusionXYXYL($currentX, $currentY, $x, $y, $l); 266 | }else{ 267 | return extrusionXYXYL(0, 0, $x, $y, $l); 268 | } 269 | } 270 | 271 | sub baseCornerPointsELN{ # calculates the corner points of the wipe tower 272 | my $e=$_[0]; 273 | my $l=$_[1]; 274 | my $n=$_[2]; 275 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 276 | my $y=$wipeTowerY; 277 | my $extrusionWidthOffset=$l*$extrusionWidth; 278 | my $points=[ 279 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset], 280 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 281 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 282 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset] 283 | ]; 284 | my $result=[ 285 | $points->[$n%4], 286 | $points->[($n+1)%4], 287 | $points->[($n+2)%4], 288 | $points->[($n+3)%4], 289 | $points->[$n%4] 290 | ]; 291 | return $result; 292 | } 293 | 294 | 295 | sub baseCornerBrimPointsELN{ # calculates the corner points of the wipe tower 296 | my $e=$_[0]; #extruder 297 | my $l=$_[1]; #loops 298 | my $n=$_[2]; #layer n 299 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 300 | my $y=$wipeTowerY; 301 | my $extrusionWidthOffset=$l*$extrusionWidth-$wipeTowerBrimLoops*$extrusionWidth; 302 | my $points=[ 303 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset], 304 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 305 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 306 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset] 307 | ]; 308 | my $result=[ 309 | $points->[$n%4], 310 | $points->[($n+1)%4], 311 | $points->[($n+2)%4], 312 | $points->[($n+3)%4], 313 | $points->[$n%4] 314 | ]; 315 | return $result; 316 | } 317 | 318 | sub generatePreTravelPointsEN{ # calculates the travel points for approaching a wipe tower 319 | my $e=$_[0]; 320 | my $n=$_[1]; 321 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 322 | my $y=$wipeTowerY; 323 | my $points=[ 324 | [ 325 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 326 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 327 | ], 328 | [ 329 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 330 | [$x-$wipeTowerW/2-$wipeOffset,$y+$wipeTowerH/2+$wipeOffset] 331 | ], 332 | [ 333 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 334 | [$x+$wipeTowerW/2+$wipeOffset,$y+$wipeTowerH/2+$wipeOffset] 335 | ], 336 | [ 337 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 338 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 339 | ] 340 | ]; 341 | return $points->[$n%4]; 342 | } 343 | 344 | sub generatePostTravelPoints{ # calculates the travel points for leaving a wipe tower 345 | my $x=$_[0]; 346 | my $y=$_[1]; 347 | my $n=$_[2]; 348 | my $points=[ 349 | [ 350 | [$x+$wipeTowerW/2+$wipeOffset,$y+$wipeTowerH/2+$wipeOffset], 351 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] 352 | ], 353 | 354 | [ 355 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 356 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 357 | ], 358 | [ 359 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 360 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 361 | ], 362 | [ 363 | [$x-$wipeTowerW/2-$wipeOffset,$y+$wipeTowerH/2+$wipeOffset], 364 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] 365 | ], 366 | ]; 367 | return $points->[$n%4]; 368 | } 369 | 370 | sub generatePurgePosition{ 371 | my $x=$_[0]; 372 | my $y=$_[1]; 373 | my $n=$_[2]; 374 | my $positions=[ 375 | [$x-$purgeOffset,$y-$purgeOffset], 376 | [$x-$purgeOffset,$y+$purgeOffset], 377 | [$x+$purgeOffset,$y+$purgeOffset], 378 | [$x+$purgeOffset,$y-$purgeOffset] 379 | ]; 380 | return $positions->[$n%4]; 381 | } 382 | 383 | 384 | 385 | ########## 386 | # TRAVEL 387 | ########## 388 | 389 | sub travelToZ{ # appends a trave move 390 | my $z=$_[0]; 391 | return "G1 Z".digitize($z,4)."\n"; 392 | } 393 | 394 | sub travelToXYF{ # appends a trave move 395 | my $x=$_[0]; 396 | my $y=$_[1]; 397 | my $f=$_[2]; 398 | 399 | if($absolutePositioning){ 400 | $currentX=$x; 401 | $currentY=$y; 402 | }else{ 403 | $currentX+=$x; 404 | $currentY+=$y; 405 | } 406 | $currentF=$f; 407 | 408 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." F".$f."\n"; 409 | } 410 | 411 | sub travelToXY{ # appends a trave move 412 | my $x=$_[0]; 413 | my $y=$_[1]; 414 | 415 | if($absolutePositioning){ 416 | $currentX=$x; 417 | $currentY=$y; 418 | }else{ 419 | $currentX+=$x; 420 | $currentY+=$y; 421 | } 422 | 423 | return "G1 X".digitize($x,4)." Y".digitize($y,4)."\n"; 424 | } 425 | 426 | sub lift{ 427 | my $gcode=""; 428 | $gcode.=relativePositioning(); 429 | $gcode.=travelToZ($_[0]); 430 | $gcode.=absolutePositioning(); 431 | return $gcode; 432 | } 433 | 434 | sub lower{ 435 | my $gcode=""; 436 | $gcode.=relativePositioning(); 437 | $gcode.=travelToZ(-$_[0]); 438 | $gcode.=absolutePositioning(); 439 | return $gcode; 440 | } 441 | 442 | ########## 443 | # EXTRUDE 444 | ########## 445 | 446 | sub extrudeEF{ # appends an extrusion (=printing) move 447 | my $e=$_[0]; 448 | my $f=$_[1]; 449 | $currentE+=$e; 450 | if($absoluteExtrusion){ 451 | return "G1 E".digitize($currentE,4)." F".digitize($f,4)."\n"; 452 | }else{ 453 | return "G1 E".digitize($e,4)." F".digitize($f,4)."\n"; 454 | } 455 | } 456 | 457 | sub extrudeE{ # appends an extrusion (=printing) move 458 | my $e=$_[0]; 459 | $currentE+=$e; 460 | if($absoluteExtrusion){ 461 | return "G1 E".digitize($currentE,4)."\n"; 462 | }else{ 463 | return "G1 E".digitize($e,4)."\n"; 464 | } 465 | } 466 | 467 | sub extrudeToXYF{ 468 | my $x=$_[0]; 469 | my $y=$_[1]; 470 | my $f=$_[2]; 471 | my $extrusionLength=extrusionXY($x,$y); 472 | $currentE+=$extrusionLength; 473 | 474 | if($absolutePositioning){ 475 | $currentX=$x; 476 | $currentY=$y; 477 | }else{ 478 | $currentX+=$x; 479 | $currentY+=$y; 480 | } 481 | $currentF=$f; 482 | 483 | if($absoluteExtrusion){ 484 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)." F".digitize($f,4)."\n"; 485 | }else{ 486 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)." F".digitize($f,4)."\n"; 487 | } 488 | } 489 | 490 | sub extrudeToXYFL{ 491 | my $x=$_[0]; 492 | my $y=$_[1]; 493 | my $f=$_[2]; 494 | my $l=$_[3]; 495 | my $extrusionLength=extrusionXYL($x,$y,$l); 496 | $currentE+=$extrusionLength; 497 | 498 | if($absolutePositioning){ 499 | $currentX=$x; 500 | $currentY=$y; 501 | }else{ 502 | $currentX+=$x; 503 | $currentY+=$y; 504 | } 505 | $currentF=$f; 506 | 507 | if($absoluteExtrusion){ 508 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)." F".digitize($f,4)."\n"; 509 | }else{ 510 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)." F".digitize($f,4)."\n"; 511 | } 512 | } 513 | 514 | sub extrudeToXY{ # appends an extrusion (=printing) move 515 | my $x=$_[0]; 516 | my $y=$_[1]; 517 | my $extrusionLength=extrusionXY($x,$y); 518 | $currentE+=$extrusionLength; 519 | 520 | if($absolutePositioning){ 521 | $currentX=$x; 522 | $currentY=$y; 523 | }else{ 524 | $currentX+=$x; 525 | $currentY+=$y; 526 | } 527 | 528 | if($absoluteExtrusion){ 529 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)."\n"; 530 | }else{ 531 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)."\n"; 532 | } 533 | } 534 | 535 | sub extrudeToXYL{ # appends an extrusion (=printing) move, respecting the layer height 536 | my $x=$_[0]; 537 | my $y=$_[1]; 538 | my $l=$_[2]; 539 | my $extrusionLength=extrusionXYL($x,$y,$l); 540 | $currentE+=$extrusionLength; 541 | 542 | if($absolutePositioning){ 543 | $currentX=$x; 544 | $currentY=$y; 545 | }else{ 546 | $currentX+=$x; 547 | $currentY+=$y; 548 | } 549 | 550 | if($absoluteExtrusion){ 551 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)."\n"; 552 | }else{ 553 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)."\n"; 554 | } 555 | } 556 | 557 | ########## 558 | # OTHER GCODES 559 | ########## 560 | 561 | 562 | sub absolutePositioning{ # changes coordinate mode and appends the necessary G-code 563 | $absolutePositioning=1; 564 | return "G90 ; set absolute positioning\n"; 565 | } 566 | 567 | sub relativePositioning{ # changes coordinate mode and appends the necessary G-code 568 | $absolutePositioning=0; 569 | return "G91 ; set relative positioning\n"; 570 | } 571 | 572 | sub absoluteExtrusion{ # changes extrusion mode and appends the necessary G-code 573 | $absoluteExtrusion=1; 574 | return "M82 ; set extruder to absolute mode\n"; 575 | } 576 | sub relativeExtrusion{ # changes extrusion mode and appends the necessary G-code 577 | $absoluteExtrusion=0; 578 | return "M83 ; set extruder to relative mode\n"; 579 | } 580 | 581 | sub selectExtruder{ # switches the used extruder and appends the necessary G-code, does NOT change $activeExtruder since we want to switch back to $activeExtruder 582 | return "T".$_[0]."\n"; 583 | } 584 | 585 | sub dwell{ # appends a dwelling G-code with the argument as seconds 586 | return "G4 S".$_[0]."\n"; 587 | } 588 | 589 | sub comment{ # appends the argument to the currently read G-code line and comments it out with a "; " 590 | return "; ".$_[0]."\n"; 591 | } 592 | 593 | 594 | ########## 595 | # PROCESSING 596 | ########## 597 | 598 | 599 | sub evaluateLine{ 600 | my $e=$gcodeActiveExtruder; 601 | if($#_==1){ 602 | $e=$_[1]; 603 | } 604 | if ($_[0]=~/^T(\d)/){ 605 | $gcodeActiveExtruder=$1; 606 | }elsif(/^; next layer/){ 607 | $start=1; 608 | }elsif(/^G90/){ 609 | $gcodeAbsolutePositioning=1; 610 | }elsif(/^G91/){ 611 | $gcodeAbsolutePositioning=0; 612 | }elsif(/^G28/){ 613 | $lastGcodeZ=0; 614 | $gcodeZ=0; 615 | }elsif(/^G29/){ 616 | $lastGcodeZ=0; 617 | $gcodeZ=0; 618 | }elsif($_[0]=~/^G[01]( X(-?\d*\.?\d*))?( Y(-?\d*\.?\d*))?( Z(-?\d*\.?\d*))?( E(-?\d*\.?\d*))?/){ 619 | if($2){ 620 | if($gcodeAbsolutePositioning){ 621 | $gcodeX[$e]=$2; 622 | }else{ 623 | $gcodeX[$e]+=$2; 624 | } 625 | } 626 | if($4){ 627 | if($gcodeAbsolutePositioning){ 628 | $gcodeY[$e]=$4; 629 | }else{ 630 | $gcodeY[$e]+=$4; 631 | } 632 | } 633 | if($6){ 634 | if($start){ 635 | $lastGcodeZ=$gcodeZ; 636 | } 637 | if($gcodeAbsolutePositioning){ 638 | $gcodeZ=$6; 639 | }else{ 640 | $gcodeZ+=$6; 641 | } 642 | } 643 | if($8){ # keeps track of relative extruder moves for each extruder, aiming to support absolute extrusion in the future 644 | if(!$2 && !$4 && !$6){ 645 | if($8<0){ 646 | $gcodeRetraction[$e]+=$8; 647 | }else{ 648 | if($8>0){ 649 | if($8>-$gcodeRetraction[$e]){ 650 | $gcodeRetraction[$e]=0; 651 | }else{ 652 | $gcodeRetraction[$e]+=$8; 653 | } 654 | } 655 | } 656 | } 657 | } 658 | } 659 | } 660 | 661 | sub insertSortedLayer{ 662 | for(my $e=0;$e<$extruders;$e++){ 663 | #if($#{$linesByExtruder[$e]}>-1){ 664 | print("; tool change\n"); 665 | print "T".$e."\n"; 666 | insertWipeTowerE($e); 667 | for(my $i=0; $i<=$#{$linesByExtruder[$e]};$i++){ 668 | evaluateLine($linesByExtruder[$e][$i],$e); 669 | print($linesByExtruder[$e][$i]); 670 | } 671 | @{$linesByExtruder[$e]}=(); 672 | #}else{ 673 | #print("; omitted tool change\n"); 674 | #} 675 | } 676 | if($#endOfLayerLines>-1){ 677 | print("; end of layer lines\n"); 678 | for(my $i=0; $i<=$#endOfLayerLines;$i++){ 679 | print($endOfLayerLines[$i]); 680 | } 681 | @endOfLayerLines=(); 682 | } 683 | } 684 | 685 | sub insertWipeTowerE{ 686 | my $e=$_[0]; 687 | my $l=$gcodeZ-$lastGcodeZ; 688 | if($l>0){ 689 | print squareTowerEL($e,$l); 690 | print lift($travelLift); 691 | print travelToXYF($gcodeX[$e],$gcodeY[$e],$travelFeedrate); 692 | print lower($travelLift); 693 | }else{ 694 | print "; omitting wipe tower\n"; 695 | } 696 | } 697 | 698 | sub readParams{ # collecting params 699 | if($_[0]=~/nozzleDiameter=(\d*\.?\d*)/){ 700 | $nozzleDiameter=$1*1.0; 701 | } 702 | if($_[0]=~/filamentDiameter=(\d*\.?\d*)/){ 703 | $filamentDiameter=$1*1.0; 704 | } 705 | if($_[0]=~/extrusionWidth=(\d*\.?\d*)/){ 706 | $extrusionWidth=$1*1.0; 707 | } 708 | if($_[0]=~/extrusionMultiplier=(\d*\.?\d*)/){ 709 | $extrusionMultiplier=$1*1.0; 710 | } 711 | if($_[0]=~/firstLayerExtrusionMultiplier=(\d*\.?\d*)/){ 712 | $firstLayerExtrusionMultiplier=$1*1.0; 713 | } 714 | if($_[0]=~/layerHeight=(\d*\.?\d*)/){ 715 | $layerHeight=$1*1.0; 716 | } 717 | if($_[0]=~/firstLayerHeight=(\d*\.?\d*)/){ 718 | $firstLayerHeight=$1*1.0; 719 | } 720 | if($_[0]=~/retractionLength=(\d*\.?\d*)/){ 721 | $retractionLength=$1*1.0; 722 | } 723 | if($_[0]=~/toolChangeRetractionLength=(\d*\.?\d*)/){ 724 | $toolChangeRetractionLength=$1*1.0; 725 | } 726 | if($_[0]=~/bedWidth=(\d*\.?\d*)/){ 727 | $bedWidth=$1*1.0; 728 | } 729 | if($_[0]=~/bedDepth=(\d*\.?\d*)/){ 730 | $bedDepth=$1*1.0; 731 | } 732 | if($_[0]=~/extruders=(\d*\.?\d*)/){ 733 | $extruders=$1*1.0; 734 | } 735 | if($_[0]=~/wipeTowerX=(\d*\.?\d*)/){ 736 | $wipeTowerX=$1*1.0; 737 | } 738 | if($_[0]=~/wipeTowerY=(\d*\.?\d*)/){ 739 | $wipeTowerY=$1*1.0; 740 | } 741 | if($_[0]=~/wipeTowerW=(\d*\.?\d*)/){ 742 | $wipeTowerW=$1*1.0; 743 | } 744 | if($_[0]=~/wipeTowerH=(\d*\.?\d*)/){ 745 | $wipeTowerH=$1*1.0; 746 | } 747 | if($_[0]=~/wipeTowerSpacing=(\d*\.?\d*)/){ 748 | $wipeTowerSpacing=$1*1.0; 749 | } 750 | if($_[0]=~/wipeTowerLoops=(\d*\.?\d*)/){ 751 | $wipeTowerLoops=$1*1.0; 752 | } 753 | if($_[0]=~/wipeTowerBrimLoops=(\d*\.?\d*)/){ 754 | $wipeTowerBrimLoops=$1*1.0; 755 | } 756 | if($_[0]=~/wipeOffset=(\d*\.?\d*)/){ 757 | $wipeOffset=$1*1.0; 758 | } 759 | if($_[0]=~/purgeOffset=(\d*\.?\d*)/){ 760 | $purgeOffset=$1*1.0; 761 | } 762 | if($_[0]=~/wipeLift=(\d*\.?\d*)/){ 763 | $wipeLift=$1*1.0; 764 | } 765 | if($_[0]=~/travelLift=(\d*\.?\d*)/){ 766 | $travelLift=$1*1.0; 767 | } 768 | if($_[0]=~/purgeAmount=(\d*\.?\d*)/){ 769 | $purgeAmount=$1*1.0; 770 | } 771 | if($_[0]=~/retractionFeedrate=(\d*\.?\d*)/){ 772 | $retractionFeedrate=$1*60.0; 773 | } 774 | if($_[0]=~/travelFeedrate=(\d*\.?\d*)/){ 775 | $travelFeedrate=$1*60.0; 776 | } 777 | if($_[0]=~/printFeedrate=(\d*\.?\d*)/){ 778 | $printFeedrate=$1*60.0; 779 | } 780 | if($_[0]=~/extrusionFeedrate=(\d*\.?\d*)/){ 781 | $extrusionFeedrate=$1*60.0; 782 | } 783 | } 784 | -------------------------------------------------------------------------------- /wipe_towers_v02.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -i 2 | use strict; 3 | use warnings; 4 | use Math::Round; 5 | use POSIX qw[ceil floor]; 6 | use List::Util qw[min max]; 7 | use constant PI => 4 * atan2(1, 1); 8 | 9 | # printer parameters with default values 10 | 11 | my $nozzleDiameter=0.4; 12 | my $filamentDiameter=1.75; 13 | my $extrusionMultiplier=1.0; 14 | my $firstLayerExtrusionMultiplier=4.0; 15 | my $extrusionWidth=$nozzleDiameter; 16 | my $layerHeight=0.2; 17 | my $firstLayerHeight=0.1; 18 | my $retractionLength=5; 19 | my $toolChangeRetractionLength=5; 20 | 21 | my $bedWidth=160; 22 | my $bedDepth=165; 23 | my $extruders=2; 24 | 25 | # other params 26 | my $travelLift=1; 27 | 28 | # wipe tower parameters with default values 29 | 30 | my $wipeTowerX=80; 31 | my $wipeTowerY=155; 32 | my $wipeTowerW=10; 33 | my $wipeTowerH=10; 34 | my $wipeTowerSpacing=20; 35 | my $wipeTowerLoops=5; 36 | my $wipeTowerBrimLoops=7; 37 | my $forceToolChanges=1; 38 | 39 | # wipe parameters with default values 40 | 41 | my $wipeOffset=2; 42 | my $purgeOffset=2; 43 | my $wipeLift=5; 44 | my $purgeAmount=3; 45 | 46 | # printing parameters with default parameters, feedrates are multiplied by 60 on import for converting them from mm/s to mm/min 47 | 48 | my $retractionFeedrate=75*60; 49 | my $travelFeedrate=150*60; 50 | my $printFeedrate=30*60; 51 | my $extrusionFeedrate=25*60; 52 | 53 | # state variables, keeping track of whats happening inside the G-code 54 | 55 | my @extruderUsed=(0,0,0,0); # counts how often an extruder is used 56 | 57 | my $gcodeF=4500; 58 | my $gcodeActiveExtruder=0; 59 | my @gcodeX=(); 60 | my @gcodeY=(); 61 | my $gcodeZ=0; 62 | my $lastGcodeZ=0; 63 | my @gcodeE=(); 64 | my @gcodeRetraction=(); 65 | my $gcodeAbsolutePositioning=0; 66 | 67 | # state variables, keeping track of what we're doing 68 | 69 | my $scriptActiveExtruder=0; 70 | 71 | my $currentF=4500; 72 | my $currentE=0; 73 | my $currentX=0; 74 | my $currentY=0; 75 | 76 | my $absolutePositioning=1; 77 | my $absoluteExtrusion=0; 78 | 79 | my $line=1; 80 | my $towerLayer=0; 81 | my @scriptRetraction=(); 82 | 83 | # processing variables 84 | 85 | my $layer = 0; 86 | my $start = 0; 87 | my $end = 0; 88 | 89 | my @linesByExtruder=(); 90 | my @endOfLayerLines=(); 91 | 92 | initializeBuffer(); 93 | 94 | ########## 95 | # MAIN LOOP 96 | ########## 97 | 98 | while (<>) { 99 | if($start==0){ 100 | readParams($_); 101 | evaluateLine($_); 102 | print; 103 | }elsif($end==1){ 104 | print; # just print out everything after the end code marker 105 | }elsif (/^T(\d)/){ 106 | evaluateLine($_); 107 | }elsif(/^; next layer/){ 108 | # do nothing, strips line by not printing it back 109 | }elsif(/^; tool change/){ 110 | # do nothing, strips line by not printing it back 111 | }elsif(/^M204/){ 112 | push(@{$linesByExtruder[$gcodeActiveExtruder]},$_); # acceleration changes are sorted into extruder arrays 113 | }elsif(/^G[01]( X(-?\d*\.?\d*))?( Y(-?\d*\.?\d*))?( Z(-?\d*\.?\d*))?( E(-?\d*\.?\d*))?/){ # regular move 114 | if($6){ # move contains z-move, interpreted as layer change if not happening before the start code marker 115 | insertSortedLayer(); # inserts all moves of the current layer, this also inserts the wipe towers on tool chage 116 | evaluateLine($_); # keeps the g-code tracker aligned 117 | print; # copying z-move 118 | print("; next layer\n"); # insert next layer marker because having stripped it before 119 | $layer++; # count layer 120 | }else{ 121 | push(@{$linesByExtruder[$gcodeActiveExtruder]},$_); # moves that do not contain z-moves are sorted into the extruder arrays 122 | } 123 | }elsif(/^; end of g-code/){ 124 | $end=1; 125 | insertSortedLayer(); # the last layer is not followed by a layer change, thats why we have to insert it here 126 | print; 127 | }else{ # all the other gcodes, such as temperature changes, fan on/off, the config summary, etc.. are shoved to the end of a layer 128 | push(@endOfLayerLines,$_); 129 | } 130 | } 131 | 132 | ########## 133 | # PRINT TOWER 134 | ########## 135 | 136 | sub squareTowerEPL{ # returns the gcode for printing a wipe tower 137 | my $e=$_[0]; 138 | my $p=$_[1]; 139 | my $l=$_[2]; 140 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 141 | my $y=$wipeTowerY; 142 | my $gcode=""; 143 | 144 | $gcode.=comment("printing square tower with layer height $l"); 145 | my $travelPoints=generatePreTravelPointsEN($p,$layer); 146 | 147 | $gcode.=lift($travelLift); 148 | $gcode.=travelToXYF($travelPoints->[0]->[0],$travelPoints->[0]->[1],$travelFeedrate); 149 | $gcode.=lower($travelLift); 150 | $gcode.=travelToXYF($travelPoints->[1]->[0],$travelPoints->[1]->[1],$travelFeedrate); 151 | 152 | if($layer==0){ 153 | $gcode.=comment("printing brim"); 154 | for(my $loop=0;$loop<$wipeTowerBrimLoops;$loop++){ 155 | my $brimPoints=baseCornerBrimPointsELN($p,$loop,$layer); 156 | $gcode.=travelToXYF($brimPoints->[0]->[0],$brimPoints->[0]->[1],$travelFeedrate); 157 | if($loop==0){ 158 | $gcode.=extrudeEF(-$gcodeRetraction[$e], $retractionFeedrate); #$retractionLength 159 | } 160 | for(my $b=1;$b<5;$b++){ 161 | $gcode.=extrudeToXYFL($brimPoints->[$b]->[0],$brimPoints->[$b]->[1],$printFeedrate,$l); 162 | } 163 | if($loop==$wipeTowerBrimLoops-1){ 164 | $gcode.=extrudeEF($gcodeRetraction[$e], $retractionFeedrate); #-$retractionLength 165 | } 166 | } 167 | } 168 | 169 | $gcode.=comment("printing loops"); 170 | for(my $loop=0;$loop<$wipeTowerLoops;$loop++){ 171 | my $printPoints=baseCornerPointsELN($p,$loop,$layer); 172 | $gcode.=travelToXYF($printPoints->[0]->[0],$printPoints->[0]->[1],$travelFeedrate); 173 | if($loop==0){ 174 | $gcode.=extrudeEF(-$gcodeRetraction[$e], $retractionFeedrate); #$retractionLength 175 | } 176 | for(my $p=1;$p<5;$p++){ 177 | $gcode.=extrudeToXYFL($printPoints->[$p]->[0],$printPoints->[$p]->[1],$printFeedrate,$l); 178 | } 179 | if($loop==$wipeTowerLoops-1){ 180 | $gcode.=extrudeEF($gcodeRetraction[$e], $retractionFeedrate); #-$retractionLength 181 | } 182 | } 183 | return $gcode; 184 | } 185 | 186 | ########## 187 | # MATH 188 | ########## 189 | 190 | sub digitize { # cut floats to size 191 | my $num=$_[0]; 192 | my $digits=$_[1]; 193 | my $factor=10**$digits; 194 | return (round($num*$factor))/$factor; 195 | } 196 | 197 | sub dist{ # calculate distances between 2d points 198 | my $x1=$_[0]; 199 | my $y1=$_[1]; 200 | my $x2=$_[2]; 201 | my $y2=$_[3]; 202 | return sqrt(($x2-$x1)**2+($y2-$y1)**2); 203 | } 204 | 205 | sub extrusionXYXY{ # calculate the extrusion length for a move from (x1,y1) to (x2,y2) 206 | my $x1=$_[0]; 207 | my $y1=$_[1]; 208 | my $x2=$_[2]; 209 | my $y2=$_[3]; 210 | my $filamentArea=$filamentDiameter*$filamentDiameter/4*PI; 211 | my $lineLength=dist($x1,$y1,$x2,$y2); 212 | my $eDist=$lineLength*$extrusionWidth/$filamentArea; 213 | if($layer==0){ 214 | $eDist*=$firstLayerHeight; 215 | $eDist*=$firstLayerExtrusionMultiplier; 216 | }else{ 217 | $eDist*=$layerHeight; 218 | $eDist*=$extrusionMultiplier; 219 | } 220 | return digitize($eDist,4); 221 | } 222 | 223 | sub extrusionXYXYL{ # calculate the extrusion length for a move from (x1,y1) to (x2,y2) 224 | my $x1=$_[0]; 225 | my $y1=$_[1]; 226 | my $x2=$_[2]; 227 | my $y2=$_[3]; 228 | my $l=$_[4]; 229 | my $filamentArea=$filamentDiameter*$filamentDiameter/4*PI; 230 | my $lineLength=dist($x1,$y1,$x2,$y2); 231 | my $eDist=$lineLength*$extrusionWidth/$filamentArea; 232 | $eDist*=$l; 233 | if($layer==0){ 234 | $eDist*=$firstLayerExtrusionMultiplier; 235 | }else{ 236 | $eDist*=$extrusionMultiplier; 237 | } 238 | return digitize($eDist,4); 239 | } 240 | 241 | sub extrusionXY { # calculate the extrusion length for a move from the current extruder position to (x,y) 242 | my $x=$_[0]; 243 | my $y=$_[1]; 244 | if($absolutePositioning){ 245 | return extrusionXYXY($currentX, $currentY, $x, $y); 246 | }else{ 247 | return extrusionXYXY(0, 0, $x, $y); 248 | } 249 | } 250 | sub extrusionXYL { # calculate the extrusion length for a move from the current extruder position to (x,y) taking a layer height 251 | my $x=$_[0]; 252 | my $y=$_[1]; 253 | my $l=$_[2]; 254 | if($absolutePositioning){ 255 | return extrusionXYXYL($currentX, $currentY, $x, $y, $l); 256 | }else{ 257 | return extrusionXYXYL(0, 0, $x, $y, $l); 258 | } 259 | } 260 | 261 | sub baseCornerPointsELN{ # calculates the corner points of the wipe tower 262 | my $e=$_[0]; 263 | my $l=$_[1]; 264 | my $n=$_[2]; 265 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 266 | my $y=$wipeTowerY; 267 | my $extrusionWidthOffset=$l*$extrusionWidth; 268 | my $points=[ 269 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset], 270 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 271 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 272 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset] 273 | ]; 274 | my $result=[ 275 | $points->[$n%4], 276 | $points->[($n+1)%4], 277 | $points->[($n+2)%4], 278 | $points->[($n+3)%4], 279 | $points->[$n%4] 280 | ]; 281 | return $result; 282 | } 283 | 284 | 285 | sub baseCornerBrimPointsELN{ # calculates the corner points of the wipe tower 286 | my $e=$_[0]; #extruder 287 | my $l=$_[1]; #loops 288 | my $n=$_[2]; #layer n 289 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 290 | my $y=$wipeTowerY; 291 | my $extrusionWidthOffset=$l*$extrusionWidth-$wipeTowerBrimLoops*$extrusionWidth; 292 | my $points=[ 293 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset], 294 | [$x-$wipeTowerW/2+$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 295 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y+$wipeTowerH/2-$extrusionWidthOffset], 296 | [$x+$wipeTowerW/2-$extrusionWidthOffset,$y-$wipeTowerH/2+$extrusionWidthOffset] 297 | ]; 298 | my $result=[ 299 | $points->[$n%4], 300 | $points->[($n+1)%4], 301 | $points->[($n+2)%4], 302 | $points->[($n+3)%4], 303 | $points->[$n%4] 304 | ]; 305 | return $result; 306 | } 307 | 308 | sub generatePreTravelPointsEN{ # calculates the travel points for approaching a wipe tower 309 | my $e=$_[0]; 310 | my $n=$_[1]; 311 | my $x=$wipeTowerX+$e*$wipeTowerSpacing; 312 | my $y=$wipeTowerY; 313 | my $points=[ 314 | [ 315 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 316 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 317 | ], 318 | [ 319 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 320 | [$x-$wipeTowerW/2-$wipeOffset,$y+$wipeTowerH/2+$wipeOffset] 321 | ], 322 | [ 323 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 324 | [$x+$wipeTowerW/2+$wipeOffset,$y+$wipeTowerH/2+$wipeOffset] 325 | ], 326 | [ 327 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 328 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 329 | ] 330 | ]; 331 | return $points->[$n%4]; 332 | } 333 | 334 | sub generatePostTravelPoints{ # calculates the travel points for leaving a wipe tower 335 | my $x=$_[0]; 336 | my $y=$_[1]; 337 | my $n=$_[2]; 338 | my $points=[ 339 | [ 340 | [$x+$wipeTowerW/2+$wipeOffset,$y+$wipeTowerH/2+$wipeOffset], 341 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] 342 | ], 343 | 344 | [ 345 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 346 | [$x+$wipeTowerW/2+$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 347 | ], 348 | [ 349 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset], 350 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] # duplicate 351 | ], 352 | [ 353 | [$x-$wipeTowerW/2-$wipeOffset,$y+$wipeTowerH/2+$wipeOffset], 354 | [$x-$wipeTowerW/2-$wipeOffset,$y-$wipeTowerH/2-$wipeOffset] 355 | ], 356 | ]; 357 | return $points->[$n%4]; 358 | } 359 | 360 | sub generatePurgePosition{ 361 | my $x=$_[0]; 362 | my $y=$_[1]; 363 | my $n=$_[2]; 364 | my $positions=[ 365 | [$x-$purgeOffset,$y-$purgeOffset], 366 | [$x-$purgeOffset,$y+$purgeOffset], 367 | [$x+$purgeOffset,$y+$purgeOffset], 368 | [$x+$purgeOffset,$y-$purgeOffset] 369 | ]; 370 | return $positions->[$n%4]; 371 | } 372 | 373 | 374 | 375 | ########## 376 | # TRAVEL 377 | ########## 378 | 379 | sub travelToZ{ # appends a trave move 380 | my $z=$_[0]; 381 | return "G1 Z".digitize($z,4)."\n"; 382 | } 383 | 384 | sub travelToXYF{ # appends a trave move 385 | my $x=$_[0]; 386 | my $y=$_[1]; 387 | my $f=$_[2]; 388 | 389 | if($absolutePositioning){ 390 | $currentX=$x; 391 | $currentY=$y; 392 | }else{ 393 | $currentX+=$x; 394 | $currentY+=$y; 395 | } 396 | $currentF=$f; 397 | 398 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." F".$f."\n"; 399 | } 400 | 401 | sub travelToXY{ # appends a trave move 402 | my $x=$_[0]; 403 | my $y=$_[1]; 404 | 405 | if($absolutePositioning){ 406 | $currentX=$x; 407 | $currentY=$y; 408 | }else{ 409 | $currentX+=$x; 410 | $currentY+=$y; 411 | } 412 | 413 | return "G1 X".digitize($x,4)." Y".digitize($y,4)."\n"; 414 | } 415 | 416 | sub lift{ 417 | my $gcode=""; 418 | $gcode.=relativePositioning(); 419 | $gcode.=travelToZ($_[0]); 420 | $gcode.=absolutePositioning(); 421 | return $gcode; 422 | } 423 | 424 | sub lower{ 425 | my $gcode=""; 426 | $gcode.=relativePositioning(); 427 | $gcode.=travelToZ(-$_[0]); 428 | $gcode.=absolutePositioning(); 429 | return $gcode; 430 | } 431 | 432 | ########## 433 | # EXTRUDE 434 | ########## 435 | 436 | sub logScriptExtrusion{ # ($tool,$extrusion) or ($extrusion) for $gcodeActiveExtruder 437 | my $t=$gcodeActiveExtruder; 438 | my $e=0; 439 | if($#_==1){ 440 | $e=$_[0]; 441 | }elsif($#_==2){ 442 | $t=$_[0]; 443 | $e=$_[1]; 444 | } 445 | $currentE+=$e; 446 | $scriptRetraction[$t]+=$e; 447 | if($scriptRetraction[$t]>0){ 448 | $scriptRetraction[$t]=0; 449 | } 450 | } 451 | 452 | sub extrudeEF{ # appends an extrusion (=printing) move 453 | my $e=$_[0]; 454 | my $f=$_[1]; 455 | $currentE+=$e; 456 | if($absoluteExtrusion){ 457 | return "G1 E".digitize($currentE,4)." F".digitize($f,4)."\n"; 458 | }else{ 459 | return "G1 E".digitize($e,4)." F".digitize($f,4)."\n"; 460 | } 461 | } 462 | 463 | sub extrudeE{ # appends an extrusion (=printing) move 464 | my $e=$_[0]; 465 | $currentE+=$e; 466 | if($absoluteExtrusion){ 467 | return "G1 E".digitize($currentE,4)."\n"; 468 | }else{ 469 | return "G1 E".digitize($e,4)."\n"; 470 | } 471 | } 472 | 473 | sub extrudeToXYF{ 474 | my $x=$_[0]; 475 | my $y=$_[1]; 476 | my $f=$_[2]; 477 | my $extrusionLength=extrusionXY($x,$y); 478 | $currentE+=$extrusionLength; 479 | 480 | if($absolutePositioning){ 481 | $currentX=$x; 482 | $currentY=$y; 483 | }else{ 484 | $currentX+=$x; 485 | $currentY+=$y; 486 | } 487 | $currentF=$f; 488 | 489 | if($absoluteExtrusion){ 490 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)." F".digitize($f,4)."\n"; 491 | }else{ 492 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)." F".digitize($f,4)."\n"; 493 | } 494 | } 495 | 496 | sub extrudeToXYFL{ 497 | my $x=$_[0]; 498 | my $y=$_[1]; 499 | my $f=$_[2]; 500 | my $l=$_[3]; 501 | my $extrusionLength=extrusionXYL($x,$y,$l); 502 | $currentE+=$extrusionLength; 503 | 504 | if($absolutePositioning){ 505 | $currentX=$x; 506 | $currentY=$y; 507 | }else{ 508 | $currentX+=$x; 509 | $currentY+=$y; 510 | } 511 | $currentF=$f; 512 | 513 | if($absoluteExtrusion){ 514 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)." F".digitize($f,4)."\n"; 515 | }else{ 516 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)." F".digitize($f,4)."\n"; 517 | } 518 | } 519 | 520 | sub extrudeToXY{ # appends an extrusion (=printing) move 521 | my $x=$_[0]; 522 | my $y=$_[1]; 523 | my $extrusionLength=extrusionXY($x,$y); 524 | $currentE+=$extrusionLength; 525 | 526 | if($absolutePositioning){ 527 | $currentX=$x; 528 | $currentY=$y; 529 | }else{ 530 | $currentX+=$x; 531 | $currentY+=$y; 532 | } 533 | 534 | if($absoluteExtrusion){ 535 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)."\n"; 536 | }else{ 537 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)."\n"; 538 | } 539 | } 540 | 541 | sub extrudeToXYL{ # appends an extrusion (=printing) move, respecting the layer height 542 | my $x=$_[0]; 543 | my $y=$_[1]; 544 | my $l=$_[2]; 545 | my $extrusionLength=extrusionXYL($x,$y,$l); 546 | $currentE+=$extrusionLength; 547 | 548 | if($absolutePositioning){ 549 | $currentX=$x; 550 | $currentY=$y; 551 | }else{ 552 | $currentX+=$x; 553 | $currentY+=$y; 554 | } 555 | 556 | if($absoluteExtrusion){ 557 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($currentE,4)."\n"; 558 | }else{ 559 | return "G1 X".digitize($x,4)." Y".digitize($y,4)." E".digitize($extrusionLength,4)."\n"; 560 | } 561 | } 562 | 563 | ########## 564 | # OTHER GCODES 565 | ########## 566 | 567 | 568 | sub absolutePositioning{ # changes coordinate mode and appends the necessary G-code 569 | $absolutePositioning=1; 570 | return "G90 ; set absolute positioning\n"; 571 | } 572 | 573 | sub relativePositioning{ # changes coordinate mode and appends the necessary G-code 574 | $absolutePositioning=0; 575 | return "G91 ; set relative positioning\n"; 576 | } 577 | 578 | sub absoluteExtrusion{ # changes extrusion mode and appends the necessary G-code 579 | $absoluteExtrusion=1; 580 | return "M82 ; set extruder to absolute mode\n"; 581 | } 582 | sub relativeExtrusion{ # changes extrusion mode and appends the necessary G-code 583 | $absoluteExtrusion=0; 584 | return "M83 ; set extruder to relative mode\n"; 585 | } 586 | 587 | sub selectExtruder{ # switches the used extruder and appends the necessary G-code, does NOT change $activeExtruder since we want to switch back to $activeExtruder 588 | return "T".$_[0]."\n"; 589 | } 590 | 591 | sub dwell{ # appends a dwelling G-code with the argument as seconds 592 | return "G4 S".$_[0]."\n"; 593 | } 594 | 595 | sub comment{ # appends the argument to the currently read G-code line and comments it out with a "; " 596 | return "; ".$_[0]."\n"; 597 | } 598 | 599 | 600 | ########## 601 | # PROCESSING 602 | ########## 603 | 604 | 605 | sub evaluateLine{ 606 | my $e=$gcodeActiveExtruder; 607 | if($#_==1){ 608 | $e=$_[1]; 609 | } 610 | if ($_[0]=~/^T(\d)/){ 611 | $gcodeActiveExtruder=$1; 612 | }elsif(/^; next layer/){ 613 | $start=1; 614 | }elsif(/^G90/){ 615 | $gcodeAbsolutePositioning=1; 616 | }elsif(/^G91/){ 617 | $gcodeAbsolutePositioning=0; 618 | }elsif(/^G28/){ 619 | $lastGcodeZ=0; 620 | $gcodeZ=0; 621 | }elsif(/^G29/){ 622 | $lastGcodeZ=0; 623 | $gcodeZ=0; 624 | }elsif($_[0]=~/^G[01]( X(-?\d*\.?\d*))?( Y(-?\d*\.?\d*))?( Z(-?\d*\.?\d*))?( E(-?\d*\.?\d*))?/){ 625 | if($2){ 626 | if($gcodeAbsolutePositioning){ 627 | $gcodeX[$e]=$2; 628 | }else{ 629 | $gcodeX[$e]+=$2; 630 | } 631 | } 632 | if($4){ 633 | if($gcodeAbsolutePositioning){ 634 | $gcodeY[$e]=$4; 635 | }else{ 636 | $gcodeY[$e]+=$4; 637 | } 638 | } 639 | if($6){ 640 | if($start){ 641 | $lastGcodeZ=$gcodeZ; 642 | } 643 | if($gcodeAbsolutePositioning){ 644 | $gcodeZ=$6; 645 | }else{ 646 | $gcodeZ+=$6; 647 | } 648 | } 649 | if($8){ # keeps track of printing (and retraction) moves for each extruder (only relative extrusion mode) 650 | #$gcodeRetraction[$e]+=$8; 651 | #if($gcodeRetraction[$e]>0){ 652 | # $gcodeRetraction[$e]=0; 653 | #} 654 | 655 | #VERY BAD CODE MUST BE IGNORED BY READER THANK YOU 656 | if(!$2 && !$4 && !$6){ # must be a retraction move 657 | if($8<0){ 658 | $gcodeRetraction[$e]+=$8; 659 | }elsif($8>0){ 660 | if($8>-$gcodeRetraction[$e]){ 661 | $gcodeRetraction[$e]=0; 662 | }else{ 663 | $gcodeRetraction[$e]+=$8; 664 | } 665 | } 666 | } 667 | } 668 | } 669 | } 670 | 671 | sub insertSortedLayer{ 672 | for(my $e=0;$e<$extruders;$e++){ 673 | if($forceToolChanges || $#{$linesByExtruder[$e]}>-1){ # only change the tool to print the tower if the tool is used, otherwise continue with current extruder 674 | 675 | # this should be a tool change sub 676 | print("; tool change\n"); 677 | print "T".$e."\n"; 678 | $scriptActiveExtruder=$e; 679 | 680 | }else{ 681 | print("; omitted tool change\n"); # printing wipe tower with current extruder, this makes sense if one of the extruders is not used for a while 682 | } 683 | insertWipeTowerEP($scriptActiveExtruder,$e); 684 | for(my $i=0; $i<=$#{$linesByExtruder[$e]};$i++){ 685 | evaluateLine($linesByExtruder[$e][$i],$e); 686 | print($linesByExtruder[$e][$i]); 687 | } 688 | @{$linesByExtruder[$e]}=(); 689 | } 690 | if($#endOfLayerLines>-1){ 691 | print("; end of layer lines\n"); 692 | for(my $i=0; $i<=$#endOfLayerLines;$i++){ 693 | print($endOfLayerLines[$i]); 694 | } 695 | @endOfLayerLines=(); 696 | } 697 | } 698 | 699 | sub insertWipeTowerEP{ 700 | my $e=$_[0]; 701 | my $p=$_[1]; 702 | my $l=$gcodeZ-$lastGcodeZ; 703 | if($l>0){ 704 | print squareTowerEPL($e,$p,$l); 705 | print lift($travelLift); 706 | print travelToXYF($gcodeX[$e],$gcodeY[$e],$travelFeedrate); 707 | print lower($travelLift); 708 | }else{ 709 | print "; omitting wipe tower due to zero layer height\n"; 710 | } 711 | } 712 | 713 | sub initializeBuffer{ 714 | for(my $i=0;$i<$extruders;$i++){ 715 | my $travelPoints=generatePreTravelPointsEN($i,$layer); 716 | $linesByExtruder[$i]=(); 717 | $gcodeX[$i]=$travelPoints->[0]->[0]; 718 | $gcodeY[$i]=$travelPoints->[0]->[1]; 719 | $gcodeE[$i]=0; 720 | $gcodeRetraction[$i]=0; 721 | $scriptRetraction[$i]=0; 722 | } 723 | } 724 | 725 | sub readParams{ # collecting params 726 | if($_[0]=~/nozzleDiameter=(\d*\.?\d*)/){ 727 | $nozzleDiameter=$1*1.0; 728 | } 729 | if($_[0]=~/filamentDiameter=(\d*\.?\d*)/){ 730 | $filamentDiameter=$1*1.0; 731 | } 732 | if($_[0]=~/extrusionWidth=(\d*\.?\d*)/){ 733 | # Use the specified extrusion width, unless it is set to the "automatic" value of 0 (Slic3r default), 734 | # in which case the pre-initialized value of $nozzleDiameter will be used 735 | unless ( $1 eq "0" ) { 736 | $extrusionWidth=$1*1.0; 737 | } 738 | } 739 | if($_[0]=~/extrusionMultiplier=(\d*\.?\d*)/){ 740 | $extrusionMultiplier=$1*1.0; 741 | } 742 | if($_[0]=~/firstLayerExtrusionMultiplier=(\d*\.?\d*)/){ 743 | $firstLayerExtrusionMultiplier=$1*1.0; 744 | } 745 | if($_[0]=~/layerHeight=(\d*\.?\d*)/){ 746 | $layerHeight=$1*1.0; 747 | } 748 | if($_[0]=~/firstLayerHeight=(\d*\.?\d*)/){ 749 | $firstLayerHeight=$1*1.0; 750 | } 751 | if($_[0]=~/retractionLength=(\d*\.?\d*)/){ 752 | $retractionLength=$1*1.0; 753 | } 754 | if($_[0]=~/toolChangeRetractionLength=(\d*\.?\d*)/){ 755 | $toolChangeRetractionLength=$1*1.0; 756 | } 757 | if($_[0]=~/bedWidth=(\d*\.?\d*)/){ 758 | $bedWidth=$1*1.0; 759 | } 760 | if($_[0]=~/bedDepth=(\d*\.?\d*)/){ 761 | $bedDepth=$1*1.0; 762 | } 763 | if($_[0]=~/extruders=(\d*\.?\d*)/){ 764 | $extruders=$1*1.0; 765 | initializeBuffer(); 766 | } 767 | if($_[0]=~/wipeTowerX=(\d*\.?\d*)/){ 768 | $wipeTowerX=$1*1.0; 769 | } 770 | if($_[0]=~/wipeTowerY=(\d*\.?\d*)/){ 771 | $wipeTowerY=$1*1.0; 772 | } 773 | if($_[0]=~/wipeTowerW=(\d*\.?\d*)/){ 774 | $wipeTowerW=$1*1.0; 775 | } 776 | if($_[0]=~/wipeTowerH=(\d*\.?\d*)/){ 777 | $wipeTowerH=$1*1.0; 778 | } 779 | if($_[0]=~/wipeTowerSpacing=(\d*\.?\d*)/){ 780 | $wipeTowerSpacing=$1*1.0; 781 | } 782 | if($_[0]=~/wipeTowerLoops=(\d*\.?\d*)/){ 783 | $wipeTowerLoops=$1*1.0; 784 | } 785 | if($_[0]=~/wipeTowerBrimLoops=(\d*\.?\d*)/){ 786 | $wipeTowerBrimLoops=$1*1.0; 787 | } 788 | if($_[0]=~/wipeOffset=(\d*\.?\d*)/){ 789 | $wipeOffset=$1*1.0; 790 | } 791 | if($_[0]=~/purgeOffset=(\d*\.?\d*)/){ 792 | $purgeOffset=$1*1.0; 793 | } 794 | if($_[0]=~/wipeLift=(\d*\.?\d*)/){ 795 | $wipeLift=$1*1.0; 796 | } 797 | if($_[0]=~/travelLift=(\d*\.?\d*)/){ 798 | $travelLift=$1*1.0; 799 | } 800 | if($_[0]=~/purgeAmount=(\d*\.?\d*)/){ 801 | $purgeAmount=$1*1.0; 802 | } 803 | if($_[0]=~/retractionFeedrate=(\d*\.?\d*)/){ 804 | $retractionFeedrate=$1*60.0; 805 | } 806 | if($_[0]=~/travelFeedrate=(\d*\.?\d*)/){ 807 | $travelFeedrate=$1*60.0; 808 | } 809 | if($_[0]=~/printFeedrate=(\d*\.?\d*)/){ 810 | $printFeedrate=$1*60.0; 811 | } 812 | if($_[0]=~/extrusionFeedrate=(\d*\.?\d*)/){ 813 | $extrusionFeedrate=$1*60.0; 814 | } 815 | if($_[0]=~/forceToolChanges=(true|false)/){ 816 | if($1 eq "true"){ 817 | $forceToolChanges=1; 818 | }elsif($1 eq "false"){ 819 | $forceToolChanges=0; 820 | } 821 | } 822 | } --------------------------------------------------------------------------------