How to measure/troubleshoot memory nesting/memory usage?

75 views
Skip to first unread message

William Adams

unread,
Jun 9, 2025, 7:04:15 PMJun 9
to PythonSCAD
I have a quite small test G-code file:

square_20mmX20mmX12_5mm_002pass.nc

(attached)

which works as expected with my library:

Screenshot 2025-06-09 185939.png

using the code:

import sys

from openscad import *
nimport("https://raw.githubusercontent.com/WillAdams/gcodepreview/refs/heads/main/gcodepreview.py")

gc_file = "C:\\Users\\willa\\OneDrive\\Desktop\\square_20mmX20mmX12_5mm_002pass.nc"

gcp = gcodepreview(True, False, False)

gcp.previewgcodefile(gc_file)

print(sys.getsizeof(gcp.toolpaths))

However, it crashes when I try to run a version of the same G-code which instead of 2 passes, makes 100:

square_20mmX20mmX12_5mm_100pass.nc

(also attached)

While I am planning on implementing an optimization where if multiple cuts are made only the one deepest cut is used (since cuts above that are redundant on a 3-axis machine) not being able to handle a 30KB G-code file has stymied me.

William

square_20mmX20mmX12_5mm_100pass.nc
square_20mmX20mmX12_5mm_002pass.nc

W W

unread,
Jun 10, 2025, 1:05:56 AMJun 10
to PythonSCAD
+1. Ability to capture a heap dump and stack trace would be valuable for filing bugs!

Currently, I dont see any logging when crash happens.

Is the complication from cross compiling? I know for Windows at least, there's some built-in API for capturing heap dumps on crashes.

Guenther Sohler

unread,
Jun 10, 2025, 4:01:39 AMJun 10
to W W, PythonSCAD
Yes, debugging errors in openscad/pythonsscad built for windows(MXE) is quite cumbersome/almost impossible.
Debugging is possible by using gdb inside MINGW32. This can debug Windows builds. but this is rather limited.
It's way easier for linux.

OpenSCAD/PythonSCAD can catch an exception thrown, but not yet a segfault.

IT would be indeed a valuable addition, but I have no clue how to start that.



--
You received this message because you are subscribed to the Google Groups "PythonSCAD" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pythonscad+...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/pythonscad/f5f5ae2f-d55e-4312-8ca0-3e38a3fa97bcn%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Guenther Sohler

unread,
Jun 10, 2025, 4:17:58 AMJun 10
to W W, PythonSCAD
Luckily, I can see the crash in Linux, too!
Its a heap-stack crash coming from
a "recursive" call all of NodeVisitor::traverse 2800 calls deep.

This is the very method, how OpenSCAD works and I doubt increasing the stack space
is an option here, because you gain 5% more ...
Of course we can improve here by displaying at least an Error message instead of a crash.

William you can still greatly improve gcodepreview by  creating the csg tree in another way.
CSG-Tree is, what pythonscad understood to render. Try to make an union of 2800 items instead of 2800 nested hierarchies

You can display the CSG tree in 
Design -> Display CSG tree.

Hope that helps.


On Tue, Jun 10, 2025 at 7:05 AM W W <willy...@gmail.com> wrote:

William Adams

unread,
Jun 10, 2025, 7:14:33 AMJun 10
to PythonSCAD
Ouch.

Why am I reminded of:


The top answer should be by Drew Lanza, and to save folks from quora.com, please find the entire post below:

I took the multi quarter sequence from Professor Donald Knuth in the 1970s. It was a mind blowing experience.

I still see him at Zott's every now and then on a warm day. He remains as friendly, funny, gracious and, dare I say goofy looking (tall and Scandinavian), as ever.

Taking the class was surreal. Many of us were undergrads who were struggling to keep up. This class was hard. Some in the class were Ph.D. Students. A really diverse mix. Knuth was awesome. He would give us undergrads something to chew on. Then he would say he was going off on a deep tangent for the PhDs in the audience and for the rest of us not to worry. Rather than scary, we all found that inspiring.

Some funny stories (forgive me if the details are hazy - it was almost 40 years ago):

