-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
1147 lines (1041 loc) · 53.3 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<html>
<head>
<title>G-Code Test Pattern Generator</title>
<style>
.output {
font-family: monospace;
font-size: 8pt;
}
</style>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="char_render.js"></script>
<meta charset="utf-8"/>
</head>
<body>
<table>
<tr><td>
<table style="width: 360px;">
<tr><td>Mode:</td><td>
<input type="radio" name="mode" value="x" checked> X ruler<br>
<input type="radio" name="mode" value="y"> Y ruler<br>
<input type="radio" name="mode" value="xy"> X and Y ruler<br>
<input type="radio" name="mode" value="perim"> Perimeter ruler<br>
<input type="radio" name="mode" value="squareness"> Squareness marks<br>
<input type="radio" name="mode" value="ztest_corners"> Z test four corners<br>
<input type="radio" name="mode" value="ztest_grid"> Z test grid<br>
<input type="radio" name="mode" value="dense_segments"> Dense segments<br>
<input type="radio" name="mode" value="accel_x"> X Acceleration<br>
<input type="radio" name="mode" value="accel_y"> Y Acceleration<br>
<input type="radio" name="mode" value="text"> Text<br>
<input type="radio" name="mode" value="surfacing"> Surfacing<br>
<input type="radio" name="mode" value="hog"> Hog-Out
</td></tr>
<tr><td>Start with G92 X0 Y0 Z0:</td><td><input type="checkbox" name="zero" checked></td></tr>
<tr><td name="z_pen_down_txt">Z level for pen-down:</td><td><input type="text" name="pen_d" value="-0.5", size="6"> mm</td></tr>
<tr><td name="z_clearance_txt">Z level for pen-up:</td><td><input type="text" name="pen_u" value="0.5", size="6"> mm</td></tr>
<tr><td>Rapid Feedrate:</td><td><input type="text" name="rapid" value="2000", size="6"> mm/min</td></tr>
<tr><td>Raise/lower Feedrate:</td><td><input type="text" name="vertical" value="800", size="6"> mm/min</td></tr>
<tr><td name="drawspeed_txt">Draw Feedrate:</td><td><input type="text" name="drawspeed" value="1000", size="6"> mm/min</td></tr>
<tr><td name="drawspeed_slow_txt">Draw Feedrate (slow):</td><td><input type="text" name="drawspeed_slow" value="200", size="6"> mm/min</td></tr>
<tr><td>Low Acceleration:</td><td><input type="text" name="accel_low" value="100", size="6"> mm/sec^2</td></tr>
<tr><td>High Acceleration:</td><td><input type="text" name="accel_high" value="1000", size="6"> mm/sec^2</td></tr>
<tr><td>Number of Accel Tests:</td><td><input type="text" name="accel_tests" value="10", size="6"> (count)</td></tr>
<tr><td>X Extent:</td><td><input type="text" name="xsize" value="100", size="6"> mm</td></tr>
<tr><td>Y Extent:</td><td><input type="text" name="ysize" value="100", size="6"> mm</td></tr>
<!-- <tr><td>Segment Length:</td><td><input type="text" name="accel_segment" value="20", size="6"> mm</td></tr>
<tr><td>Corner Radius:</td><td><input type="text" name="accel_corner" value="0", size="6"> mm</td></tr> -->
<tr><td>Z Test Cross Size:</td><td><input type="text" name="zxsize" value="14", size="6"> mm</td></tr>
<tr><td>Dense segments min:</td><td><input type="text" name="dense_minseg" value="0.02", size="6"> mm</td></tr>
<tr><td>Dense segments max:</td><td><input type="text" name="dense_maxseg" value="0.5", size="6"> mm</td></tr>
<tr><td>Save bandwidth:</td><td><input type="checkbox" name="dense_efficient"></td></tr>
<tr><td>Draw diagonally:</td><td><input type="checkbox" name="dense_diagonal"></td></tr>
<tr><td name="stepover_txt">Surfacing stepover:</td><td><input type="text" name="stepover" value="12", size="6"> mm</td></tr>
<tr><td>Cutting direction:</td><td><input type="text" name="direction" value="E", size="6"> (N, S, E, or W)</td></tr>
<tr><td>Perimeter:</td><td><input type="checkbox" name="perimeter"></td></tr>
<tr><td>Orientation:</td><td><input type="text" name="orientation" value="X", size="6"> (X or Y)</td></tr>
<tr><td>Pattern count:</td><td><input type="text" name="hog_count" value="1", size="6"></td></tr>
<tr><td>Pattern offset:</td><td><input type="text" name="hog_offset" value="10", size="6"> mm</td></tr>
<tr><td>Final feedrate:</td><td><input type="text" name="final_feedrate" value="1000", size="6"> mm</td></tr>
<tr><td>Final stepover:</td><td><input type="text" name="final_stepover" value="3", size="6"> mm</td></tr>
<tr><td></td><td><input type="button" value="Generate" onclick="generate()"></td></tr>
</table>
</td><td style="vertical-align:top">
<div id="ruler_desc">
<h1>Ruler Mode</h1>
<p>This mode draws many short (~10 mm) segments 1mm apart, with the end result resembling a ruler. These rulers are drawn in pairs, with two rulers drawn next to each other, but drawn in opposite directions.
</p><p>
These are potentially useful for identifying errors in steps per mm, not only for uniform spacing error, but also for nonuniform errors in spacing. These should be uncommon but the test pattern can help identify it or rule it out. By drawing two rulers in opposite directions, backlash (or other similar effects) can be detected. The "perimeter ruler" can also be used to determine whether the machine is square by comparing the diagonals (although it is not a particularly efficient test pattern for just determining squareness).</p>
<ul><li>
X ruler draws a pair of adjacent rulers along the positive X axis. The "X Extent" setting determines the length to be created.
</li><li>
Y ruler draws a pair of adjacent rulers along the positive Y axis. The "Y Extent" setting determines the length to be created.
</li><li>
X and Y ruler draws both of the above.
</li><li>
Perimeter ruler draws four (pairs of) rulers around the perimeter within a rectangle whose dimensions are specified by X extent and Y extent.
</li></ul>
</div>
<div id="squareness_desc">
<h1>Squareness Marks</h1>
<p>Draws four "L" shape marks in the corners of the specified X and Y extent. These marks are possibly useful for indicating the extent of the workspace and can also be useful for testing squarenes.
</p>
</div>
<div id="ztest_desc">
<h1>Z-Test Mode</h1>
<p>These modes, Z-Test four corners and Z-Test grid, are for checking whether the work surface is level and flat.
</p><p>
These work by drawing "X" shaped marks, where the pen descends (lowers Z) at a shallow angle as it approaches the center of the X, and then slowly rises (increasing Z) as it moves away from the center of the X. Small differences in the height of the surface create longer or shorter pen marks, effectively amplifying surface height errors to be more easily visible.
</p><p>
The Z Test Cross Size setting specifies the horizontal and vertical dimension of the "X" shape. The default value of 14 mm has a corner-to-corner diagonal length of about 20 mm, or corner-to-center length of about 10 mm. With a Z travel of 1 mm (from +0.5 to -0.5) the shallow descent and ascent is at a slope of about 1:10. This means a Z displacement is reflected in segment length amplified by a factor of 20.
</p><p>
For Z-Test grid mode, as many as possible "X" shapes are fit, evenly spaced, within the specified X and Y extent.
</p>
</div>
<div id="dense_seg_desc">
<h1>Dense Segments</h1>
<p>To test parsing and communication speed, this generates line segments that are drawn using a large number of very short G1 segments end-to-end in the same direction.
</p><p>
The lines are drawn in the positive X direction, and the total length is determined by X Extent.
Multiple subdivision lengths are tested, each offset by 1 mm in the Y direction. The Y Extent determines the Y size of the final drawn pattern, which is also the number of subdivision lengths to be tested. The "Dense segments min" is the smallest subdivision length that is tested, and the "Dense segments max" is the largest subdivision length. For each 1 mm increment in the Y direction, the subdivision length is increased linearly between the min and max.
</p><p>
The g-code also contains comments showing the subdivision lengths before each line is drawn.
</p><p>
As an additional option to see the possible influence on processing speed, a more efficient mode can be enabled by checking "Save bandwidth". This generates the same toolpath but does not repeat the Z, Y, or F values any more than necessary.
</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/FYIWPjkD5JY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div id="accel_desc">
<h1>Acceleration Test</h1>
<p>Generates gcode to test acceleration. Assumes junction deviation is being used, in
which case "jerk" (instantaneous speed change for low enough speed difference) should not be in effect.
</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/RIHGWwliisE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>
The generated g-code will issue M501 at the beginning and at the end, to minimize the chance that modified
acceleration settings will leave the machine in a bad state.
</p><p>
The basic idea for (let's say x acceleration) is:
<ol>
<li>Draw a short segment in the positive y direction
<li>Set high acceleration using M204 to set the default travel acceleration and M201 to set the max acceleration for the axis being tested (X or Y)
<li>Move slowly ramping up speed in +x direction and change direction suddenly to the -x
direction and gradually slow to a stop
<li>Set default and max acceleration back to default value of 180
<li>Draw a short segment directly above the line from step 1
<li>Move over in +x direction a bit and repeat from step 1
</ol>
<ul>
<li>The low acceleration is the minimum acceleration of the test.
<li>The high acceleration is the maximum acceleration of the test
<li>The number of accel tests is the number of segment, high acceleration sequence, segment iterations
<li>The segments are always 10mm long with a 1mm gap between them
<li>The drawing feedrate is used to draw the two segments
<li>The sideways step between acceleration tests is always 2mm
<li>The rapid feedrate is the speed at which the acceleration is tested
<li>The X (or Y) extent is the length of the speed ramp
<li>A short diagonal move is performed before each segment is drawn, so the line position would be consistent even if there were backlash.
</ul>
</p>
</div>
<div id="text_desc">
<h1>Text Generation</h1>
<p>Generates toolpaths from text, using a fixed-width font, which is potentially useful for ASCII art.
</p><p>
<span style="color:red">This requires firmware support for G5 (Bezier splines)!</span> This is not enabled by default in MPCNC firmware, but it is fairly straightforward to enable.
</p><p>
The text is stretched to fill a rectangle defined by X Extent and Y Extent. This can create extremely stretched or squished characters which might not be desirable. Strongly recommended: look in the g-code comments at the x pitch and y pitch and y/x aspect ratio, which can indicate if the generated text is likely to be of the size and proportion you want.
</p>
</div>
<div id="surfacing_desc">
<h1>Spoil Board (or Workpiece) Surfacing</h1>
<p>Generates strokes at a given depth traveling in only one direction.
</p><p>
If the router is not quite perpendicular, then one side will cut slightly deeper than the other. This is not normally an issue for small-diameter bits but the amplitude is larger for large-diameter bits.
</p><p>It is assumed that the bits are intended for cutting radially and not on axially, so the deeper-cutting side should be the leading side. A standard pocketing tool path from Estlcam for example will traverse back and forth in opposite directions. With the deeper side leading, it will cut radially, but in the opposite direction the leading edge will cut slightly shallow and the trailing edge will cut axially or possibly rub and generate heat.
</p><p>Therefore the cutting strategy for this cut is to cut only one direction, lifting the bit between cuts. The cutting direction is specified and must be "N", "S", "E", or "W", which cut in the +Y, -Y, +X, and -X directions, respectively.
</p><p>As an added feature, the cutting toolpath is split into relatively small segments instead of being single, very long G1 movements, which allows dynamic adjustment of the feedrate via the Octoprint interface or the LCD.
</p><p><span style="color:red">Note the recommended feedrates for surfacing are quite a bit slower than for drawing and must be chosen manually. The defaults are unlikely to work well!</span>
</p><p>Note that the X extent and Y extent are the extent of travel of the center of the cutting bit. The pocket size will be larger by the diameter of the cutting bit.
</p>
<p>The 'perimeter' option is for endgrain cutting boards or other situations where some cuts can cause tearout. Before surfacing, the tool traces the perimeter clockwise (twice) to ensure the perimeter is cut with the tool cutting into the workpiece instead of cutting out ofthe workpiece. Here is an example of using the perimeter option to surface some endgrain veneer, which is prone to chip-out if the perimeter option is not used:
</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/WHqcnfb_Bm0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div id="hog_desc">
<h1>Hog-Out Optimization</h1>
<p>This test is intended to answer the following question: for a given material and a given combination of tool, stepover,
and depth, how does feedrate affect accuracy (deflection), and how high a feedrate is possible? Using arbitrary conservative
numbers without experimenting might leave a lot of performance on the table. Knowing the behavior at high speeds allows a
more informed choice of speeds.
</p><p>
The test works by first preparing a "top-hat" shape at low speed, first with a slotting cut and then a finishing pass so the
top-hat profile is fairly precise. Then at high speed, the tool cuts through (see video). If the tool deflects due to the
cutting load, it can be measured. Higher speeds are expected to produce higher loads and higher deflection. The sound and
overall quality can also be evaluated subjectively.
</p><p>
This allows determining the highest viable speed by pushing higher and higher feedrates experimentally without
generating a lot of wasted parts.
</p><p>
Recommended starting values for wood with 1/8" bit: <br>
Z height of cut: -6 mm<br>
Z clearance level: 0.5 mm<br>
Rapid Feedrate: 2000 mm/min<br>
Raise/lower Feedrate: 180 mm/min<br>
Cutting Feedrate: 600 mm/min<br>
Slotting Feedrate (slow): 300 mm/min<br>
Stepover: 1.5 mm
</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/_ABgu-GGs90" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</td></tr></table>
<div>
Text:<br>
<textarea rows="5" cols="40" name="text_input"></textarea>
</div>
Github repository here: <a href="https://github.com/vector76/gcode_tpgen">https://github.com/vector76/gcode_tpgen</a><br>
Questions, suggestions, feature requests are welcome.
<pre id="output" class="output">
</pre>
<script>
$("input[name=mode]").click(function () { show_options($(this).val()); });
show_options($("input[name=mode]:checked").val());
function show_options (mode) {
// var mode = $(this).val();
// zero, pen_d, pen_u, rapid, vertical, drawspeed are always shown
// xsize, ysize are hidden for 'hog' mode
if (mode == "x" || mode == "y" || mode == "xy" || mode == "perim") {
$("div[id=ruler_desc]").show();
}
else {
$("div[id=ruler_desc]").hide();
}
if (mode == "squareness") {
$("div[id=squareness_desc]").show();
}
else {
$("div[id=squareness_desc]").hide();
}
// zxsize is only used for ztest_corners and ztest_grid
if (mode == "ztest_corners" || mode == "ztest_grid") {
$("input[name=zxsize]").parent().parent().show();
$("div[id=ztest_desc]").show();
}
else {
$("input[name=zxsize]").parent().parent().hide();
$("div[id=ztest_desc]").hide();
}
// dense_minseg and dense_maxseg and dense_efficient are only shown for dense_segments test
if (mode == "dense_segments") {
$("input[name=dense_minseg]").parent().parent().show();
$("input[name=dense_maxseg]").parent().parent().show();
$("input[name=dense_efficient]").parent().parent().show();
$("input[name=dense_diagonal]").parent().parent().show();
$("div[id=dense_seg_desc]").show();
}
else {
$("input[name=dense_minseg]").parent().parent().hide();
$("input[name=dense_maxseg]").parent().parent().hide();
$("input[name=dense_efficient]").parent().parent().hide();
$("input[name=dense_diagonal]").parent().parent().hide();
$("div[id=dense_seg_desc]").hide();
}
if (mode == "hog") {
$("input[name=drawspeed_slow]").parent().parent().show();
}
else {
$("input[name=drawspeed_slow]").parent().parent().hide();
}
if (mode == "accel_x" || mode == "accel_y") {
//$("input[name=accel_segment]").parent().parent().show();
//$("input[name=accel_corner]").parent().parent().show();
$("input[name=accel_tests]").parent().parent().show();
$("input[name=accel_high]").parent().parent().show();
$("input[name=accel_low]").parent().parent().show();
$("div[id=accel_desc]").show();
}
else {
//$("input[name=accel_segment]").parent().parent().hide();
//$("input[name=accel_corner]").parent().parent().hide();
$("input[name=accel_tests]").parent().parent().hide();
$("input[name=accel_high]").parent().parent().hide();
$("input[name=accel_low]").parent().parent().hide();
$("div[id=accel_desc]").hide();
}
if (mode == "text") {
$("textarea[name=text_input]").parent().show();
$("div[id=text_desc]").show();
}
else {
$("textarea[name=text_input]").parent().hide();
$("div[id=text_desc]").hide();
}
if (mode == "surfacing") {
$("input[name=direction]").parent().parent().show();
$("input[name=perimeter]").parent().parent().show();
$("div[id=surfacing_desc]").show();
}
else {
$("input[name=direction]").parent().parent().hide();
$("input[name=perimeter]").parent().parent().hide();
$("div[id=surfacing_desc]").hide();
}
if (mode == "surfacing" || mode == "hog") {
$("input[name=stepover]").parent().parent().show();
$("td[name=z_pen_down_txt]")[0].innerHTML = "Z height of cut (usually negative):";
$("td[name=z_clearance_txt]")[0].innerHTML = "Z clearance level:";
$("td[name=drawspeed_txt]")[0].innerHTML = "Cutting Feedrate:";
}
else {
$("td[name=z_pen_down_txt]")[0].innerHTML = "Z level for pen-down:";
$("td[name=z_clearance_txt]")[0].innerHTML = "Z level for pen-up:";
$("td[name=drawspeed_txt]")[0].innerHTML = "Draw Feedrate:";
$("input[name=stepover]").parent().parent().hide();
}
if (mode == "hog") {
$("td[name=stepover_txt]")[0].innerHTML = "Stepover:";
$("td[name=drawspeed_slow_txt]")[0].innerHTML = "Slotting Feedrate (slow):";
$("input[name=orientation]").parent().parent().show();
$("input[name=xsize]").parent().parent().hide();
$("input[name=ysize]").parent().parent().hide();
$("input[name=hog_count]").parent().parent().show();
$("input[name=hog_offset]").parent().parent().show();
$("input[name=final_feedrate]").parent().parent().show();
$("input[name=final_stepover]").parent().parent().show();
$("div[id=hog_desc]").show();
}
else {
$("td[name=stepover_txt]")[0].innerHTML = "Surfacing stepover:";
$("td[name=drawspeed_slow_txt]")[0].innerHTML = "Draw Feedrate (slow):";
$("input[name=orientation]").parent().parent().hide();
$("input[name=xsize]").parent().parent().show();
$("input[name=ysize]").parent().parent().show();
$("input[name=hog_count]").parent().parent().hide();
$("input[name=hog_offset]").parent().parent().hide();
$("input[name=final_feedrate]").parent().parent().hide();
$("input[name=final_stepover]").parent().parent().hide();
$("div[id=hog_desc]").hide();
}
};
function output_append(x) {
var output = document.getElementById('output');
var newcontent = document.createElement('div');
newcontent.innerHTML = x + "\n";
while (newcontent.firstChild) {
output.appendChild(newcontent.firstChild);
}
}
function dense_x_segments(seglength, xstart, xend, y, zup, zdn, rapid, vertical, drawspeed, efficient, diag) {
var nsegs = Math.floor(Math.abs(xend-xstart)/seglength);
var xdir = (xend > xstart) ? 1 : -1;
output_append("G0 X" + xstart + " Y" + y + zup + " F" + rapid);
output_append("G1" + zdn + " F" + vertical);
if (diag) {
var dlength = seglength * Math.sqrt(0.5);
for (var i=1; i < nsegs; i++) {
var x = xstart + i * dlength * xdir;
var yy = y + i * dlength;
x = x.toFixed(4);
yy = yy.toFixed(4);
if (efficient) {
if (i == 1) {
output_append("G1 X" + x + " Y" + yy + " F" + drawspeed); // no need to repeat Z position
}
else {
output_append("G1 X" + x + " Y" + yy); // no need to repeat Z position or feedrate
}
}
else {
output_append("G1 X" + x + " Y" + yy + zdn + " F" + drawspeed);
}
}
}
else {
for (var i=1; i < nsegs; i++) {
var x = xstart + i * seglength * xdir;
if (efficient) {
// bare minimum to save bandwidth
if (i == 1) {
// convert to string, keeping four decimals
x = x.toFixed(4);
output_append("G1 X" + x + " F" + drawspeed);
}
else {
x = x.toFixed(4);
output_append("G1 X" + x);
}
}
else {
x = x.toFixed(4);
output_append("G1 X" + x + " Y" + y + zdn + " F" + drawspeed);
}
}
}
output_append("G0" + zup + " F" + vertical);
}
function x_zig(xstart, xend, ymid, zup, zdn, rapid, vertical, drawspeed) {
for (var x=xstart; x <= xend; x++) {
var yend = ymid-0.5;
var ystart = yend-5.5; // most draw from ymid-5.5 to ymid-0.5
if (x % 5 == 0) {
ystart = ystart - 2;
}
if (x % 10 == 0) {
ystart = ystart - 2;
}
output_append("G0 X" + x + " Y" + ystart + zup + " F" + rapid);
output_append("G1 X" + x + " Y" + ystart + zdn + " F" + vertical);
output_append("G1 X" + x + " Y" + yend + zdn + " F" + drawspeed);
output_append("G0 X" + x + " Y" + yend + zup + " F" + vertical);
}
}
function x_zag(xstart, xend, ymid, zup, zdn, rapid, vertical, drawspeed) {
for (var x=xend; x >= xstart; x--) {
var ystart = ymid + 0.5;
var yend = ystart + 5.5; // most go from ymid+0.5 to ymid+6
if (x % 5 == 0) {
yend = yend + 2; // 5 and 10 mm go from ymid+0.5 to ymid+8
}
if (x % 10 == 0) {
yend = yend + 2; // 10 mm goes from ymid+0.5 to ymid+10
}
output_append("G0 X" + x + " Y" + ystart + zup + " F" + rapid);
output_append("G1 X" + x + " Y" + ystart + zdn + " F" + vertical);
output_append("G1 X" + x + " Y" + yend + zdn + " F" + drawspeed);
output_append("G0 X" + x + " Y" + yend + zup + " F" + vertical);
}
}
function y_zig(xmid, ystart, yend, zup, zdn, rapid, vertical, drawspeed) {
for (var y=ystart; y <= yend; y++) {
var xend = xmid-0.5;
var xstart = xend-5.5;
if (y % 5 == 0) {
xstart = xstart - 2;
}
if (y % 10 == 0) {
xstart = xstart - 2;
}
output_append("G0 X" + xstart + " Y" + y + zup + " F" + rapid);
output_append("G1 X" + xstart + " Y" + y + zdn + " F" + vertical);
output_append("G1 X" + xend + " Y" + y + zdn + " F" + drawspeed);
output_append("G0 X" + xend + " Y" + y + zup + " F" + vertical);
}
}
function y_zag(xmid, ystart, yend, zup, zdn, rapid, vertical, drawspeed) {
for (var y=yend; y >= ystart; y--) {
var xstart = xmid + 0.5;
var xend = xstart + 5.5;
if (y % 5 == 0) {
xend = xend + 2;
}
if (y % 10 == 0) {
xend = xend + 2;
}
output_append("G0 X" + xstart + " Y" + y + zup + " F" + rapid);
output_append("G1 X" + xstart + " Y" + y + zdn + " F" + vertical);
output_append("G1 X" + xend + " Y" + y + zdn + " F" + drawspeed);
output_append("G0 X" + xend + " Y" + y + zup + " F" + vertical);
}
}
function z_test(x, y, size, zup, zdn, rapid, drawspeed) {
var x0 = x - size/2;
var x1 = x + size/2;
var y0 = y - size/2;
var y1 = y + size/2;
// convert to strings, keeping three decimals
x0 = x0.toFixed(3);
x1 = x1.toFixed(3);
y0 = y0.toFixed(3);
y1 = y1.toFixed(3);
x = x.toFixed(3);
y = y.toFixed(3);
output_append("G0 X" + x0 + " Y" + y0 + zup + " F" + rapid);
output_append("G1 X" + x + " Y" + y + zdn + " F" + drawspeed);
output_append("G1 X" + x1 + " Y" + y1 + zup + " F" + drawspeed);
output_append("G0 X" + x0 + " Y" + y1 + zup + " F" + rapid);
output_append("G1 X" + x + " Y" + y + zdn + " F" + drawspeed);
output_append("G1 X" + x1 + " Y" + y0 + zup + " F" + drawspeed);
}
function xy_stairs(xstart, ystart, seg_length, numsegs, zup, zdn, rapid, vertical, radius, speedfast, speedslow) {
// raise pen
output_append("G0" + zup + " F" + vertical);
// move to xstart, ystart
output_append("G0 X" + xstart.toFixed(3) + " Y" + ystart.toFixed(3) + " F" + rapid);
// lower pen
output_append("G1" + zdn + " F" + vertical);
var x = xstart;
var y = ystart;
var speed_incr = (speedfast-speedslow)/(numsegs-1);
for (i=0; i < numsegs; i++) {
var speed = speedfast - speed_incr * i;
if (i % 2 == 0) {
// draw from x, y to x+seg_length-radius, y
output_append("G1 X" + (x+seg_length-radius).toFixed(3) + " Y" + y.toFixed(3) + " F" + Math.round(speed));
if (radius > 0) {
// arc from x+seg_length-radius, y to x+seg_length, y+radius
}
else {
x = x + seg_length;
}
}
else {
// draw from x, y to x, y+seg_length-radius
output_append("G1 X" + x.toFixed(3) + " Y" + (y+seg_length-radius).toFixed(3) + " F" + Math.round(speed));
if (radius > 0) {
// arc from x, y+seg_length-radius to x+radius, y+seg_length
}
else {
y = y + seg_length;
}
}
}
}
function gen_subdivided_line(x0, y0, x1, y1, drawspeed) {
var fifth_second = drawspeed/300; // how far do we go in 1/5th of a second
var dist = Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
var stroke_segs = Math.ceil(dist/fifth_second)+1; // how many segments to subdivide a stroke to get about 1/5 second per segment
var dx = (x1-x0)/stroke_segs;
var dy = (y1-y0)/stroke_segs;
for (var i=1; i <= stroke_segs; i++) {
var x = x0 + i*dx;
var y = y0 + i*dy;
output_append("G1 X" + x.toFixed(3) + " Y" + y.toFixed(3) + " F" + drawspeed);
}
}
function surfacing_perim(xsize, ysize, stepover, direction, zup, zdn, rapid, vertical, drawspeed) {
var xstart = -2*stepover;
var ystart = 4*stepover;
var slow = Math.ceil(drawspeed/4);
output_append("G0 " + zup + " F" + vertical); // raise to zup
output_append("G0 X" + xstart + " Y" + ystart + " F" + rapid); // move to starting location where plunge occurs
output_append("G1 " + zdn + " F" + vertical); // plunge to zdn
output_append("G1 X0 Y" + ystart + " F" + slow); // engage laterally but slowly (slow movement exclusive to this move)
gen_subdivided_line(0, ystart, 0, ysize, drawspeed); // run north along west edge
gen_subdivided_line(0, ysize, xsize, ysize, drawspeed); // run east along north edge
gen_subdivided_line(xsize, ysize, xsize, 0, drawspeed); // run south along east edge
gen_subdivided_line(xsize, 0, 0, 0, drawspeed); // run west along south edge (back to origin)
gen_subdivided_line(0, 0, 0, ystart, drawspeed); // north back to ystart
// a dovetail bit that traces the perimeter still won't be able to plunge at those same locations that were just cut, but one stepover should do.
// cut entire perimeter again, one stepover inward
gen_subdivided_line(0, ystart, 0, 0, drawspeed);
gen_subdivided_line(0, 0, stepover, 0, drawspeed);
gen_subdivided_line(stepover, 0, stepover, ysize-stepover, drawspeed);
gen_subdivided_line(stepover, ysize-stepover, xsize-stepover, ysize-stepover, drawspeed);
gen_subdivided_line(xsize-stepover, ysize-stepover, xsize-stepover, stepover, drawspeed);
gen_subdivided_line(xsize-stepover, stepover, stepover, stepover, drawspeed);
gen_subdivided_line(stepover, stepover, 0, 0, drawspeed);
output_append("G0 " + zup + " F" + vertical); // raise to zup
}
function surfacing(xmin, ymin, xmax, ymax, stepover, direction, zup, zdn, rapid, vertical, drawspeed) {
var strokestart, strokeend, rowstart, rowend, horiz;
// orient so that we're always climb milling. This should reduce the tilting of the router instead of increasing it.
if (direction == "E"){
// left to right, top to bottom
strokestart = xmin;
strokeend = xmax;
rowstart = ymax;
rowend = ymin;
horiz = true;
}
else if (direction == "W") {
// right to left, bottom to top
strokestart = xmax;
strokeend = xmin;
rowstart = ymin;
rowend = ymax;
horiz = true;
}
else if (direction == "N") {
// bottom to top, left to right
strokestart = ymin;
strokeend = ymax;
rowstart = xmin;
rowend = xmax;
horiz = false;
}
else if (direction == "S") {
// top to bottom, right to left
strokestart = ymax;
strokeend = ymin;
rowstart = xmax;
rowend = xmin;
horiz = false;
}
else {
alert("internal error, surfacing direction must be 'N', 'S', 'E', or 'W'");
}
// some of this code and logic can be universal for all four directions
var nrows = Math.ceil(Math.abs(rowend-rowstart)/stepover)+1;
// stepover is a guideline but we adjust possibly smaller so each row is equally spaced
var rowstep = (rowend-rowstart)/(nrows-1);
for (var row=0; row < nrows; row++) {
var rowcoord = rowstart + row*rowstep;
if (horiz) {
output_append("G0 " + zup + " F" + vertical); // raise to zup
output_append("G0 X" + strokestart + " Y" + rowcoord.toFixed(3) + " F" + rapid); // rapid move to rowcoord, strokestart
output_append("G1 " + zdn + " F" + vertical); // plunge to zdn
gen_subdivided_line(strokestart, rowcoord, strokeend, rowcoord, drawspeed);
}
else {
output_append("G0 " + zup + " F" + vertical); // raise to zup
output_append("G0 X" + rowcoord.toFixed(3) + " Y" + strokestart + " F" + rapid); // rapid move to rowcoord, strokestart
output_append("G1 " + zdn + " F" + vertical); // plunge to zdn
gen_subdivided_line(rowcoord, strokestart, rowcoord, strokeend, drawspeed);
}
}
output_append("G0 " + zup + " F" + vertical); // raise to zup
output_append("G0 X0 Y0 F" + rapid); // rapid move to 0,0
}
function top_hat_dbl(xstart, ystart, xend, yend, height, brim, zup, zdn, rapid, vertical, drawspeed, offs) {
// perform double top-hat with a bit of stock to leave on first pass
// first calculate left direction
var dx = xend-xstart;
var dy = yend-ystart;
var oal = Math.sqrt(dx*dx + dy*dy);
var xu = dx/oal;
var yu = dy/oal;
var xl = -yu;
var yl = xu;
// add offs * xl to first pass
var xstart_off = xstart + xl*offs;
var ystart_off = ystart + yl*offs;
var xend_off = xend + xl*offs;
var yend_off = yend + yl*offs;
output_append("; top hat with stock to leave");
top_hat(xstart_off, ystart_off, xend_off, yend_off, height, brim, zup, zdn, rapid, vertical, drawspeed);
// second pass follows regular geometry
output_append("; top hat finishing");
top_hat(xstart, ystart, xend, yend, height, brim, zup, zdn, rapid, vertical, drawspeed);
}
function top_hat(xstart, ystart, xend, yend, height, brim, zup, zdn, rapid, vertical, drawspeed) {
// top-hat function from xstart, ystart to xend, yend, height of h and brim size of b
var dx = xend-xstart;
var dy = yend-ystart;
// overal length = sqrt((x1-x2)^2 + (y1-y2)^2)
var oal = Math.sqrt(dx*dx + dy*dy);
// overall length should be nonzero, do we have to check for it?
// actually it must be at least 2*brim, let's check for that
if (oal <= 2*brim) {
alert("internal error, invalid top hat parameters"); // should be impossible unless there are bugs somewhere
return;
}
// unit vector in direction from x1,y1 to x2,y2 is (xu, yu) = (x2-x1, y2-y1)/length;
var xu = dx/oal;
var yu = dy/oal;
// unit vector to left (90 degrees counter-clockwise) is (xl, yl) = (-yu, xu)
var xl = -yu;
var yl = xu;
// intermediate points xA, yA, xB, yB, xC, yC, xD, yD
// A = start + u*brim
var xA = xstart + xu*brim;
var yA = ystart + yu*brim;
// B = A + l*height
var xB = xA + xl*height;
var yB = yA + yl*height;
// D = end - u*brim
var xD = xend - xu*brim;
var yD = yend - yu*brim;
// C = D + l*height
var xC = xD + xl*height;
var yC = yD + yl*height;
var fspeed = " F" + Math.round(drawspeed);
// move to x1, y1, and plunge to z depth
output_append("G0" + zup + " F" + vertical);
output_append("G0 X" + xstart.toFixed(3) + " Y" + ystart.toFixed(3) + " F" + rapid);
output_append("G1" + zdn + " F" + vertical);
// Travel to A, B, C, D, and end
output_append("G1 X" + xA.toFixed(3) + " Y" + yA.toFixed(3) + fspeed);
output_append("G1 X" + xB.toFixed(3) + " Y" + yB.toFixed(3) + fspeed);
output_append("G1 X" + xC.toFixed(3) + " Y" + yC.toFixed(3) + fspeed);
output_append("G1 X" + xD.toFixed(3) + " Y" + yD.toFixed(3) + fspeed);
output_append("G1 X" + xend.toFixed(3) + " Y" + yend.toFixed(3) + fspeed);
// Then raise up
output_append("G0" + zup + " F" + vertical);
}
function full_speed_swipe(xstart, ystart, xend, yend, zup, zdn, rapid, vertical, drawspeed) {
output_append("; full speed cut");
// move to x1, y1, and plunge to z depth
output_append("G0" + zup + " F" + vertical);
output_append("G0 X" + xstart.toFixed(3) + " Y" + ystart.toFixed(3) + " F" + rapid);
output_append("G1" + zdn + " F" + vertical);
// plow through from start to end
output_append("G1 X" + xend.toFixed(3) + " Y" + yend.toFixed(3) + " F" + Math.round(drawspeed));
// Then raise up
output_append("G0" + zup + " F" + vertical);
}
function hog_test_x(cornerx, cornery, stepover, zup, zdn, rapid, vertical, drawspeed_slow, drawspeed) {
var xstart = cornerx;
var ystart = cornery;
var xend = cornerx + 60;
var yend = cornery;
var height = stepover;
var brim = 15;
var stock_to_leave = 0.2; // first pass leaves this much
top_hat_dbl(xstart, ystart, xend, yend, height, brim, zup, zdn, rapid, vertical, drawspeed_slow, stock_to_leave);
full_speed_swipe(xstart, ystart, xend, yend, zup, zdn, rapid, vertical, drawspeed)
}
function hog_test_y(cornerx, cornery, stepover, zup, zdn, rapid, vertical, drawspeed_slow, drawspeed) {
var xstart = cornerx;
var ystart = cornery + 60;
var xend = cornerx;
var yend = cornery;
var height = stepover;
var brim = 15;
var stock_to_leave = 0.2; // first pass leaves this much
top_hat_dbl(xstart, ystart, xend, yend, height, brim, zup, zdn, rapid, vertical, drawspeed_slow, stock_to_leave);
full_speed_swipe(xstart, ystart, xend, yend, zup, zdn, rapid, vertical, drawspeed)
}
function accel_pre(mode, i, zup, zdn, rapid, vertical, drawspeed) {
// draw 10 mm segment prior to running acceleration test
var xstart = (mode == 'accel_x') ? 2*i : 0;
var xend = (mode == 'accel_x') ? 2*i : 10;
var ystart = (mode == 'accel_x') ? 0 : 2*i;
var yend = (mode == 'accel_x') ? 10 : 2*i;
var xpre = xstart+5;
var ypre = ystart+5;
// raise pen
output_append("G0" + zup + " F" + vertical);
// move to xpre, ypre
output_append("G0 X" + xpre.toFixed(3) + " Y" + ypre.toFixed(3) + " F" + rapid);
// move to xstart, ystart
output_append("G0 X" + xstart.toFixed(3) + " Y" + ystart.toFixed(3) + " F" + rapid);
// lower pen
output_append("G1" + zdn + " F" + vertical);
// move to xend, yend
output_append("G1 X" + xend.toFixed(3) + " Y" + yend.toFixed(3) + " F" + drawspeed);
// raise pen
output_append("G0" + zup + " F" + vertical);
}
function accel_execute(mode, i, extent, rapid) {
var subdiv = 40; // subdivide extent into this many smaller segments
var xstart = (mode == 'accel_x') ? 2*i : 10;
var ystart = (mode == 'accel_x') ? 10 : 2*i;
var xstep = (mode == 'accel_x') ? extent/subdiv : 0;
var ystep = (mode == 'accel_x') ? 0 : extent/subdiv;
for (var i=1; i <= subdiv; i++) {
var x = xstart + i*xstep;
var y = ystart + i*ystep;
var vel = rapid * Math.sqrt(i / subdiv);
output_append("G1 X" + x.toFixed(3) + " Y" + y.toFixed(3) + " F" + Math.round(vel));
}
for (var i=subdiv-1; i >= 0; i--) {
var x = xstart + i*xstep;
var y = ystart + i*ystep;
var vel = rapid * Math.sqrt((i+1) / subdiv);
output_append("G1 X" + x.toFixed(3) + " Y" + y.toFixed(3) + " F" + Math.round(vel));
}
}
function accel_post(mode, i, zup, zdn, rapid, vertical, drawspeed) {
// draw second 10mm segment after running acceleration test
var xstart = (mode == 'accel_x') ? 2*i : 11;
var xend = (mode == 'accel_x') ? 2*i : 21;
var ystart = (mode == 'accel_x') ? 11 : 2*i;
var yend = (mode == 'accel_x') ? 21 : 2*i;
var xpre = xstart+5;
var ypre = ystart+5;
// raise pen
output_append("G0" + zup + " F" + vertical);
// move to xpre, ypre
output_append("G0 X" + xpre.toFixed(3) + " Y" + ypre.toFixed(3) + " F" + rapid);
// move to xstart, ystart
output_append("G0 X" + xstart.toFixed(3) + " Y" + ystart.toFixed(3) + " F" + rapid);
// lower pen
output_append("G1" + zdn + " F" + vertical);
// move to xend, yend
output_append("G1 X" + xend.toFixed(3) + " Y" + yend.toFixed(3) + " F" + drawspeed);
// raise pen
output_append("G0" + zup + " F" + vertical);
}
function double_line(sixcoords, zup, zdn, vertical, rapid, drawspeed) {
// expects to already be raised. move to first point
output_append("G0 X" + sixcoords[0] + " Y" + sixcoords[1] + " F" + rapid);
output_append("G1" + zdn + " F" + vertical);
output_append("G1 X" + sixcoords[2] + " Y" + sixcoords[3] + " F" + drawspeed);
output_append("G1 X" + sixcoords[4] + " Y" + sixcoords[5] + " F" + drawspeed);
output_append("G0" + zup + " F" + vertical);
}
function generate() {
// clear output in case it's been run already:
document.getElementById('output').innerHTML = "";
var zero = $("input[name=zero]").prop('checked');
var pen_d = parseFloat($("input[name=pen_d]").val());
var pen_u = parseFloat($("input[name=pen_u]").val());
var rapid = parseFloat($("input[name=rapid]").val());
var vertical = parseFloat($("input[name=vertical]").val());
var drawspeed = parseFloat($("input[name=drawspeed]").val());
var ysize = parseFloat($("input[name=ysize]").val());
var xsize = parseFloat($("input[name=xsize]").val());
var zxsize = parseFloat($("input[name=zxsize]").val());
//var accel_segment = parseFloat($("input[name=accel_segment]").val());
//var accel_corner = parseFloat($("input[name=accel_corner]").val());
var accel_tests = parseFloat($("input[name=accel_tests]").val());
var drawspeed_slow = parseFloat($("input[name=drawspeed_slow]").val());
var accel_high = parseFloat($("input[name=accel_high]").val());
var accel_low = parseFloat($("input[name=accel_low]").val());
var dense_minseg = parseFloat($("input[name=dense_minseg]").val());
var dense_maxseg = parseFloat($("input[name=dense_maxseg]").val());
var dense_efficient = $("input[name=dense_efficient]").prop('checked');
var dense_diagonal = $("input[name=dense_diagonal]").prop('checked');
var mode = $("input[name=mode]:checked").val();
var textlines = $("textarea[name=text_input]").val().split('\n');
var stepover = parseFloat($("input[name=stepover]").val());
var direction = $("input[name=direction]").val();
var perimeter = $("input[name=perimeter]").prop('checked');
var orientation = $("input[name=orientation]").val();
var hog_count = parseFloat($("input[name=hog_count]").val());
var hog_offset = parseFloat($("input[name=hog_offset]").val());
var final_feedrate = parseFloat($("input[name=final_feedrate]").val());
var final_stepover = parseFloat($("input[name=final_stepover]").val());
var longestline = 1;
var textpitchx = 1;
var textpitchy = 1;
if (textlines.length > 0) {
for (var i=0; i < textlines.length; i++) {
if (textlines[i].length > longestline) {
longestline = textlines[i].length;
}
}
textpitchx = xsize / longestline;
textpitchy = ysize / textlines.length;
}
output_append("; mode: " + mode);
output_append("; rapid feedrate: " + rapid + " mm/min");
output_append("; raise/lower feedrate: " + vertical + " mm/min");
if (mode != "hog") {
// all non-hog mode use the normal names and include xsize and ysize
output_append("; pen down z level: " + pen_d);
output_append("; pen up z level: " + pen_u);
output_append("; drawing feedrate: " + drawspeed + " mm/min");
output_append("; x extent: " + xsize);
output_append("; y extent: " + ysize);
}
else {
output_append("; z height of cut: " + pen_d);
output_append("; clearance z height: " + pen_u);
output_append("; cutting feedrate: " + drawspeed + " mm/min");
}
if (mode == "squareness") {
// no special parameters for squareness. all the defaults (ruler parameters) are enough.
}
if (mode == "ztest_corners" || mode == "ztest_grid") {
output_append("; zxsize: " + zxsize);
}
if (mode == "dense_segments") {
output_append("; dense_minseg: " + dense_minseg);
output_append("; dense_maxseg: " + dense_maxseg);
output_append("; dense_efficient: " + dense_efficient);
output_append("; dense_diagonal: " + dense_diagonal);
}
if (mode == "text") {
output_append("; text: " + textlines.length + " lines, longest line " + longestline + " characters");
output_append("; y pitch: " + textpitchy + " mm (line-to-line)");
output_append("; x pitch: " + textpitchx + " mm (character-to-character)");
output_append("; y/x aspect ratio: " + (textpitchy/textpitchx) + " ideally should be about 2, or at least between 1 and 4");
}
if (mode == "accel_x" || mode == "accel_y") {
// output_append("; slow drawing feedrate: " + drawspeed_slow + " mm/min");
// output_append("; segment length: " + accel_segment + " mm");
// output_append("; corner radius: " + accel_corner + " mm");
output_append("; number of accel tests: " + accel_tests);
output_append("; high acceleration: " + accel_high + " mm/s^2");
output_append("; low acceleration: " + accel_low + " mm/s^2");
}
if (mode == "surfacing") {
output_append("; stepover: " + stepover + " mm");
output_append("; direction: " + direction);
output_append("; perimeter: " + perimeter);
}
if (mode == "hog") {
output_append("; slotting feedrate: " + drawspeed_slow + " mm/min");
output_append("; stepover: " + stepover + " mm");
output_append("; orientation: " + orientation);
}
var zup = " Z" + pen_u;
var zdn = " Z" + pen_d;
if (zero) {
output_append("G92 X0 Y0 Z0");
}
if (mode == "x" || mode == "xy" || mode == "perim") {
x_zig(0, xsize, 10, zup, zdn, rapid, vertical, drawspeed);
x_zag(0, xsize, 10, zup, zdn, rapid, vertical, drawspeed);
}
if (mode == "y" || mode == "xy" || mode == "perim") {
y_zig(10, 0, ysize, zup, zdn, rapid, vertical, drawspeed);
y_zag(10, 0, ysize, zup, zdn, rapid, vertical, drawspeed);
}
if (mode == "perim") {
x_zig(0, xsize, ysize-10, zup, zdn, rapid, vertical, drawspeed);
x_zag(0, xsize, ysize-10, zup, zdn, rapid, vertical, drawspeed);
y_zig(xsize-10, 0, ysize, zup, zdn, rapid, vertical, drawspeed);
y_zag(xsize-10, 0, ysize, zup, zdn, rapid, vertical, drawspeed);
}
if (mode == "squareness") {
output_append("G0" + zup + " F" + vertical);
double_line([0, 10, 0, 0, 10, 0], zup, zdn, vertical, rapid, drawspeed);
double_line([xsize-10, 0, xsize, 0, xsize, 10], zup, zdn, vertical, rapid, drawspeed);
double_line([xsize, ysize-10, xsize, ysize, xsize-10, ysize], zup, zdn, vertical, rapid, drawspeed);
double_line([10, ysize, 0, ysize, 0, ysize-10], zup, zdn, vertical, rapid, drawspeed);
// after last 'L' shape pen is up
output_append('G0 X0 Y0 F' + rapid);
}
if (mode == "ztest_corners") {
output_append("G0" + zup + " F" + vertical);
z_test(zxsize/2, zxsize/2, zxsize, zup, zdn, rapid, drawspeed);
z_test(xsize-zxsize/2, zxsize/2, zxsize, zup, zdn, rapid, drawspeed);
z_test(xsize-zxsize/2, ysize-zxsize/2, zxsize, zup, zdn, rapid, drawspeed);
z_test(zxsize/2, ysize-zxsize/2, zxsize, zup, zdn, rapid, drawspeed);
}
if (mode == "ztest_grid") {
output_append("G0" + zup + " F" + vertical);
// minimum 1 mm space between X marks: N*zxsize+(N-1)*space <= xsize (or ysize)
var space = 1;
var num_x = Math.floor((xsize+space)/(zxsize+space));
var num_y = Math.floor((ysize+space)/(zxsize+space));
if (num_x < 2 || num_y < 2) {
output_append("; not enough room for grid");
}
else {
var step_x = (xsize-zxsize)/(num_x-1);
var step_y = (ysize-zxsize)/(num_y-1);
for (var iy=0; iy < num_y; iy++) {
if (iy % 2 == 0) {
for (var ix=0; ix < num_x; ix++) {
z_test(zxsize/2+ix*step_x, zxsize/2+iy*step_y, zxsize, zup, zdn, rapid, drawspeed);
}
}
else {
for (var ix=num_x-1; ix >= 0; ix--) {
z_test(zxsize/2+ix*step_x, zxsize/2+iy*step_y, zxsize, zup, zdn, rapid, drawspeed);
}
}
}
}
}