WillAdams
(William Adams)
May 31, 2025, 6:04pm
1
There have been a few instances of this being used for modeling, but the default library expects a screen to be available, which doesn’t work in OpenPythonSCAD, so it will be necessary to re-implement.
We start with:
#!/usr/bin/env python
from openscad import *
and then need to consider the class and necessary variables…
WillAdams
(William Adams)
May 31, 2025, 6:18pm
2
Naturally, this will need to be a class, and variables will be needed for:
X/Y/Z position
current pointing direction for XY rotation
current inclination/declination of movement to allow for 3D
current/default object shape
which results in:
class threeDmodelturtle:
def __init__(self,
xpos = 0,
ypos = 0,
zpos = 0,
XYdir = 0,
Zdir = 0,
size = 5,
turtle = sphere
):
self.xpos = xpos
self.ypos = ypos
self.zpos = zpos
self.XYdir = XYdir
self.Zdir = Zdir
self.size = size
self.turtle = turtle
self.model = sphere(0.000000001)
Next, we need some commands…
WillAdams
(William Adams)
May 31, 2025, 8:59pm
3
But the need for the commands argues for:
import math
The directional commands are obvious (but had to be fixed/corrected and in addition, code added to adjust for exceeding 360):
def left(self, angle):
self.XYdir = self.XYdir + angle
if self.XYdir > 360 :
self.XYdir = self.XYdir - 360
def right(self, angle):
self.XYdir = self.XYdir + (360 - angle)
if self.XYdir < 360 :
self.XYdir = self.XYdir + 360
def incline(self, angle):
self.Zdir = self.Zdir - angle
def decline(self, angle):
self.Zdir = self.Zdir + angle
but forward(steps) demands trigonometry in three dimensions…
WillAdams
(William Adams)
May 31, 2025, 9:37pm
4
Probably, to prevent divide by zero errors, we should check for the cardinal directions… or, rather than do trigonometry, it should work just to move the turtle, then rotate it per the XYdir and Zdir angles…
Roughing things out in BlockSCAD we get:
so it should work to draw the steps as if all angles were zero, then rotate, then apply the distance transformation — which gets us back to trigonometry, unless the translated object can be queried for its position?!?
WillAdams
(William Adams)
May 31, 2025, 10:18pm
5
Starting out by handling the cardinal directions and then the interstices and their trigonometric calculations become obvious:
def forward(self, steps):
if self.turtle == sphere :
tortoise = sphere(self.size/2)
pastturtle = tortoise.translate([self.xpos, self.ypos, self.zpos])
xpast = self.xpos
ypast = self.ypos
zpast = self.zpos
dot = sphere(self.size/4)
if self.XYdir == 0 :
self.xpos = self.xpos + steps
if (self.XYdir > 0 and self.XYdir <90) :
self.xpos = self.xpos + math.cos(math.radians(self.XYdir)) * steps
self.ypos = self.ypos + math.sin(math.radians(self.XYdir)) * steps
if self.XYdir == 90 :
self.ypos = self.ypos + steps
if (self.XYdir > 90 and self.XYdir <180) :
self.xpos = self.xpos + math.sin(math.radians(90 - self.XYdir)) * steps
self.ypos = self.ypos + math.cos(math.radians(90 - self.XYdir)) * steps
if self.XYdir == 180 :
self.xpos = self.xpos - steps
if (self.XYdir > 180 and self.XYdir <270) :
self.xpos = self.xpos - math.cos(math.radians(180 - self.XYdir)) * steps
self.ypos = self.ypos + math.sin(math.radians(180 - self.XYdir)) * steps
if self.XYdir == 270 :
self.ypos = self.ypos - steps
if (self.XYdir > 270 and self.XYdir <360) :
self.xpos = self.xpos - math.sin(math.radians(270 - self.XYdir)) * steps
self.ypos = self.ypos - math.cos(math.radians(270 - self.XYdir)) * steps
futureturtle = tortoise.translate([self.xpos, self.ypos, self.zpos])
path = hull(dot.translate([xpast, ypast, zpast]), dot.translate([self.xpos, self.ypos, self.zpos]))
self.model = self.model.union(pastturtle, path, futureturtle)
def showmodel(self):
show(self.model)
WillAdams
(William Adams)
May 31, 2025, 10:30pm
6
in lieu of doing the 3D trigonometry, we just add commands for climb/descend:
def climb(self,steps):
if self.turtle == sphere :
tortoise = sphere(self.size/2)
zpast = self.zpos
pastturtle = tortoise.translate([self.xpos, self.ypos, self.zpos])
dot = sphere(self.size/4)
self.zpos = self.zpos + steps
futureturtle = tortoise.translate([self.xpos, self.ypos, self.zpos])
path = hull(dot.translate([self.xpos, self.ypos, zpast]), dot.translate([self.xpos, self.ypos, self.zpos]))
self.model = self.model.union(pastturtle, path, futureturtle)
def descend(self,steps):
if self.turtle == sphere :
tortoise = sphere(self.size/2)
zpast = self.zpos
pastturtle = tortoise.translate([self.xpos, self.ypos, self.zpos])
dot = sphere(self.size/4)
self.zpos = self.zpos - steps
futureturtle = tortoise.translate([self.xpos, self.ypos, self.zpos])
path = hull(dot.translate([self.xpos, self.ypos, zpast]), dot.translate([self.xpos, self.ypos, self.zpos]))
self.model = self.model.union(pastturtle, path, futureturtle)
which lets us move in three dimensions at right angles:
Files pushed up to:
#!/usr/bin/env python
import math
from openscad import *
class threeDmodelturtle:
def __init__(self,
xpos = 0,
ypos = 0,
zpos = 0,
XYdir = 0,
Zdir = 0,
size = 5,
turtle = sphere
):
self.xpos = xpos
self.ypos = ypos
self.zpos = zpos
This file has been truncated. show original
Since this was first pushed up, the trigonometry for angles in XY has been added.
1 Like
dougl
(Doug L)
June 1, 2025, 6:26pm
7
That’s pretty cool. I think I would have started with just moving in 2D like the typical Turtle programming movement then added Z. But I get you’ve had your head in 3 dimensions in this space for awhile.
Keep up the good work!
1 Like
WillAdams
(William Adams)
June 2, 2025, 1:56pm
8
I bailed on doing more than up/down in Z — the math is at least moderately complex — there’s a bit of discussion about vector math on the subreddit for openpythonscad — maybe that will yield a more useful approach.
1 Like
WillAdams
(William Adams)
June 5, 2025, 2:41pm
9
Turns out this was much simpler than I thought — since the XY position is calculated, the Z is just one additional trigonometric calculation:
z = zpos + steps * Sin(zdir)
Note that the new OpenPythonSCAD adds trigonometric functions:
hence the “Sin()” (as opposed to math.sin(math.radians(zdir)) or something along those lines.
That this will result in the proper position is easily visualized by drawing a cone defined by the length and height and angle with the result that the radius coincides with the XY position.
1 Like
WillAdams
(William Adams)
June 7, 2025, 5:43pm
10
New version up at:
#!/usr/bin/env python
import math
from openscad import *
class threeDmodelturtle:
def __init__(self,
size = 5,
turtle = sphere,
xpos = 0,
ypos = 0,
zpos = 0,
XYdir = 0,
Zdir = 0
):
self.xpos = xpos
self.ypos = ypos
self.zpos = zpos
This file has been truncated. show original
and the code is sufficiently obvious I won’t belabour it.
The next thing to look into is if this could be paired w/
so that OpenPythonSCAD could function as a direct and immediate/interactive 3D previewer.
1 Like