Occasionally Knuth would bring in a PhD thesis from another university. He would tell us there was a math error in it. Extra credit if you could find the error. We left those for the PhDs in our cohort.

If you know Knuth's books, then you know that he's got some really hard problems at the end of the sections. It was not unusual for him to turn to the undergrads and say, "If you find this area interesting and you'd like to solve this problem, come see me - there might be a PhD thesis in there somewhere." His door was always open. I don't think he was kidding about the thesis thing.

One of my proudest memories of Stanford University was when I won one of Knuth's weekly contests. Often the homework/contest was a program to be written in MIX.

One week the assigment was to read in an input deck that described a Crossword Puzzle and then to print out the puzzle. The challenge was to do it in the fewest instructions possible. I stayed up all night working on it.

I vividly remember that day. He would announce the winners at the end of class.

"This week was rather unusual", he started out. "Third place goes to John Smith whose program was fifty instructions. Second place goes to Jane Doe with forty five." John and Jane were PhD candidates - the winners invariably were.

I was excited! I knew mine was shorter than that. Had an undergrad finally 'scored one for the team'?

Knuth continued. "I am reluctant to award first place." He looked up and smiled. He sighed. "The winning program was half the length of the nearest runner up."

So what's the problem?

"When I first ran the program it seemed to be stuck in an infinite loop. The mainframe aborted the job after the default 1 second of CPU time. So I ran it again but gave it 3 seconds. It failed to complete. I looked at the code. I couldn't make heads or tails of it. But this student has always turned in his work on time. So I took a chance on him and set the CPU time to 30 seconds."

Oh God! I'd run it with a small input deck because CPU time was ridiculously expensive and we all had miniscule budgets to use. Had that test deck failed to unearth an infinite loop bug?

No. Wait. Knuth had said first place. What's up?

He sighed again. "It took over 20 seconds of CPU time, Mr. Lanza, more than $100 worth. Please don't do that again. But it did work and it was the shortest program so Mr. Lanza wins this week. But never again, ok?"

I was mortified. It was as if he was telling me I had cheated. Everyone certainly was looking at me that way. I slumped deep into my seat.

We met up after class. I was terrified. Hell, petrified. Here was one of the smartest men in the world and a guy I really looked up to.

But Knuth's a nice guy. He laughed. "You took this assignment a little too literally. That program should've executed in linear time. Yours was exponential. Did you know that?"

"To be honest, Professor, you said the fewest instructions. I didn't stop to think about run time."

"Fair enough I suppose. But never again, ok? I couldn't make heads or tails out of your code. How did your algorithm work?"

"Well, Professor, I took McCarthy's Lisp class last quarter. To be honest, I had a heck of a time with recursion. At about 2am, though, it struck me to treat this crossword problem like a homework assignment I'd had from McCarthy. It just starts by the first square of the crossword puzzle asking its neighbor squares if they know what to do. Then each neighbor square does the same thing recursively. Eventually the boundary squares figure out that they're on the edge and they report that back."

His eyes opened wide. He laughed and laughed. "But that meant you had to read in the entire deck that described the crossword puzzle for each and every one of those recursions looking for the boundary conditions. No wonder it took so long."

"Well, Professor, it seemed to me that the best way to get to the fewest instructions was just to have all of the boxes in the crossword puzzle talk to each other and let them sort it out. That's what the Lisp problem we had to solve was all about."

He looked at me in a fatherly way. "I'll mention this to John [McCarthy] the next time I see him. I'm sure he'll get a laugh. By the way, what grade did you get in his Lisp class?"

"A C-. I never did get recursion."

Knuth smiled at me and said, "Oh, I wouldn't say that." He walked away chuckling to himself.

W W

unread,
Jun 10, 2025, 11:40:50 AMJun 10
to PythonSCAD
The ending cracks me up.

I had to learn programming on a lisp variant.

I still have occasional nightmares in trying to find the mismatch parenthesis on a large code file.

Thankfully they stopped doing this for intro courses about ten years ago.

Guenther Sohler

unread,
Jun 10, 2025, 11:48:57 AMJun 10
to W W, PythonSCAD
you are wrong.  ...
My trade biz is creating microchips and the software which is called cadence uses skill as its language.
Its a lisp derivative and its good style to put a comment on the closing bracket, where it matches to.


