-
Notifications
You must be signed in to change notification settings - Fork 4
/
gcodepreview.tex
4043 lines (3383 loc) · 159 KB
/
gcodepreview.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.7}{2024/11/11}{Python re-write}
\def\fileversion{v0.7} \def\filedate{2024/11/11}
%\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
OpenPythonSCAD 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 OpenPythonSCAD to function as a compleat CAD/CAM solution for subtractive 3-axis CNC (mills and routers) by writing out G-code in addition to 3D modeling (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
If using from OpenPythonSCAD, 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(stockXwidth, stockYheight, stockZthickness, zeroheight, stockzero);
movetosafez();
toolchange(squaretoolnum,speed * square_ratio);
begintoolpath(0,0,0.25);
beginpolyline(0,0,0.25);
cutoneaxis_setfeed("Z",-1,plunge*square_ratio);
addpolyline(stockXwidth/2,stockYheight/2,-stockZthickness);
cutwithfeed(stockXwidth/2,stockYheight/2,-stockZthickness,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
Alternately, gcodepreview.py may be placed in a Python library location and used directly from Python --- note that it is possible to use it from a "normal" Python when generating only DXFs.
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 Note that a Python template has since been developed as well, allowing usage without OpenSCAD code, 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.61
- validate all code so that it runs without errors from sample file
- NEW: Note that this version is archived as gcodepreview-openscad_0_6.tex and the matching PDF is available as well
Version 0.7
- re-write completely in Python --- note that it is possible to use from within OpenPythonSCAD and an OpenSCAD wrapper is not functional at this time --- note that the OpenSCAD wrapper will need to be rewritten
Possible future improvements:
- rewrite OpenSCAD wrapper
- restore support for additional tooling shapes (dovetail, roundover)
- support for additional tooling shapes such as tapered ball-nose tools or lollipop cutters or thread-cutting tools
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 or STEP or other model format). There are multiple modes for this, doing so requires up to three files:
\begin{itemize}
\item A Python file: gcodepreview.py (\texttt{gcpy}) --- this has variables in the
traditional sense which may be used for tracking machine position and so forth.
Note that where it is placed/loaded from will depend on whether it is imported into
a Python file:\\
\verb|import gcodepreview_standalone as gcp|\\
or used in an OpenSCAD file:\\
\verb|use <gcodepreview.py>;|\\
with additional OpenSCAD modules which allow accessing it
\item An OpenSCAD file: pygcodepreview.scad (\texttt{pyscad}) --- which wraps the Python code
in OpenSCAD (note that it too is included by \verb|use <pygcodepreview.scad>|)
\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}
\noindent Note that this architecture requires that many OpenSCAD modules are essentially
``Dispatchers'' which pass information from one aspect of the environment to another.
\subsection{gcodepreviewtemplate}
The various commands are shown all together in templates 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 template files seem overly verbose, they specifically incorporate 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
\subsubsection{gcodepreviewtemplate.scad}
\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;
/* [Stock] */
stockXwidth = 219;
/* [Stock] */
stockYheight = 150;
/* [Stock] */
stockZthickness = 8.35;
/* [Stock] */
zeroheight = "Top"; // [Top, Bottom]
/* [Stock] */
stockzero = "Center"; // [Lower-Left, Center-Left, Top-Left, Center]
/* [Stock] */
retractheight = 9;
/* [Export] */
Base_filename = "export";
/* [Export] */
generatedxf = true;
/* [Export] */
generategcode = true;
///* [Export] */
//generatesvg = false;
/* [CAM] */
toolradius = 1.5875;
/* [CAM] */
large_square_tool_num = 0; // [0:0,112:112,102:102,201:201]
/* [CAM] */
small_square_tool_num = 102; // [0:0,122:122,112:112,102:102]
/* [CAM] */
large_ball_tool_num = 0; // [0:0,111:111,101:101,202:202]
/* [CAM] */
small_ball_tool_num = 0; // [0:0,121:121,111:111,101:101]
/* [CAM] */
large_V_tool_num = 0; // [0:0,301:301,690:690]
/* [CAM] */
small_V_tool_num = 0; // [0:0,390:390,301:301]
/* [CAM] */
DT_tool_num = 0; // [0:0,814:814]
/* [CAM] */
KH_tool_num = 0; // [0:0,374:374,375:375,376:376,378]
/* [CAM] */
Roundover_tool_num = 0; // [56125:56125, 56142:56142,312:312, 1570:1570]
/* [CAM] */
MISC_tool_num = 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] */
large_ball_ratio = 1.0; // [0.25:2]
/* [Feeds and Speeds] */
small_ball_ratio = 0.75; // [0.25:2]
/* [Feeds and Speeds] */
large_V_ratio = 0.875; // [0.25:2]
/* [Feeds and Speeds] */
small_V_ratio = 0.625; // [0.25:2]
/* [Feeds and Speeds] */
DT_ratio = 0.75; // [0.25:2]
/* [Feeds and Speeds] */
KH_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]
filename_gcode = str(Base_filename, ".nc");
filename_dxf = str(Base_filename);
opengcodefile(filename_gcode);
opendxffile(filename_dxf);
difference() {
setupstock(stockXwidth, stockYheight, stockZthickness, zeroheight, stockzero);
movetosafez();
toolchange(small_square_tool_num,speed * small_square_ratio);
begintoolpath(0,0,0.25);
cutoneaxis_setfeed("Z",0,plunge*small_square_ratio);
cutwithfeed(stockXwidth/2,stockYheight/2,-stockZthickness,feed);
dxfpolyline(getxpos(),getypos(),stockXwidth/2,stockYheight/2, small_square_tool_num);
endtoolpath();
rapid(-(stockXwidth/4-stockYheight/16),stockYheight/4,0);
cutoneaxis_setfeed("Z",-stockZthickness,plunge*small_square_ratio);
cutarcNECCdxf(-stockXwidth/4, stockYheight/4+stockYheight/16, -stockZthickness, -stockXwidth/4, stockYheight/4, stockYheight/16, small_square_tool_num);
cutarcNWCCdxf(-(stockXwidth/4+stockYheight/16), stockYheight/4, -stockZthickness, -stockXwidth/4, stockYheight/4, stockYheight/16, small_square_tool_num);
cutarcSWCCdxf(-stockXwidth/4, stockYheight/4-stockYheight/16, -stockZthickness, -stockXwidth/4, stockYheight/4, stockYheight/16, small_square_tool_num);
cutarcSECCdxf(-(stockXwidth/4-stockYheight/16), stockYheight/4, -stockZthickness, -stockXwidth/4, stockYheight/4, stockYheight/16, small_square_tool_num);
rapid(getxpos(),getypos(),stockZthickness);
toolchange(KH_tool_num,speed * KH_ratio);
rapid(-stockXwidth/8,-stockYheight/4,0);
cutkeyhole_toolpath((stockZthickness), (stockZthickness), "N", stockYheight/8, KH_tool_num);
rapid(getxpos(),getypos(),stockZthickness);
rapid(-stockXwidth/4,-stockYheight/4,0);
cutkeyhole_toolpath((stockZthickness), (stockZthickness), "S", stockYheight/8, KH_tool_num);
rapid(getxpos(),getypos(),stockZthickness);
rapid(-stockXwidth/4,-stockYheight/8,0);
cutkeyhole_toolpath((stockZthickness), (stockZthickness), "E", stockYheight/8, KH_tool_num);
rapid(getxpos(),getypos(),stockZthickness);
rapid(-stockXwidth/8,-stockYheight/8*3,0);
cutkeyhole_toolpath((stockZthickness), (stockZthickness), "W", stockYheight/8, KH_tool_num);
rapid(getxpos(),getypos(),stockZthickness);
toolchange(DT_tool_num,speed * DT_ratio);
rapid(0,-(stockYheight/2+tool_diameter(DT_tool_num,0)),0);
cutoneaxis_setfeed("Z",-stockZthickness,plunge*DT_ratio);
cutwithfeed(0,-(stockYheight/4),-stockZthickness,feed*DT_ratio);
rapid(0,-(stockYheight/2+tool_diameter(DT_tool_num,0)),-stockZthickness);
rapid(getxpos(),getypos(),stockZthickness);
toolchange(Roundover_tool_num, speed * RO_ratio);
rapid(-(stockXwidth/2),-(stockYheight/2),0);
cutoneaxis_setfeed("Z",-4.509,plunge*RO_ratio);
cutroundovertool(-(stockXwidth/2++0.507/2), -(stockYheight/2+0.507/2), -4.509, stockXwidth/2+0.507/2, -(stockYheight/2+0.507/2), -4.509, 0.507/2, 4.509);
cutroundover(stockXwidth/2+0.507/2, -(stockYheight/2+0.507/2), -4.509, stockXwidth/2+0.507/2, stockYheight/2+0.507/2, -4.509, 1570);
cutroundover(stockXwidth/2+0.507/2, stockYheight/2+0.507/2, -4.509, -(stockXwidth/2+0.507/2), stockYheight/2+0.507/2, -4.509, 1570);
cutroundover(-(stockXwidth/2+0.507/2), stockYheight/2+0.507/2, -4.509, -(stockXwidth/2+0.507/2), -(stockYheight/2+0.507/2), -4.509, 1570);
//for (i = [0 : abs(1) : 80]) {
// cutwithfeed(stockXwidth/4,-stockYheight/4,-stockZthickness/4,feed);
// cutwithfeed(stockXwidth/8+(stockXwidth/256*i),-stockYheight/2,-stockZthickness*3/4,feed);
// }
hull(){
cutwithfeed(stockXwidth/4,-stockYheight/4,-stockZthickness/4,feed);
cutwithfeed(stockXwidth/8,-stockYheight/2,-stockZthickness*3/4,feed);
cutwithfeed(stockXwidth/8+(stockXwidth*0.3125),-stockYheight/2,-stockZthickness*3/4,feed);
}
}
closegcodefile();
closedxffile();
\end{writecode}
\addtocounter{gcptmpl}{157}
%Note that the line:
%
%\begin{verbatim}
%toolchange(small_square_tool_num,speed * square_ratio);
%\end{verbatim}
%
%\noindent may be commented out --- whether or no it is actually necessary will need to be
%decided upon.
\begin{samepage}
Which cuts as:
\includegraphics[width=\linewidth]{C:/Users/willa/OneDrive/Documents/GitHub/gcodepreview/gcodepreview_unittests.png}
\end{samepage}
Some comments on the template:
\begin{itemize}
\item minimal --- it is intended as a framework for a minimal working example (MWE) --- it should be
possible to comment out unused portions and so arrive at code which tests
any aspect of this project
\item compleat --- a quite wide variety of tools are listed (and probably more will be
added in the future), but pre-defining them and having these ``hooks'' seems
the easiest (non-object-oriented) mechanism to handle everything
\item shortcuts --- as the last example shows, while in real life it is necessary to make many
passes with a tool, an expedient shortcut is to forgo the \verb|loop|
operation and just use a \verb|hull()| operation
\end{itemize}
Further features will be added to the template, and the main image updated to reflect the capabilities of the system.
\subsubsection{gcodepreviewtemplate.py}
Note that with the v0.7 re-write, it is possible to directly use the underlying Python code directly.
\lstset{firstnumber=1}%\thegcptmplpy}
\begin{writecode}{w}{gcodepreviewtemplate.py}{python}
#!/usr/bin/env python
# getting openscad functions into namespace
#https://github.com/gsohler/openscad/issues/39
from openscad import *
import sys
try:
if 'gcodepreview' in sys.modules:
del sys.modules['gcodepreview']
except AttributeError:
pass
#Below command only needed if using withing OpenPythonSCAD
from gcodepreview import *
#fa = 2
#fs = 0.125
# [Export] */
Base_filename = "export"
# [Export] */
generatedxf = True
# [Export] */
generategcode = True
# [Stock] */
stockXwidth = 219
# [Stock] */
stockYheight = 150
# [Stock] */
stockZthickness = 8.35
# [Stock] */
zeroheight = "Top" # [Top, Bottom]
# [Stock] */
stockzero = "Center" # [Lower-Left, Center-Left, Top-Left, Center]
# [Stock] */
retractheight = 9
# [CAM] */
toolradius = 1.5875
# [CAM] */
large_square_tool_num = 0 # [0:0,112:112,102:102,201:201]
# [CAM] */
small_square_tool_num = 102 # [0:0,122:122,112:112,102:102]
# [CAM] */
large_ball_tool_num = 0 # [0:0,111:111,101:101,202:202]
# [CAM] */
small_ball_tool_num = 0 # [0:0,121:121,111:111,101:101]
# [CAM] */
large_V_tool_num = 0 # [0:0,301:301,690:690]
# [CAM] */
small_V_tool_num = 0 # [0:0,390:390,301:301]
# [CAM] */
DT_tool_num = 0 # [0:0,814:814]
# [CAM] */
KH_tool_num = 0 # [0:0,374:374,375:375,376:376,378]
# [CAM] */
Roundover_tool_num = 0 # [56125:56125, 56142:56142,312:312, 1570:1570]
# [CAM] */
MISC_tool_num = 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] */
large_ball_ratio = 1.0 # [0.25:2]
# [Feeds and Speeds] */
small_ball_ratio = 0.75 # [0.25:2]
# [Feeds and Speeds] */
large_V_ratio = 0.875 # [0.25:2]
# [Feeds and Speeds] */
small_V_ratio = 0.625 # [0.25:2]
# [Feeds and Speeds] */
DT_ratio = 0.75 # [0.25:2]
# [Feeds and Speeds] */
KH_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]
gcp = gcodepreview(Base_filename, #"export", basefilename
True, #generategcode
True, #generatedxf
stockXwidth,
stockYheight,
stockZthickness,
zeroheight,
stockzero,
retractheight,
large_square_tool_num,
toolradius,
plunge,
feed,
speed)
gcp.opengcodefile(Base_filename)
gcp.opendxffiles(Base_filename)
gcp.setupstock(stockXwidth,stockYheight,stockZthickness,"Top","Center",retractheight)
gcp.movetosafeZ()
gcp.toolchange(102,10000)
#gcp.rapidXY(6,12)
gcp.rapidZ(0)
#print (gcp.xpos())
#print (gcp.ypos())
#psetzpos(7)
#gcp.setzpos(-12)
#print (gcp.zpos())
#print ("X", str(gcp.xpos()))
#print ("Y", str(gcp.ypos()))
#print ("Z", str(gcp.zpos()))
toolpaths = gcp.currenttool()
toolpaths = toolpaths.union(gcp.cutlinedxfgc(stockXwidth/2, stockYheight/2, -stockZthickness))
gcp.rapidZ(retractheight)
gcp.toolchange(201,10000)
gcp.rapidXY(0, stockYheight/16)
gcp.rapidZ(0)
toolpaths = toolpaths.union(gcp.cutlinedxfgc(stockXwidth/16*7, stockYheight/2, -stockZthickness))
gcp.setzpos(retractheight)
gcp.toolchange(202,10000)
gcp.rapidXY(0, stockYheight/8)
gcp.rapidZ(0)
toolpaths = toolpaths.union(gcp.cutlinedxfgc(stockXwidth/16*6, stockYheight/2, -stockZthickness))
gcp.setzpos(retractheight)
gcp.toolchange(101,10000)
gcp.rapidXY(0, stockYheight/16*3)
gcp.rapidZ(0)
toolpaths = toolpaths.union(gcp.cutlinedxfgc(stockXwidth/16*5, stockYheight/2, -stockZthickness))
gcp.setzpos(retractheight)
gcp.toolchange(390,10000)
gcp.rapidXY(0, stockYheight/16*4)
gcp.rapidZ(0)
toolpaths = toolpaths.union(gcp.cutlinedxfgc(stockXwidth/16*4, stockYheight/2, -stockZthickness))
gcp.setzpos(retractheight)
gcp.toolchange(301,10000)
gcp.rapidXY(0, stockYheight/16*6)
gcp.rapidZ(0)
toolpaths = toolpaths.union(gcp.cutlinedxfgc(stockXwidth/16*2, stockYheight/2, -stockZthickness))
#gcp.setzpos(retractheight)
#gcp.toolchange(102,10000)
#gcp.rapidXY(stockXwidth/4+stockYheight/16, -(stockYheight/4))
#gcp.rapidZ(0)
##arcloop(barc, earc, xcenter, ycenter, radius)
#gcp.settzpos(stockZthickness/90)
#toolpaths = toolpaths.union(gcp.arcloop(0, 90, stockXwidth/4, -stockYheight/4, stockYheight/16))
gcp.setzpos(retractheight)
gcp.toolchange(102,10000)
gcp.rapidXY(stockXwidth/4+stockYheight/8+stockYheight/16, +stockYheight/8)
gcp.rapidZ(0)
#gcp.settzpos(stockZthickness/90)
#toolpaths = toolpaths.union(gcp.arcloop(0, 90, stockXwidth/4+stockYheight/8, stockYheight/8, stockYheight/16))
toolpaths = toolpaths.union(gcp.cutarcNECCdxf(stockXwidth/4+stockYheight/8, stockYheight/8+stockYheight/16, -stockZthickness, stockXwidth/4+stockYheight/8, stockYheight/8, stockYheight/16))
toolpaths = toolpaths.union(gcp.cutarcNWCCdxf(stockXwidth/4+stockYheight/8-stockYheight/16, stockYheight/8, -stockZthickness, stockXwidth/4+stockYheight/8, stockYheight/8, stockYheight/16))
toolpaths = toolpaths.union(gcp.cutarcSWCCdxf(stockXwidth/4+stockYheight/8, stockYheight/8-stockYheight/16, -stockZthickness, stockXwidth/4+stockYheight/8, stockYheight/8, stockYheight/16))
toolpaths = toolpaths.union(gcp.cutarcSECCdxf(stockXwidth/4+stockYheight/8+stockYheight/16, stockYheight/8, -stockZthickness, stockXwidth/4+stockYheight/8, stockYheight/8, stockYheight/16))
#a = gcp.currenttool()
#arcbegin = a.translate([64.37357214209116, -37.33638368965047,-stockZthickness])
#arcend = a.translate([55.16361631034953, -28.12642785790883,-stockZthickness])
#toolpaths = toolpaths.union(arcbegin)
#toolpaths = toolpaths.union(arcend)
#cu = cube([10,20,30])
#c = cu.translate([0,0,gcp.zpos()])
part = gcp.stock.difference(toolpaths)
#output(gcp.stock)
#output(gcp.currenttool())
#output(test)
output(part)
#output(toolpaths)
#output (arc)
gcp.setzpos(retractheight)
gcp.closegcodefile()
gcp.closedxffile()
#gcp.closedxffiles()
\end{writecode}
\addtocounter{gcptmplpy}{201}
\subsection{Implementation files and gcodepreview class}
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.23 and Python 3.11
#gcodepreview 0.7, for use with OpenPythonSCAD,
#if using from OpenPythonSCAD see gcodepreview.scad
# getting openscad functions into namespace
#https://github.com/gsohler/openscad/issues/39
from openscad import *
# add math functions (using radians by default, convert to degrees where necessary)
import math
\end{writecode}
\addtocounter{gcpy}{14}
\begin{writecode}{w}{pygcodepreview.scad}{scad}
//!OpenSCAD
//gcodepreview 0.7, see gcodepreview.scad
\end{writecode}
\addtocounter{pyscad}{4}
\begin{writecode}{w}{gcodepreview.scad}{scad}
//!OpenSCAD
//gcodepreview 0.7
//
//used via use <gcodepreview.py>;
// use <pygcodepreview.scad>;
// include <gcodepreview.scad>;
//
\end{writecode}
\addtocounter{gcpscad}{9}
If all functions are to be handled within Python, then they will need to be gathered into a class which contains them and which may be initialized so as to define shared variables, and then there will need to be objects/commands for each aspect of the program, each of which will initialize needed variables and will contain appropriate commands. Note that they will be divided between mandatory and optional functions/variables/objects:
\begin{outline}
\1 Mandatory
\2 stocksetup:
\3 stockXwidth, stockYheight, stockZthickness, zeroheight, stockzero, retract\-height
\2 gcpfiles:
\3 basefilename, generatedxf, generategcode
\2 largesquaretool:
\3 large\_square\_tool\_num, toolradius, plunge, feed, speed
\1 Optional
\2 smallsquaretool:
\3small\_square\_tool\_num, small\_square\_ratio
\2 largeballtool:
\3 large\_ball\_tool\_num, large\_ball\_ratio
\2 largeVtool:
\3 large\_V\_tool\_num, large\_V\_ratio
\2 smallballtool:
\3 small\_ball\_tool\_num, small\_ball\_ratio
\2 smallVtool:
\3 small\_V\_tool\_num, small\_V\_ratio
\2 DTtool:
\3 DT\_tool\_num, DT\_ratio
\2 KHtool:
\3 KH\_tool\_num, KH\_ratio
\2 Roundovertool:
\3 Roundover\_tool\_num, RO\_ratio
\2 misctool:
\3 MISC\_tool\_num, MISC\_ratio
\end{outline}
The first class which is defined is \DescribeRoutine{gcodepreview} which includes an \verb|init| method which allows passing in the variables which will be used by the other methods in this class.
\lstset{firstnumber=\thegcpy}
\begin{writecode}{a}{gcodepreview.py}{python}
class gcodepreview:
def __init__(self, basefilename = "export",
generategcode = False,
generatedxf = False,
stockXwidth = 25,
stockYheight = 25,
stockZthickness = 1,
zeroheight = "Top",
stockzero = "Lower-left" ,
retractheight = 6,
currenttoolnum = 102,
toolradius = 3.175,
plunge = 100,
feed = 400,
speed = 10000):
self.basefilename = basefilename
self.generategcode = generategcode
self.generatedxf = generatedxf
self.stockXwidth = stockXwidth
self.stockYheight = stockYheight
self.stockZthickness = stockZthickness
self.zeroheight = zeroheight
self.stockzero = stockzero
self.retractheight = retractheight
self.currenttoolnum = currenttoolnum
self.toolradius = toolradius
self.plunge = plunge
self.feed = feed
self.speed = speed
# global toolpaths
# self.toolpaths = cylinder(1.5875, 12.7)
global generatedxfs
self.generatedxfs = False
\end{writecode}
\addtocounter{gcpy}{35}
%Note that it is best-practice to add the other global variables here to ensure that they are initialized:
%
%\lstset{firstnumber=\thegcpy}
%\begin{writecode}{a}{gcodepreview.py}{python}
% global mpx
% mpx = float(0)
% global mpy
% mpy = float(0)
% global mpz
% mpz = float(0)
% global tpz
% tpz = float(0)
% global currenttoolnum
% currenttoolnum = 102
% global stock
% self.stock = cube([25,25,25])
%
%\end{writecode}
%\addtocounter{gcpy}{1}
\subsubsection{Output files}
The \verb|gcodepreview| class will write out DXF and G-code files.
\paragraph{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/\allowbreak command instantiate a matching command so as to create a 3D model/preview of the file. One possible option would be to make specialized commands for movement which correspond to the various axis combinations (XYZ, XY, XZ, YZ, X, Y, Z).
\paragraph{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}
The class \verb|gcodepreview| will need additional commands for opening files:
\lstset{firstnumber=\thegcpy}
\begin{writecode}{a}{gcodepreview.py}{python}
def opengcodefile(self, basefilename = "export"):
if self.generategcode == True:
self.gcodefilename = basefilename + ".nc"
self.gc = open(self.gcodefilename, "w")
def opendxffile(self, basefilename = "export"):
global generatedxfs
if self.generatedxf == True:
self.generatedxfs = False
self.dxffilename = basefilename + ".dxf"
self.dxf = open(self.dxffilename, "w")
self.dxfpreamble(-1)
def opendxffiles(self, basefilename = "export",
large_square_tool_num = 0,
small_square_tool_num = 0,
large_ball_tool_num = 0,
small_ball_tool_num = 0,
large_V_tool_num = 0,
small_V_tool_num = 0,
DT_tool_num = 0,
KH_tool_num = 0,
Roundover_tool_num = 0,
MISC_tool_num = 0):
global generatedxfs
self.generatedxfs = True
self.large_square_tool_num = large_square_tool_num
self.small_square_tool_num = small_square_tool_num
self.large_ball_tool_num = large_ball_tool_num
self.small_ball_tool_num = small_ball_tool_num
self.large_V_tool_num = large_V_tool_num
self.small_V_tool_num = small_V_tool_num
self.DT_tool_num = DT_tool_num
self.KH_tool_num = KH_tool_num
self.Roundover_tool_num = Roundover_tool_num
self.MISC_tool_num = MISC_tool_num
if self.generatedxf == True:
if (large_square_tool_num > 0):
self.dxflgsqfilename = basefilename + str(large_square_tool_num) + ".dxf"
print("Opening ", str(self.dxflgsqfilename))
self.dxflgsq = open(self.dxflgsqfilename, "w")
self.dxfpreamble(large_square_tool_num)
if (small_square_tool_num > 0):
print("Opening small square")
self.dxfsmsqfilename = basefilename + str(small_square_tool_num) + ".dxf"
self.dxfsmsq = open(self.dxfsmsqfilename, "w")
self.dxfpreamble(small_square_tool_num)
if (large_ball_tool_num > 0):
print("Opening large ball")
self.dxflgblfilename = basefilename + str(large_ball_tool_num) + ".dxf"
self.dxflgbl = open(self.dxflgblfilename, "w")
self.dxfpreamble(large_ball_tool_num)
if (small_ball_tool_num > 0):
print("Opening small ball")
self.dxfsmblfilename = basefilename + str(small_ball_tool_num) + ".dxf"
self.dxfsmbl = open(self.dxfsmblfilename, "w")
self.dxfpreamble(small_ball_tool_num)
if (large_V_tool_num > 0):
print("Opening large V")
self.dxflgVfilename = basefilename + str(large_V_tool_num) + ".dxf"
self.dxflgV = open(self.dxflgVfilename, "w")
self.dxfpreamble(large_V_tool_num)
if (small_V_tool_num > 0):
print("Opening small V")
self.dxfsmVfilename = basefilename + str(small_V_tool_num) + ".dxf"
self.dxfsmV = open(self.dxfsmVfilename, "w")
self.dxfpreamble(small_V_tool_num)