-
Notifications
You must be signed in to change notification settings - Fork 4
/
gcodepreview-openscad_0_6.tex
3602 lines (2991 loc) · 124 KB
/
gcodepreview-openscad_0_6.tex
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
% gcodepreview.tex
% Author: William F. Adams (willadams at aol dot com)
% Copyright 2021--24 William F. Adams
%
% This work may be distributed and/or modified under the
% conditions of the GNU LESSER GENERAL PUBLIC LICENSE
% Version 2.1, February 1999
%
% This work consists of the files listed in the README file.
%
%
%
\documentclass{ltxdoc}
%https://tex.stackexchange.com/questions/722886/how-to-write-out-multiple-text-files-from-multiple-instances-of-latex-environmen
\usepackage{literati}
\usepackage[paper=legalpaper,left=1.75in,right=0.75in,top=1in,bottom=1in]{geometry}
\begin{document}
%\DoNotIndex{\bullet}
%\changes{v0.61}{2024/09/08}{modules and tests}
\def\fileversion{v0.61} \def\filedate{2024/09/08}
%\changes{v0.6}{2024/08/30}{modules and setupstock}
%\def\fileversion{v0.6} \def\filedate{2024/08/30}
%\changes{v0.5}{2024/08/10}{DXFs and images}
%\def\fileversion{v0.5} \def\filedate{2024/08/10}
%\changes{v0.4}{2024/07/28}{Literary re-write}
%\def\fileversion{v0.4} \def\filedate{2024/07/28}
%\changes{v0.3}{2024/07/01}{Curves and roundover tooling}
%\def\fileversion{v0.3} \def\filedate{2024/07/01}
%\changes{v0.2}{2024/04/12}{Initial conversion to DTX}
%\def\dtxfile{gcodepreview.dtx}
\title{The gcodepreview OpenSCAD library\thanks{This
file (\texttt{\jobname}) has version number \fileversion, last revised
\filedate.}}
\author{%
Author: William F. Adams\\
\texttt{willadams at aol dot com}
}
\date{\filedate}
\maketitle
\begin{abstract}
\noindent The gcodepreview library allows using OpenPythonSCAD to move a tool in lines
and arcs and output dxf and G-code files so as to work as a CAD/\allowbreak CAM program
for CNC.
\end{abstract}
%\enlargethispage{\baselineskip}
\tableofcontents
\clearpage
\section{readme.md}
\begin{readme}
# gcodepreview
OpenSCAD library for moving a tool in lines and arcs
so as to model how a part would be cut using G-Code,
so as to allow OpenSCAD to function as a compleat
CAD/CAM solution for subtractive 3-axis CNC (mills
and routers) by writing out G-code (in some cases
toolpaths which would not normally be feasible),
and to write out DXF files which may be imported
into a traditional CAM program to create toolpaths.
![OpenSCAD Cut Joinery Module](https://raw.githubusercontent.com/WillAdams/gcodepreview/main/gcodepreview_unittests.png?raw=true)
Updated to make use of Python in OpenSCAD:[^rapcad]
[^rapcad]: Previous versions had used RapCAD, so as to take advantage of the writeln command, which has since been re-written in Python.
https://pythonscad.org/ (previously this was http://www.guenther-sohler.net/openscad/ )
A BlockSCAD file for the initial version of the
main modules is available at:
https://www.blockscad3d.com/community/projects/1244473
The project is discussed at:
https://forum.makerforums.info/t/g-code-preview-using-openscad-rapcad/85729
and
https://forum.makerforums.info/t/openscad-and-python-looking-to-finally-be-resolved/88171
and
https://willadams.gitbook.io/design-into-3d/programming
Since it is now programmed using Literate Programming
(initially a .dtx, now a .tex file) there is a PDF:
https://github.com/WillAdams/gcodepreview/blob/main/gcodepreview.pdf
which includes all of the source code with formatted
commentary.
The files for this library are:
- gcodepreview.py (gcpy) --- the Python functions and variables
- pygcodepreview.scad (pyscad) --- the Python functions wrapped in OpenSCAD
- gcodepreview.scad (gcpscad) --- OpenSCAD modules and variables
- gcodepreview_template.scad (gcptmpl) --- example file
- cut2Dshapes.scad (cut2D) --- code for cutting 2D shapes
Place the files in C:\Users\\\~\Documents\OpenSCAD\libraries and call as:[^libraries]
[^libraries]: C:\Users\\\~\Documents\RapCAD\libraries is deprecated since RapCAD is no longer needed since Python is now used for writing out files)
use <gcodepreview.py>;
use <pygcodepreview.scad>;
include <gcodepreview.scad>;
Note that it is necessary to use the first two files
(this allows loading the Python commands and then
wrapping them in OpenSCAD commands) and then include
the last file (which allows using OpenSCAD variables
to selectively implement the Python commands via their
being wrapped in OpenSCAD modules) and define
variables which match the project and then use
commands such as:
opengcodefile(Gcode_filename);
opendxffile(DXF_filename);
difference() {
setupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin);
movetosafez();
toolchange(squaretoolno,speed * square_ratio);
begintoolpath(0,0,0.25);
beginpolyline(0,0,0.25);
cutoneaxis_setfeed("Z",-1,plunge*square_ratio);
addpolyline(stocklength/2,stockwidth/2,-stockthickness);
cutwithfeed(stocklength/2,stockwidth/2,-stockthickness,feed);
endtoolpath();
endpolyline();
}
closegcodefile();
closedxffile();
which makes a G-code file:
![OpenSCAD template G-code file](https://raw.githubusercontent.com/WillAdams/gcodepreview/main/gcodepreview_template.png?raw=true)
but one which could only be sent to a machine so as to
cut only the softest and most yielding of materials
since it makes a single full-depth pass, and of which
has a matching DXF which may be imported into a
CAM tool --- but which it is not directly possible
to assign a toolpath in readily available CAM tools
(since it varies in depth from beginning-to-end).
Importing this DXF and actually cutting it
is discussed at:
https://forum.makerforums.info/t/rewriting-gcodepreview-with-python/88617/14
Tool numbers match those of tooling sold by Carbide 3D
(ob. discl., I work for them).
Comments are included in the G-code to match those
expected by CutViewer.
A complete example file is: gcodepreview_template.scad
and another example is openscad_gcodepreview_cutjoinery.tres.scad
which is made from an OpenSCAD Graph Editor file:
![OpenSCAD Graph Editor Cut Joinery File](https://raw.githubusercontent.com/WillAdams/gcodepreview/main/OSGE_cutjoinery.png?raw=true)
Version 0.1 supports setting up stock, origin, rapid
positioning, making cuts, and writing out matching
G-code, and creating a DXF with polylines.
Added features since initial upload:
- endpolyline(); --- this command allows ending one polyline so as to allow multiple lines in a DXF
- separate dxf files are written out for each tool where tool is ball/square/V and small/large (10/31/23)
- re-writing as a Literate Program using the LaTeX package docmfp (begun 4/12/24)
- support for additional tooling shapes such as dovetail and keyhole tools
Version 0.2 adds support for arcs
- DXF: support for arcs (which may be used to make circles) (6/1/24)
- Specialty toolpaths such as Keyhole which may be used for dovetail as well as keyhole cutters
Version 0.3
- Support for curves along the 3rd dimension
- support for roundover tooling
Version 0.4
- Rewrite using literati documentclass, suppression of SVG code
- dxfrectangle (without G-code support)
Version 0.5
- more shapes
- consolidate rectangles, arcs, and circles in gcodepreview.scad
Version 0.6
- notes on modules
- change file for setupstock
Version 0.7
- reduce usage of tool numbers
- validate all code so that it runs without errors from sample file
Possible future improvements:
- support for additional tooling shapes such as tapered ball-nose tools or lollipop cutters or thread-cutting tools
- G-code: support for G2/G3 arcs and circles
- G-code: import external tool libraries and feeds and speeds from JSON or CSV files ---
- general coding improvements --- current coding style is quite prosaic
- additional generalized modules for cutting out various shapes/geometries
Note for G-code generation that it is up to the user
to implement Depth per Pass so as to not take a
single full-depth pass. Working from a DXF of course
allows one to off-load such considerations to a
specialized CAM tool.
Deprecated feature:
- exporting SVGs --- while this was begun, it turns out that these would be written out upside down due to coordinate system differences between OpenSCAD/DXFs and SVGs requiring managing the inversion of the coordinate system (it is possible that METAPOST, which shares the same orientation and which can write out SVGs will be used instead for future versions)
\end{readme}
\clearpage
\section{gcodepreview}
This library for OpenPythonSCAD works by using Python code as a back-end so as to persistently store
and access variables, and to write out files while both modeling the motion of a 3-axis CNC machine
and if desired, writing out DXF and/or G-code files (as opposed to the normal technique of
rendering to a 3D model and writing out an STL). Doing so requires a total of three files:
\begin{itemize}
\item A Python file: gcodepreview.py (\texttt{gcpy}) --- this will have variables in the
traditional sense which may be used for tracking machine position and so forth
\item An OpenSCAD file: pygcodepreview.scad (\texttt{pyscad}) --- which wraps the Python code
in OpenSCAD
\item An OpenSCAD file: gcodepreview.scad (\texttt{gcpscad}) --- which uses the other two files
and which is \texttt{include}d allowing it to access OpenSCAD variables for branching
\end{itemize}
Note that this architecture requires that many modules are essentially ``Dispatchers'' which
pass information from one aspect of the environment to another.
\subsection{gcodepreviewtemplate.scad}
The various commands are shown all together in a template so as to provide examples of usage,
and to ensure that the various files are used/included as necessary, all variables are set up
with the correct names, and that files are opened before being written to, and that each
is closed at the end.
Note that while the file seems overly verbose, it specifically incorporates variables
for each tool shape, possibly in two different sizes, and a feed rate parameter or ratio
for each, which may be used (by setting a tool \#) or ignored (by leaving the variable
at zero (0).
It should be that this section is all the documentation which some users will need.
(and arguably is still too much). The balance of the document after this section
shows all the code and implementation details.
%Creating a template from memory, the file should be something like:
%
%List of all tools
%chipload for each tool
%Depth per Pass for each tool
%scaling factor for chipload to arrive at feeds and speeds
%Booleans for generateDXF/Gcode, 3D Preview, assembly or parts or drop-down list of parts, &c.
%
%setupstock --- should this open gcode/dxf files as well?
%
%toolchange to first tool
%
%Rapid to location of first cut
%cutline/cutarc --- iterate as needed
%Rapid to safe height
%
%Program END --- should close gcode/DXF files and include rapid to safe height and rapid to back
\lstset{firstnumber=1}%\thegcptmpl}
\begin{writecode}{w}{gcodepreviewtemplate.scad}{scad}
//!OpenSCAD
use <gcodepreview.py>;
use <pygcodepreview.scad>;
include <gcodepreview.scad>;
$fa = 2;
$fs = 0.125;
/* [Export] */
Base_filename = "export";
/* [Export] */
generatedxf = true;
/* [Export] */
generategcode = true;
///* [Export] */
//generatesvg = false;
/* [CAM] */
toolradius = 1.5875;
/* [CAM] */
large_ball_tool_no = 0; // [0:0,111:111,101:101,202:202]
/* [CAM] */
large_square_tool_no = 0; // [0:0,112:112,102:102,201:201]
/* [CAM] */
large_V_tool_no = 0; // [0:0,301:301,690:690]
/* [CAM] */
small_ball_tool_no = 0; // [0:0,121:121,111:111,101:101]
/* [CAM] */
small_square_tool_no = 102; // [0:0,122:122,112:112,102:102]
/* [CAM] */
small_V_tool_no = 0; // [0:0,390:390,301:301]
/* [CAM] */
KH_tool_no = 0; // [0:0,374:374,375:375,376:376,378]
/* [CAM] */
DT_tool_no = 0; // [0:0,814:814]
/* [CAM] */
Roundover_tool_no = 56125; // [56125:56125, 56142:56142,312:312, 1570:1570]
/* [CAM] */
MISC_tool_no = 0; //
/* [Feeds and Speeds] */
plunge = 100;
/* [Feeds and Speeds] */
feed = 400;
/* [Feeds and Speeds] */
speed = 16000;
/* [Feeds and Speeds] */
small_square_ratio = 0.75; // [0.25:2]
/* [Feeds and Speeds] */
small_ball_ratio = 0.75; // [0.25:2]
/* [Feeds and Speeds] */
large_ball_ratio = 1.0; // [0.25:2]
/* [Feeds and Speeds] */
small_V_ratio = 0.625; // [0.25:2]
/* [Feeds and Speeds] */
large_V_ratio = 0.875; // [0.25:2]
/* [Feeds and Speeds] */
KH_ratio = 0.75; // [0.25:2]
/* [Feeds and Speeds] */
DT_ratio = 0.75; // [0.25:2]
/* [Feeds and Speeds] */
RO_ratio = 0.5; // [0.25:2]
/* [Feeds and Speeds] */
MISC_ratio = 0.5; // [0.25:2]
/* [Stock] */
stocklength = 219;
/* [Stock] */
stockwidth = 150;
/* [Stock] */
stockthickness = 8.35;
/* [Stock] */
zeroheight = "Top"; // [Top, Bottom]
/* [Stock] */
stockorigin = "Center"; // [Lower-Left, Center-Left, Top-Left, Center]
/* [Stock] */
retractheight = 9;
filename_gcode = str(Base_filename, ".nc");
filename_dxf = str(Base_filename);
opengcodefile(filename_gcode);
opendxffile(filename_dxf);
difference() {
setupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin);
movetosafez();
toolchange(small_square_tool_no,speed * small_square_ratio);
begintoolpath(0,0,0.25);
cutoneaxis_setfeed("Z",0,plunge*small_square_ratio);
cutwithfeed(stocklength/2,stockwidth/2,-stockthickness,feed);
dxfpolyline(getxpos(),getypos(),stocklength/2,stockwidth/2, small_square_tool_no);
endtoolpath();
rapid(-(stocklength/4-stockwidth/16),stockwidth/4,0);
cutoneaxis_setfeed("Z",-stockthickness,plunge*small_square_ratio);
cutarcNECCdxf(-stocklength/4, stockwidth/4+stockwidth/16, -stockthickness, -stocklength/4, stockwidth/4, stockwidth/16, small_square_tool_no);
cutarcNWCCdxf(-(stocklength/4+stockwidth/16), stockwidth/4, -stockthickness, -stocklength/4, stockwidth/4, stockwidth/16, small_square_tool_no);
cutarcSWCCdxf(-stocklength/4, stockwidth/4-stockwidth/16, -stockthickness, -stocklength/4, stockwidth/4, stockwidth/16, small_square_tool_no);
cutarcSECCdxf(-(stocklength/4-stockwidth/16), stockwidth/4, -stockthickness, -stocklength/4, stockwidth/4, stockwidth/16, small_square_tool_no);
rapid(getxpos(),getypos(),stockthickness);
toolchange(KH_tool_no,speed * KH_ratio);
rapid(-stocklength/8,-stockwidth/4,0);
cutkeyhole_toolpath((stockthickness), (stockthickness), "N", stockwidth/8, KH_tool_no);
rapid(getxpos(),getypos(),stockthickness);
rapid(-stocklength/4,-stockwidth/4,0);
cutkeyhole_toolpath((stockthickness), (stockthickness), "S", stockwidth/8, KH_tool_no);
rapid(getxpos(),getypos(),stockthickness);
rapid(-stocklength/4,-stockwidth/8,0);
cutkeyhole_toolpath((stockthickness), (stockthickness), "E", stockwidth/8, KH_tool_no);
rapid(getxpos(),getypos(),stockthickness);
rapid(-stocklength/8,-stockwidth/8*3,0);
cutkeyhole_toolpath((stockthickness), (stockthickness), "W", stockwidth/8, KH_tool_no);
rapid(getxpos(),getypos(),stockthickness);
toolchange(DT_tool_no,speed * DT_ratio);
rapid(0,-(stockwidth/2+tool_diameter(DT_tool_no,0)),0);
cutoneaxis_setfeed("Z",-stockthickness,plunge*DT_ratio);
cutwithfeed(0,-(stockwidth/4),-stockthickness,feed*DT_ratio);
rapid(0,-(stockwidth/2+tool_diameter(DT_tool_no,0)),-stockthickness);
rapid(getxpos(),getypos(),stockthickness);
toolchange(Roundover_tool_no, speed * RO_ratio);
rapid(-(stocklength/2),-(stockwidth/2),0);
cutoneaxis_setfeed("Z",-4.509,plunge*RO_ratio);
cutroundovertool(-(stocklength/2++0.507/2), -(stockwidth/2+0.507/2), -4.509, stocklength/2+0.507/2, -(stockwidth/2+0.507/2), -4.509, 0.507/2, 4.509);
cutroundover(stocklength/2+0.507/2, -(stockwidth/2+0.507/2), -4.509, stocklength/2+0.507/2, stockwidth/2+0.507/2, -4.509, 1570);
cutroundover(stocklength/2+0.507/2, stockwidth/2+0.507/2, -4.509, -(stocklength/2+0.507/2), stockwidth/2+0.507/2, -4.509, 1570);
cutroundover(-(stocklength/2+0.507/2), stockwidth/2+0.507/2, -4.509, -(stocklength/2+0.507/2), -(stockwidth/2+0.507/2), -4.509, 1570);
for (i = [0 : abs(1) : 80]) {
cutwithfeed(stocklength/4,-stockwidth/4,-stockthickness/4,feed);
cutwithfeed(stocklength/8+(stocklength/256*i),-stockwidth/2,-stockthickness*3/4,feed);
}
closegcodefile();
closedxffile();
\end{writecode}
\addtocounter{gcptmpl}{145}
%Note that the line:
%
%\begin{verbatim}
%toolchange(small_square_tool_no,speed * square_ratio);
%\end{verbatim}
%
%\noindent may be commented out --- whether or no it is actually necessary will need to be
%decided upon.
\subsection{Implementation}
Each file will begin with a suitable comment indicating the file type and suitable notes/comments:
\begin{writecode}{w}{gcodepreview.py}{python}
#!/usr/bin/env python
#icon "C:\Program Files\PythonSCAD\bin\openscad.exe" --trust-python
#Currently tested with 2024.09.03 and Python 3.11
#gcodepreview 0.61, see gcodepreview.scad
\end{writecode}
\addtocounter{gcpy}{6}
\begin{writecode}{w}{pygcodepreview.scad}{scad}
//!OpenSCAD
//gcodepreview 0.61, see gcodepreview.scad
\end{writecode}
\addtocounter{pyscad}{5}
\begin{writecode}{w}{gcodepreview.scad}{scad}
//!OpenSCAD
//gcodepreview 0.61
//
//used via use <gcodepreview.py>;
// use <pygcodepreview.scad>;
// include <gcodepreview.scad>;
//
\end{writecode}
\addtocounter{gcpscad}{10}
The original implementation in \textsf{RapSCAD} used a command
\DescribeRoutine{writeln} --- fortunately,
this command is easily re-created in Python:
\lstset{firstnumber=\thegcpy}
\begin{writecode}{a}{gcodepreview.py}{python}
def writeln(*arguments):
line_to_write = ""
for element in arguments:
line_to_write += element
f.write(line_to_write)
f.write("\n")
\end{writecode}
\addtocounter{gcpy}{7}
\noindent which command will accept a series of arguments and then write them out to a file
object.
\subsection{Position and Variables}
In modeling the machine motion and G-code it will be necessary to have the machine track
several variables for machine position, current tool, and depth in toolpath.
This will be done using paired functions (which will set and return the
matching variable) and a matching (global) variable, as well as additional functions for
setting the matching variable(s).
\begin{samepage}
The first such variables are for XYZ position:
\begin{itemize}
\item \DescribeVariable{mpx}
\item \DescribeVariable{mpy}
\item \DescribeVariable{mpz}
\end{itemize}
\end{samepage}
\begin{samepage}
\noindent Similarly, for some toolpaths it will be necessary to track the depth along the Z-axis
as the toolpath is cut out:
\begin{itemize}
\item \DescribeVariable{tpz}
\end{itemize}
\end{samepage}
\begin{samepage}
\noindent It will further be necessary to have a variable for the current tool:
\begin{itemize}
\item \DescribeVariable{currenttool}
\end{itemize}
\end{samepage}
Note that the \verb|currenttool| variable should always be used for any specification
of a tool, being read in whenever a tool is to be made use of, or a parameter or aspect
of the tool needs to be used in a calculation. %(Which implies that
%the tool parameters in modules other then \verb|toolchange(tool_number, speed);|
%should be removed.)
For each intended command it will be necessary to implement an appropriate aspect in each file.
The Python file will manage the Python variables and handle things which can only be done in
Python, while there will be two OpenSCAD files as noted above, one which calls the Python code
(this will be \texttt{use}d), while the other will be able to access
and use OpenSCAD variables, as well as implement Customizer options
(this will be \texttt{include}d).
\subsection{Output files}
\subsubsection{G-code and modules and commands}
Each module/command will write out certain G-code commands:
\bigskip
\noindent \begin{tabular}{@{}ll@{}} \toprule
Command/Module & G-code \\ \midrule
\texttt{opengcodefile(...);}
\texttt{setupstock(...)} & \texttt{(export.nc)} \\
& \texttt{(stockMin: -109.5, -75mm, -8.35mm)}\\
& \texttt{(stockMax:109.5mm, 75mm, 0.00mm)}\\
& \texttt{(STOCK/BLOCK, 219, 150, 8.35, 109.5, 75, 8.35)}\\
& \texttt{G90}\\
& \texttt{G21} \\ \midrule
\texttt{movetosafez()} & \texttt{(Move to safe Z to avoid workholding)}\\
& \texttt{G53G0Z-5.000}\\ \midrule
\texttt{toolchange(...);} & \texttt{(TOOL/MILL,3.17, 0.00, 0.00, 0.00)} \\
& \texttt{M6T102} \\
& \texttt{M03S16000} \\ \midrule
\verb|cutoneaxis_setfeed(...);| & \texttt{(PREPOSITION FOR RAPID PLUNGE)}\\
& \texttt{G0X0Y0} \\
& \texttt{Z0.25} \\
& \texttt{G1Z0F100} \\
& \texttt{G1 X109.5 Y75 Z-8.35F400} \\
& \texttt{Z9}\\
\texttt{cutwithfeed(...);}\\ \midrule
\texttt{closegcodefile();} & \texttt{M05}\\
& \texttt{M02}\\
\bottomrule
\end{tabular}
\bigskip
%(Toolpath)
%M05
\noindent Conversely, the G-code commands which are supported are generated by the following modules:
\bigskip
\noindent \begin{tabular}{@{}ll@{}} \toprule
G-code & Command/Module \\ \midrule
\verb|(Design File: )| & \texttt{opengcodefile(...);}
\texttt{setupstock(...)}\\
\verb|(stockMin:0.00mm, -152.40mm, -34.92mm)| \\
\verb|(stockMax:109.50mm, -77.40mm, 0.00mm)| \\
\verb|(STOCK/BLOCK,109.50, 75.00, 34.92,0.00, 152.40, 34.92)| \\
\verb|G90| \\
\verb|G21| \\ \midrule
\verb|(Move to safe Z to avoid workholding)| & \texttt{movetosafez()} \\
\verb|G53G0Z-5.000| \\ \midrule
\verb|(Toolpath: Contour Toolpath 1)| & \texttt{toolchange(...);}\\
\verb|M05| \\
\verb|(TOOL/MILL,3.17, 0.00, 0.00, 0.00)| \\
\verb|M6T102| \\
\verb|M03S10000| \\ \midrule
\verb|(PREPOSITION FOR RAPID PLUNGE)| & \texttt{writecomment(...)}\\ \midrule
\verb|G0X0.000Y-152.400| & \texttt{rapid(...)}\\
\verb|Z0.250| & \texttt{rapid(...)}\\ \midrule
\verb|G1Z-1.000F203.2| & \texttt{cutwithfeed(...);}\\
\verb|X109.500Y-77.400F508.0| & \texttt{cutwithfeed(...);} \\
\verb|X57.918Y16.302Z-0.726|\\
\verb|Y22.023Z-1.023|\\
\verb|X61.190Z-0.681|\\
\verb|Y21.643|\\
\verb|X57.681|\\
\verb|Z12.700|\\ \midrule
\verb|M05| & \texttt{closegcodefile();}\\
\verb|M02|\\
\bottomrule
\end{tabular}
\bigskip
The implication here is that it should be possible to read in a G-code file, and for each
line/command instantiate a matching command so as to create a 3D model/preview of the file.
\subsubsection{DXF}
Elements in DXFs are represented as lines or arcs. A minimal file showing both:
\begin{verbatim}
0
SECTION
2
ENTITIES
0
LWPOLYLINE
90
2
70
0
43
0
10
-31.375
20
-34.9152
10
-31.375
20
-18.75
0
ARC
10
-54.75
20
-37.5
40
4
50
0
51
90
0
ENDSEC
0
EOF
\end{verbatim}
\subsection{Modules}
Note that as a convention, where it is necessary for a module to coordinate between
Python~and OpenSCAD, in certain cases it will be necessary for there to be three separate
versions: a \texttt{p}<foo>~Python definition for the manipulation of Python variables and
any file routines, an \texttt{o}<foo> OpenSCAD module which will wrap up the Python
function call, and lastly a <foo> OpenSCAD module which will be \texttt{<include>}d
so as to be able to make use of OpenSCAD variables.
In natural languages such as English, there is an order to various parts of speech such as
adjectives
%(determiner, opinion/observation, size, age, shape, color, origin/nationality,
%material, purpose/qualifier)
--- since various prefixes and suffixes will be used for module names, having a consistent
ordering/usage will help in consistency and make expression clearer.
The ordering should be: sequence, action, function, parameter, filetype
\begin{outline}
\1 Both prefix and suffix
\2 \texttt{dxf} (action (write out dxf file), filetype)
\1 Prefixes
\2 \texttt{begin} (sequence)
\2 \texttt{continue} (sequence)
\2 \texttt{end} (sequence)
\2 \texttt{cut} (action)
\2 \texttt{move} (action)
\2 \texttt{rapid} (action)
\2 \texttt{open} (action)
\2 \texttt{close} (action)
\2 \texttt{set} (action/function)
\1 Suffixes
\2 \texttt{feed} (parameter)
\2 \texttt{gcode} (filetype)
\2 \texttt{polyline) (file (element))}
\end{outline}
%\subsubsection{Interfaces}
For the sake of convenience, all user-facing modules will be listed here with their interface
requirements/variables. Where appropriate, modules which interact will be listed together.
%\lstset{firstnumber=1}
%\begin{writecode}{w}{interfaces.txt}{txt}
\begin{verbatim}
begincutdxf(rh, ex, ey, ez, fr); and continuecutdxf(ex, ey, ez, fr);
beginpolyline(bx,by,bz); and addpolyline(bx,by,bz); and closepolyline();
begintoolpath(bx,by,bz); and endtoolpath();
current_tool(); [function]
cut(ex, ey, ez);
cutoneaxis_setfeed(axis,depth,feed);
cutwithfeed(ex, ey, ez, feed);
cutarcNECCdxf(ex, ey, ez, xcenter, ycenter, radius);
cutarcNWCCdxf(ex, ey, ez, xcenter, ycenter, radius);
cutarcSWCCdxf(ex, ey, ez, xcenter, ycenter, radius);
cutarcSECCdxf(ex, ey, ez, xcenter, ycenter, radius);
cutarcNECWdxf(ex, ey, ez, xcenter, ycenter, radius);
cutarcSECWdxf(ex, ey, ez, xcenter, ycenter, radius);
cutarcSWCWdxf(ex, ey, ez, xcenter, ycenter, radius);
cutkeyhole_toolpath(kh_start_depth, kh_max_depth, kht_direction, kh_distance);
cutoutrectangledxf(bx, by, bz, rwidth, rheight, rdepth);
cutrectangledxf(bx, by, bz, rwidth, rheight, rdepth);
cutrectangleoutlinedxf(bx, by, bz, rwidth, rheight, rdepth);
cutroundover(bx, by, bz, ex, ey, ez);
dxfarc(xcenter, ycenter, radius, anglebegin, endangle);
dxfpolyline(xbegin,ybegin,xend,yend);
movetosafeheight();
movetosafez();
opendxffile(fn); and closedxffile();
opengcodefile(fn); and closegcodefile();
rapidbx(bx, by, bz, ex, ey, ez);
rapid(ex, ey, ez);
rectangleoutlinedxf(bx, by, bz, rwidth, rheight);
setupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin);
setxpos(newxpos);
setypos(newypos);
setzpos(newzpos);
settzpos(newtzpos);
toolchange(tool_number,speed);
tool_diameter(td_tool, td_depth); [function]
tool_radius(td_tool, td_depth); [function]
writecomment(comment);
\end{verbatim}
%\end{writecode}
Principles for naming modules (and variables):
\begin{itemize}
\item minimize use of underscores (for convenience sake, underscores are not used for index entries)
\item identify which aspect of the project structure is being worked with (\texttt{cut}(ting),
\texttt{dxf}, \texttt{gcode}, \texttt{tool} management, etc.) and esp. note the use of
\texttt{o}(penscad) and \texttt{p}(ython) as prefixes
\end{itemize}
Structurally, this will typically look like:
\begin{verbatim}
The user-facing module is \DescribeRoutine{FOOBAR}
\lstset{firstnumber=\thegcpscad}
\begin{writecode}{a}{gcodepreview.scad}{scad}
module FOOBAR(...) {
oFOOBAR(...);
}
\end{writecode}
\addtocounter{gcpscad}{4}
which calls the internal OpenSCAD Module \DescribeSubroutine{FOOBAR}{oFOOBAR}
\begin{writecode}{a}{pygcodepreview.scad}{scad}
module oFOOBAR(...) {
pFOOBAR(...);
}
\end{writecode}
\addtocounter{pyscad}{4}
which in turn calls the internal Python definitioon \DescribeSubroutine{FOOBAR}{pFOOBAR}
\lstset{firstnumber=\thegcpy}
\begin{writecode}{a}{gcodepreview.py}{python}
def pFOOBAR (...)
...
\end{writecode}
\addtocounter{gcpy}{3}
\end{verbatim}
Further note that this definition will not be necessary for some later modules since they are
in turn calling internal modules which already use this structure.
\subsubsection{Initial Modules}
The first such routine, (actually a subroutine, see \DescribeRoutine{setupstock})
\DescribeSubroutine{setupstock}{psetupstock} will be appropriately enough,
to set up the stock, and perform other initializations --- in Python all that needs
to be done is to set the value of the persistent (Python) variables:
\lstset{firstnumber=\thegcpy}
\begin{writecode}{a}{gcodepreview.py}{python}
def psetupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin):
global mpx
mpx = float(0)
global mpy
mpy = float(0)
global mpz
mpz = float(0)
global tpz
tpz = float(0)
global currenttool
currenttool = 102
\end{writecode}
\addtocounter{gcpy}{12}
Note that while the \#102 is declared as a default tool, while it was originally necessary
to call a tool change after invoking \verb|setupstock| in the 2024.09.03 version of
PythonSCAD this requirement went away when a but which interfered with persistently setting
a variable directly was fixed.
The intermediary OpenSCAD code, \DescribeSubroutine{setupstock}{osetupstock} simply
calls the Python version. Note that while the parameters are passed all the way down
(for consistency) they are not used.
\lstset{firstnumber=\thepyscad}
\begin{writecode}{a}{pygcodepreview.scad}{scad}
module osetupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin) {
psetupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin);
}
\end{writecode}
\addtocounter{pyscad}{4}
The OpenSCAD code, \DescribeRoutine{setupstock} requires that the user set parameters
for stock dimensions and so forth, and will create comments in the G-code which incorporate
the stock dimensions and its position relative to the zero as set relative to the stock.
The internal variable \verb|stockorigin| is used in an \verb|<if then else>| structure to position
the 3D model of the stock.
\lstset{firstnumber=\thegcpscad}
\begin{writecode}{a}{gcodepreview.scad}{scad}
module setupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin) {
osetupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin);
//initialize default tool and XYZ origin
osettool(102);
oset(0,0,0);
if (zeroheight == "Top") {
if (stockorigin == "Lower-Left") {
translate([0, 0, (-stockthickness)]){
cube([stocklength, stockwidth, stockthickness], center=false);
if (generategcode == true) {
owritethree("(stockMin:0.00mm, 0.00mm, -",str(stockthickness),"mm)");
owritefive("(stockMax:",str(stocklength),"mm, ",str(stockwidth),"mm, 0.00mm)");
owritenine("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),", 0.00, 0.00, ",str(stockthickness),")");
}
}
}
else if (stockorigin == "Center-Left") {
translate([0, (-stockwidth / 2), -stockthickness]){
cube([stocklength, stockwidth, stockthickness], center=false);
if (generategcode == true) {
owritefive("(stockMin:0.00mm, -",str(stockwidth/2),"mm, -",str(stockthickness),"mm)");
owritefive("(stockMax:",str(stocklength),"mm, ",str(stockwidth/2),"mm, 0.00mm)");
owriteeleven("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),", 0.00, ",str(stockwidth/2),", ",str(stockthickness),")");
}
}
} else if (stockorigin == "Top-Left") {
translate([0, (-stockwidth), -stockthickness]){
cube([stocklength, stockwidth, stockthickness], center=false);
if (generategcode == true) {
owritefive("(stockMin:0.00mm, -",str(stockwidth),"mm, -",str(stockthickness),"mm)");
owritethree("(stockMax:",str(stocklength),"mm, 0.00mm, 0.00mm)");
owriteeleven("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),", 0.00, ",str(stockwidth),", ",str(stockthickness),")");
}
}
}
else if (stockorigin == "Center") {
//owritecomment("Center");
translate([(-stocklength / 2), (-stockwidth / 2), -stockthickness]){
cube([stocklength, stockwidth, stockthickness], center=false);
if (generategcode == true) {
owriteseven("(stockMin: -",str(stocklength/2),", -",str(stockwidth/2),"mm, -",str(stockthickness),"mm)");
owritefive("(stockMax:",str(stocklength/2),"mm, ",str(stockwidth/2),"mm, 0.00mm)");
owritethirteen("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),", ",str(stocklength/2),", ", str(stockwidth/2),", ",str(stockthickness),")");
}
}
}
} else if (zeroheight == "Bottom") {
//owritecomment("Bottom");
if (stockorigin == "Lower-Left") {
cube([stocklength, stockwidth, stockthickness], center=false);
if (generategcode == true) {
owriteone("(stockMin:0.00mm, 0.00mm, 0.00mm)");
owriteseven("(stockMax:",str(stocklength),"mm, ",str(stockwidth),"mm, ",str(stockthickness),"mm)");
owriteseven("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),",0.00, 0.00, 0.00)");
}
} else if (stockorigin == "Center-Left") {
translate([0, (-stockwidth / 2), 0]){
cube([stocklength, stockwidth, stockthickness], center=false);
if (generategcode == true) {
owritethree("(stockMin:0.00mm, -",str(stockwidth/2),"mm, 0.00mm)");
owriteseven("(stockMax:",str(stocklength),"mm, ",str(stockwidth/2),"mm, ",str(stockthickness),"mm)");
owritenine("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),",0.00, ",str(stockwidth/2),", 0.00)");
}
}
} else if (stockorigin == "Top-Left") {
translate([0, (-stockwidth), 0]){
cube([stocklength, stockwidth, stockthickness], center=false);
}
if (generategcode == true) {
owritethree("(stockMin:0.00mm, -",str(stockwidth),"mm, 0.00mm)");
owritefive("(stockMax:",str(stocklength),"mm, 0.00mm, ",str(stockthickness),"mm)");
owritenine("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),", 0.00, ", str(stockwidth),", 0.00)");
}
} else if (stockorigin == "Center") {
translate([(-stocklength / 2), (-stockwidth / 2), 0]){
cube([stocklength, stockwidth, stockthickness], center=false);
}
if (generategcode == true) {
owritefive("(stockMin:-",str(stocklength/2),", -",str(stockwidth/2),"mm, 0.00mm)");
owriteseven("(stockMax:",str(stocklength/2),"mm, ",str(stockwidth/2),"mm, ",str(stockthickness),"mm)");
owriteeleven("(STOCK/BLOCK, ",str(stocklength),", ",str(stockwidth),", ",str(stockthickness),", ",str(stocklength/2),", ", str(stockwidth/2),", 0.00)");
}
}
}
if (generategcode == true) {
owriteone("G90");
owriteone("G21");
// owriteone("(Move to safe Z to avoid workholding)");
// owriteone("G53G0Z-5.000");
}
//owritecomment("ENDSETUP");
}
\end{writecode}
\addtocounter{gcpscad}{93}
An example usage would be:
\begin{verbatim}
difference() {
setupstock(stocklength, stockwidth, stockthickness, zeroheight, stockorigin);
... // Cutting commands go here
}
\end{verbatim}
It will be necessary to have Python functions (\DescribeRoutine{xpos}, \DescribeRoutine{ypos},
and \DescribeRoutine{zpos}) which return the current values of the machine position in
Cartesian coordinates:
\lstset{firstnumber=\thegcpy}
\begin{writecode}{a}{gcodepreview.py}{python}
def xpos():
global mpx