W W

unread,
Jun 10, 2025, 5:36:02 PMJun 10
to PythonSCAD
Interesting a lisp derivative is chosen. It looks somewhat mainstream in the chip world actually. Today I learned :)

Functional programming is rewarding, but sadly not as common anymore in the general programming world.

As a result, I believe lisp or any other functional languages are a difficult choice to teach computer science with.

William Adams

unread,
Jun 10, 2025, 9:20:09 PMJun 10
to PythonSCAD
I dunno, using lisp for didactive purposes seemed to work for:



Though I'm not quite sure what to make of the token graphic designer in my comparative programming languages class (me) being the only person to get all the Lisp problems done (and correct).

William

W W

unread,
Jun 10, 2025, 9:26:19 PMJun 10
to PythonSCAD
They have switched from Scheme to Python circa 2009 :)

William Adams

unread,
Jun 10, 2025, 9:33:51 PMJun 10
to PythonSCAD
Getting back on topic, I found:


but checking at:


I don't see a "children" command --- how does one close one level of a group while leaving the previous level open so that the union will be implicit?

William


On Tuesday, June 10, 2025 at 4:17:58 AM UTC-4 guenthe...@gmail.com wrote:

William Adams

unread,
Jun 12, 2025, 8:50:50 AMJun 12
to PythonSCAD
I've been experimenting with every variation I can think of, and AFAICT, every time one adds an element to a toolpath or other variable using union, then one gets:

union( r = 0 , fn = 1 ) {

...

}


wrapping things up, and goes one level deeper.


Is there a command/option/mechanism to do an implicit union?


As noted, I couldn't find anything at:


raw.githubusercontent.com/pythonscad/pythonscad/refs/heads/master/libraries/python/openscad.pyi


which seemed to address this.


Is there some command/mechanism for directly manipulating the CSG tree? If so, would that be a valid approach?


BTW, there's a typo in the above:

    def nimport(url:str) -> PyOpenSCAD:
        """Same as python inport , but with an URL instead

import.

William

Guenther Sohler

unread,
Jun 12, 2025, 9:47:31 AMJun 12
to William Adams, PythonSCAD
William,

I did not study your code yet carefully enough, but I suspect that you repeatedly do:

woodpiece = woodpiece - track_of_mill
woodpiece -= track_of_another_mill

key to not pollute the stack/build hierarchy is changing overall concept.

You could dramatically improve stack usage by collecting all tracks created by a single GCODE (Arc) command like that

tracks = []

tracks.append( maybe_cylindric_shape)
tracks.append( maybe_cylindric_shape)
tracks.append( maybe_cylindric_shape)
tracks.append( maybe_cylindric_shape)

#note that no CSG is performed up to now and no deep tree is built for that

#finally make one tree hierarchy
woodpiece = difference(woodpiece, tracks) # here implicit union of python listrs  is applied

Hope that helps






William Adams

unread,
Jun 12, 2025, 11:30:35 AMJun 12
to PythonSCAD
Thanks!

So, just to make sure I understand the terminology/methodology:

[] == empty list (this from Python itself)

<foo>.append(bar) (Python command to add to list)

This seems to be working:

    from openscad import *
    fn=20
    
    box = cube([40,40,40])
    
    features = []
    
    features.append(cube([36,36,40]) + [2,2,2])
    features.append(cylinder(d=20,h=5) + [20,20,-1])
    features.append(cylinder(d=3,h=10) ^ [[5,35],[5,35], -1])
    
    part = difference(box, features)
    
    show(part)

which makes sense to me, and results in a far flatter CSG tree, so I guess it's time for yet another re-write (which I'd been needing to do anyway) --- this should get me very close to a final form, which thinking out loud will be:

 - manage all tool/shapes as lists
 - manage all toolpaths as lists of lists
 - stock, since it is one or maybe two pieces can just be primitive objects
 - subtract toolpaths (lists of lists) from stock (primitive objects)

Will experiment with that and report back presently.

William

William Adams

unread,
Jun 17, 2025, 11:40:25 PMJun 17
to PythonSCAD
Not sure what is happening here:

I have defined:

        def toolmovement(self, bx, by, bz, ex, ey, ez):
            tslist = []
            if self.endmilltype == "O-flute":
                ts = cylinder(r1=(self.diameter / 2), r2=(self.diameter / 2), h=self.flute, center = False)
                tslist.append(hull(ts.translate([bx, by, bz]), ts.translate([ex, ey, ez])))
                return tslist

and then w/ the variable set correctly called it as:

s = t.toolmovement(0,0,0,1,2,3)
show(s)

but I get:

ERROR: Traceback (most recent call last):
  File "<string>", line 806, in <module>
TypeError: Invalid type for Object in show

If I print the type of s I get:

<class 'NoneType'>


which is confusing.


So I guess the question is, how does one manipulate a list filled with 3D shapes in a class and return it so that it can be differenced and shown?


William


W W

unread,
Jun 17, 2025, 11:45:28 PMJun 17
to PythonSCAD
What does it say when you print the type for s?

print(type(s))

Add it right before the show(s) line.

If it is a list of OpenScad objects, it **should** work as expected.

If the list contains any object that isn't a OpenScad object, then that's probably your culprit.

If you are getting None from  toolmovement(), it could be due to the if block not entering, and the execution "drops off" which defaults to None.

William Adams

unread,
Jun 18, 2025, 6:46:04 AMJun 18
to PythonSCAD
As noted:

>If I print the type of s I get:
>

><class 'NoneType'>


However, you were on the right track --- I had the return in the wrong place so that it wasn't getting done in the if block, so once again, it's a stupid error on my part --- in my defense, it took a lot of code to get to this state, and it was late last night, and I'm working in a narrow text window.... (all of which is why I usually develop this sort of thing in BlockSCAD).

Hopefully, that will be the final hurdle for this and I can get this working again....

William

W W

unread,
Jun 18, 2025, 5:59:39 PMJun 18
to PythonSCAD
Oh ha, I totally misread the last part of your original email.

At least my second suggestion nailed it :)

Python is more loosey goosey on typing, so the return type enforcement feels whacky if you are not used to it.

-W

William Adams

unread,
Jul 12, 2025, 8:45:09 AMJul 12
to PythonSCAD
Okay, thanks to your patient instruction we now have a file:  gcodepreview/templates/gcpgc.py at main · WillAdams/gcodepreview

    import sys
    
    fn = 180
    
    from openscad import *
    #nimport("https://raw.githubusercontent.com/WillAdams/gcodepreview/refs/heads/main/gcodepreview.py")
    
    try:
        if 'gcodepreview' in sys.modules:
            del sys.modules['gcodepreview']
    except AttributeError:
        pass
    
    from gcodepreview import *
    
    gc_file = "C:\\Users\\willa\\OneDrive\\Desktop\\19mm_1_32_depth.nc"
    
    gcp = gcodepreview(False, False)
    
    gcp.previewgcodefile(gc_file)
    
    #print(sys.getsizeof(gcp.toolpaths))

which works:

gcpgc.png

where the file is: (see attached) and we are loading the current version of gcodepreview.py: https://github.com/WillAdams/gcodepreview/blob/main/gcodepreview.py (also attached).

However, if one doubles down again, halving the depth per pass and roughly doubling the number of line from 7,156 to 14,069 in the also attached 19mm_1_64_depth.nc then the process runs for a while and crashes.

A quick check of my Downloads folder reveals a G-code file with a length of 1,256,873 lines --- what should one reasonably expect this program to be able to directly process?

One obvious approach is looking into some sort of hashing to identify redundant tool movements and not process them --- but first I need to make tools and tool handling more general.

William

On Thursday, June 12, 2025 at 9:47:31 AM UTC-4 guenthe...@gmail.com wrote:
19mm_1_64_depth.nc
19mm_1_32_depth.nc
gcodepreview.py

William Adams

unread,
Jul 15, 2025, 7:36:32 AMJul 15
to PythonSCAD
Okay, this response on github may be of interest:


> I might recommend calling NumVerts() occasionally to force it to evaluate and return memory.

a couple of things were mentioned which I was planning to add as an optimization (only processing the deepest cut, simplifying the tool shape) and for the latter the comment was:

> I'd also recommend keeping the tool mesh and cylinder paths very low res - you can always smooth out the visual later with CalculateNormals or even SmoothOut.

Would it be possible to provide access to these commands?

I'm having a problem where a 4th arc cut when called from OpenSCAD is not processed, but the equivalent PythonSCAD code is fine.

William

Guenther Sohler

unread,
Jul 15, 2025, 12:48:45 PMJul 15
to William Adams, PythonSCAD
This is a very interesting discussion actually. Nice seeing that you already collected quite many replies.
Long story short: Yes, calling  NumVerts() can help, but issue is that manifold processing starts only when your complete python program has finished, so its useless to make this NumVertes accessible to you. It should go to my tasklist.

And sorry, I have been supporting with gcodepreview only from the side until now. But when I realize that you sometimes have 100.000 solids to union, I come to the conclusion, that you mill an arc into the wood milling many supersmall pieces into the wood which finally look like a complete arc.

Do you mill G1 into wood like this :


from openscad import *

wood = cube([100,100,10])

mill=cylinder(r=3,h=10)
G0_cutout = hull(
                mill.translate([20,20,5]),
                mill.translate([70,50,5])
                )

ang1=90
ang2=200
r=30
G3_center =[50,50,5]                
G3_start = translate(G3_center, [r*Cos(ang1),r*Sin(ang1),0])
G3_end = translate(G3_center, [r*Cos(ang2),r*Sin(ang2),0])

G3_cutout = union(
                mill.translate(G3_start),
                mill.translate(G3_end),
                square([2*3, 10]).translate([r-3,0,0]).rotate_extrude(angle=ang2-ang1).rotz(ang1) + G3_center
              )  
cutouts = [G0_cutout, G3_cutout]                
wood -= cutouts                
wood.show()

Especially the arc is super efficient as it only consists of 3 elements and its scalable, just fn definies its precision
Yes, I am aware, that there are way more different mills than just stupid cylindrical flush mill, but i believe all ot them can be implemented with small modifications.
If you further optimize your gcode program by only cutting the deepest milling curves you very likely get the same result at a fraction of the processing power. but yes, its effort to code that.

What do you think ?



William Adams

unread,
Jul 15, 2025, 10:10:08 PMJul 15
to PythonSCAD
Funny you should ask....

Let's trade code --- I'll take yours and see if I can get it to work, and if you could take a moment to look at the following...

First, every so often when loading/working with these files I get the message:

>ERROR:   File "<string>", line 1
>    //!OpenSCAD
>    ^^
>SyntaxError: invalid syntax

(which I'm baffled by)

I have the file gcodereview.py (a much shortened version of the full gcodepreview.py which I've been using for testing/debugging):

import sys
import math

from openscad import *

class gcodereview:

    def __init__(self):
        self.mc = "Initialized"
        self.mpx = float(0)
        self.mpy = float(0)
        self.mpz = float(0)
        self.tpz = float(0)
        self.currenttoolnum = 102
       
    def xpos(self):
        return self.mpx

    def ypos(self):
        return self.mpy

    def zpos(self):
        return self.mpz

    def setupstock(self, stockXwidth,
                 stockYheight,
                 stockZthickness,
                 zeroheight,
                 stockzero,
                 retractheight):
        self.toolpaths = []
        stock = cube([stockXwidth, stockYheight, stockZthickness])            
        self.stock = stock.translate([0,0,-stockZthickness])

    def rapid(self, ex, ey, ez):
        self.mpx = ex
        self.mpy = ey
        self.mpz = ez

    def xpos(self):
        return self.mpx

    def ypos(self):
        return self.mpy

    def zpos(self):
        return self.mpz
       
    def setxpos(self, ex):
        self.mpx = ex

    def setypos(self, ey):
        self.mpy = ey

    def setzpos(self, ez):
        self.mpz = ez
       
    def cutline(self, ex, ey, ez):
        tool = cylinder(d = 1.5875, h = 12)
        tpb = tool.translate([self.xpos(), self.ypos(), self.zpos()])
        tpe = tool.translate([ex, ey, ez])
        toolpath = hull(tpb, tpe)
        self.mpx = ex
        self.mpy = ey
        self.mpz = ez
        self.toolpaths.append(toolpath)

    def cutquarterCCNE(self, ex, ey, ez, radius):
        if self.zpos() == ez:
            tpzinc = 0
        else:
            tpzinc = (ez - self.zpos()) / 90
        i = 1
        while i < 91:
            self.cutline(ex + radius * Cos(i), ey - radius + radius * Sin(i), self.zpos()+tpzinc)
            i += 1

    def cutquarterCCNW(self, ex, ey, ez, radius):
        if self.zpos() == ez:
            tpzinc = 0
        else:
            tpzinc = (ez - self.zpos()) / 90
        i = 91
        while i < 181:
            self.cutline(ex + radius + radius * Cos(i), ey + radius * Sin(i), self.zpos()+tpzinc)
            i += 1

    def cutquarterCCSW(self, ex, ey, ez, radius):
        if self.zpos() == ez:
            tpzinc = 0
        else:
            tpzinc = (ez - self.zpos()) / 90
        i = 181
        while i < 271:
            self.cutline(ex + radius * Cos(i), ey + radius + radius * Sin(i), self.zpos()+tpzinc)
            i += 1

    def cutquarterCCSE(self, ex, ey, ez, radius):
        if self.zpos() == ez:
            tpzinc = 0
        else:
            tpzinc = (ez - self.zpos()) / 90
        i = 271
        while i < 361:
            self.cutline(ex - radius + radius * Cos(i), ey + radius * Sin(i), self.zpos()+tpzinc)
            i += 1

    def stockandtoolpaths(self):
        part = self.stock.difference(self.toolpaths)
        show(part)

    def returnstockandtoolpaths(self):
        part = self.stock.difference(self.toolpaths)
        return part

    def showstock(self):
        return self.stock

(attached)

when called by a Python file, it works as expected:

#!/usr/bin/env python

import sys

try:
    if 'gcodereview' in sys.modules:
        del sys.modules['gcodereview']
except AttributeError:
    pass

from gcodereview import *

fa = 2
fs = 0.125

# [Stock] */
stockXwidth = 220
# [Stock] */
stockYheight = 150
# [Stock] */
stockZthickness = 8.35

gcp = gcodereview()

gcp.setupstock(stockXwidth,stockYheight,stockZthickness,"Top","Center",9)

gcp.cutline(stockXwidth, stockYheight, -stockZthickness)

gcp.rapid(stockXwidth/2, stockYheight/2, 0)

gcp.cutquarterCCNE(gcp.xpos() - stockYheight/8, gcp.ypos() + stockYheight/8, -stockZthickness/4, stockYheight/8)
gcp.cutquarterCCNW(gcp.xpos() - stockYheight/8, gcp.ypos() - stockYheight/8, -stockZthickness/2, stockYheight/8)
gcp.cutquarterCCSW(gcp.xpos() + stockYheight/8, gcp.ypos() - stockYheight/8, -stockZthickness * 0.75, stockYheight/8)
gcp.cutquarterCCSE(gcp.xpos() + stockYheight/8, gcp.ypos() + stockYheight/8, -stockZthickness, stockYheight/8)

gcp.stockandtoolpaths()

(attached)

as shown in the exported pixel image:

gcodereviewtemplate.png

Wrapping this up in OpenSCAD code is pretty straight-forward:

//!OpenSCAD

use <gcodereview.py>

module setupstock(stockXwidth, stockYheight, stockZthickness, zeroheight, stockzero, retractheight) {
    gcp.setupstock(stockXwidth, stockYheight, stockZthickness, zeroheight, stockzero, retractheight);
}

module rapid(ex, ey, ez){
    gcp.rapid(ex,ey,ez);
}
       
function xpos() = gcp.xpos();

function ypos() = gcp.ypos();

function zpos() = gcp.zpos();

module cutline(ex, ey, ez){
    gcp.cutline(ex, ey, ez);
}

module cutquarterCCNE(ex, ey, ez, radius){
     gcp.cutquarterCCNE(ex, ey, ez, radius);
}

module cutquarterCCNW(ex, ey, ez, radius){
     gcp.cutquarterCCNW(ex, ey, ez, radius);
}

module cutquarterCCSW(ex, ey, ez, radius){
    gcp.cutquarterCCSW(ex, ey, ez, radius);
}

module cutquarterCCSE(self, ex, ey, ez, radius){
    gcp.cutquarterCCSE(ex, ey, ez, radius);
}

module cutquarterCCNEdxf(ex, ey, ez, radius){
     gcp.cutquarterCCNEdxf(ex, ey, ez, radius);
}

module stockandtoolpaths(){
    gcp.stockandtoolpaths();
}

module showstockandtoolpaths(){
    gcp.returnstockandtoolpaths();
}

module showstock(){
    gcp.showstock();
}

(also attached)

but when we make a .scad file to recreate the Python template:

//!OpenSCAD

use <gcodereview.py>
include <gcodereview.scad>

$fa = 2;
$fs = 0.125;

/* [Stock] */
stockXwidth = 219;
/* [Stock] */
stockYheight = 150;
/* [Stock] */
stockZthickness = 8.35;
/* [Stock] */

gcp = gcodereview();

setupstock(stockXwidth, stockYheight, stockZthickness, "Top", "Center",0);

cutline(stockXwidth, stockYheight, -stockZthickness);

rapid(stockXwidth/2, stockYheight/2, 0);

cutquarterCCNE(xpos() - stockYheight/8, ypos() + stockYheight/8, -stockZthickness/4, stockYheight/8);
cutquarterCCNW(xpos() - stockYheight/8, ypos() - stockYheight/8, -stockZthickness/2, stockYheight/8);
cutquarterCCSW(xpos() + stockYheight/8, ypos() - stockYheight/8, -stockZthickness * 0.75, stockYheight/8);
cutquarterCCSE(xpos() + stockYheight/8, ypos() + stockYheight/8, -stockZthickness, stockYheight/8);

showstockandtoolpaths();

(also attached)

one instead gets:

>Parsing design (AST generation)...
>Compiling design (CSG Tree generation)...
>WARNING: Python: 'TypeError("unsupported operand type(s) for -: 'float' and 'NoneType'") in line -1' in file ../../OpenSCAD/libraries/gcodereview.scad, line 36
>WARNING: Python: 'SystemError('<built-in method difference of PyOpenSCAD object at 0x0000017447FAD110> returned a result with an exception set') in line -1' in file ../../OpenSCAD/libraries/gcodereview.scad, line 48
>Rendering Polygon Mesh using Manifold...
>WARNING: No top level geometry to render

which I'm kind of mystified by, since my file doesn't have a line # -1.

William
gcodereview.scad
gcodereviewtemplate.scad
gcodereview.py
gcodereviewtemplate.py

William Adams

unread,
Jul 15, 2025, 10:26:18 PMJul 15
to PythonSCAD
Looking at your code, it will work for "normal" tooling such as square and ball-nose and bowl and V and tapered ball-nose tools --- it won't work as-is for round-over tools since a hull operation on the entire tool converts it into a flat-bottomed V.

For such tooling I've been slicing it into a series of truncated cones which describe a slice of the shape in question.

I believe your code could be used for handling such slices --- I'll have to see what I can puzzle out.

William

Guenther Sohler

unread,
Jul 16, 2025, 2:07:47 AMJul 16
to William Adams, PythonSCAD
Great that my code is useful to you. I am sure you can get another performance boost when using it.
Even if you cannot use it for all tooling, even using it conditionally, it's an improvement.
Yes, hull will kill ability to use all tooling, but it was just shortcut for sake of readability.

instead you should position to P1, calculate correct angle with Atan2 and place linear_extrude there so it exactly shoot/target towards2 P2.  Yes, even more math

For future of gvode preview
you could map 
each vertex of your tool path to a cylinder/truncated cone
G0      to linear_extrude
G3     to rotate_extrude  (even if z changes during toolpath you can use helical gear/screw feature)



--
You received this message because you are subscribed to the Google Groups "PythonSCAD" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pythonscad+...@googlegroups.com.
Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
0 new messages