In [1]:
import numpy as np
from numpy import radians as rad
import k3d
In [2]:
FIL_D = 1.75
NOZ_D = 0.4
RATE = NOZ_D**2 / FIL_D**2
WIDTH = DEPTH = HEIGHT = 300
In [3]:
class PrintTurtle:
def __init__(self):
self.moves = []
self.lines = []
self.blobs = []
self.gcode = []
self.extrude = False
self.position = np.array([0, 0, 0])
self.rotation = np.identity(4)
self.rot_unit = np.array([0, 1, 0, 0])
self.rate = 1.0
def note(self, msg):
self.gcode.append(f'; {msg}')
def blob(self, amount=5.0, speed=50):
self.gcode.append(f'G1 E{amount * RATE:.2f} F{speed}')
self.blobs.append(self.position.copy())
def goto(self, point, speed):
move = point - self.position
dist = np.linalg.norm(move)
loc = 'X{:.2f} Y{:.2f} Z{:.2f}'.format(*move)
line = [self.position.tolist(), point.tolist(), (np.nan, np.nan, np.nan)]
if self.extrude:
self.gcode.append(f'G1 {loc} E{self.rate * RATE * dist:.2f} F{speed}')
self.lines.extend(line)
else:
self.gcode.append(f'G0 {loc} F{speed}')
self.moves.extend(line)
self.position = point
def forward(self, dist, speed=1500):
point = self.position + dist * (self.rotation @ self.rot_unit)[:3]
self.goto(point, speed)
def turn(self, x=0.0, y=0.0, z=0.0):
rot_x = np.array([[1, 0, 0, 0],
[0, np.cos(x), -np.sin(x), 0],
[0, np.sin(x), np.cos(x), 0],
[0, 0, 0, 1]])
rot_y = np.array([[np.cos(y), 0, np.sin(y), 0],
[0, 1, 0, 0],
[-np.sin(y), 0, np.cos(y), 0],
[0, 0, 0, 1]])
rot_z = np.array([[np.cos(z), -np.sin(z), 0, 0],
[np.sin(z), np.cos(z), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]])
self.rotation = self.rotation @ rot_x @ rot_y @ rot_z
def pendown(self, rate=None):
self.extrude = True
if rate:
self.rate = rate
def penup(self):
self.extrude = False
def bbox(self):
points = np.vstack([*self.lines, *self.moves, *self.blobs])
low = [np.nanmin(points[:,0]), np.nanmin(points[:,1]), np.nanmin(points[:,2])]
high = [np.nanmax(points[:,0]), np.nanmax(points[:,1]), np.nanmax(points[:,2])]
return (low, high)
def save(self, path):
bbox = self.bbox()
width = bbox[1][0] - bbox[0][0]
depth = bbox[1][1] - bbox[0][1]
x0 = WIDTH/2. - bbox[0][0] - width/2.
y0 = DEPTH/2. - bbox[0][1] - depth/2.
head = ('; start_print BED_TEMP=60.0 EXTRUDER_TEMP=215.0\n'
'M106 S255 ; turn on part fan\n'
'M140 S50 ; Start bed heating\n'
'M109 S215 ; Set and wait for nozzle to reach temperature\n'
'M190 S50 ; Wait for bed to reach temperature\n'
'G90 ; absolute xyz positioning\n'
'G28 ; home\n'
f'G0 X{x0:.2f} Y{y0:.2f} Z0.3 F1500 ; move to center of print area\n'
'G91 ; relative xyz positioning\n')
tail = 'end_print'
with open(path, 'wt+') as file:
file.write(f'{head}\n{'\n'.join(self.gcode)}\n{tail}')
def show(self):
plot = k3d.plot()
if self.lines:
lines = np.vstack(self.lines)
indices = list((x, x+1) for x in range(0, len(lines), 2))
plot += k3d.lines(lines, indices, width=NOZ_D/2, color=0xff0000)
if self.moves:
moves = np.vstack(self.moves)
indices = list((x, x+1) for x in range(0, len(moves), 2))
plot += k3d.lines(moves, indices, width=NOZ_D/4, color=0x00ff00)
if self.blobs:
blobs = np.vstack(self.blobs)
plot += k3d.points(blobs, point_size=4 * NOZ_D, color=0x0000ff)
plot.display()
Bumpy Vase¶
In [4]:
rng = np.random.default_rng()
turtle = PrintTurtle()
base = 15
for i in range(150):
turtle.note(f'LEVEL {i}')
coeff = 10 + 20 * np.sin(np.pi*i/150 + np.pi/8)
xp = [coeff*np.sin(2*np.pi*x/360) for x in range(360)]
yp = [coeff*np.cos(2*np.pi*x/360) for x in range(360)]
turtle.pendown(3)
for (x, y) in zip(xp, yp):
turtle.goto(np.array([x, y, turtle.position[-1]]), 1500)
if rng.random() > 0.90:
size = rng.random() * coeff
turtle.blob(size)
turtle.penup()
# offset = 2 * (rng.random(2) - 0.5)
turtle.goto(turtle.position + [0, 0, 1.5*NOZ_D], 1500)
turtle.show()
turtle.save('vase.gcode')
/home/louis/.local/lib/python3.12/site-packages/traittypes/traittypes.py:97: UserWarning: Given trait value dtype "float64" does not match required type "float32". A coerced copy has been created. warnings.warn(
Output()
Tube¶
In [5]:
turtle = PrintTurtle()
for z in range(100):
for i in range(60):
if (z + i) % 2 == 0:
turtle.blob(10)
turtle.forward(2)
turtle.turn(0, 0, rad(6))
turtle.goto(turtle.position + [0, 0, 2*NOZ_D], 1500)
turtle.show()
turtle.save('tube.gcode')
Output()
Cone¶
In [6]:
turtle = PrintTurtle()
turtle.pendown(3)
height = 150
for i in range(height):
turtle.note(f'LEVEL {i}')
for w in range(3):
turtle.forward(30 - ((i + (w/3)) / (height / 30)), 5000)
turtle.turn(0, 0, rad(120 + 2*i/height))
turtle.blob(15)
turtle.turn(rad(90), 0, 0)
turtle.forward(2*NOZ_D, 3000)
turtle.turn(-rad(90), 0, 0)
#turtle.turn(0, 0, rad(0.5))
turtle.pendown(1.5)
turtle.show()
turtle.save('cone.gcode')
Output()
In [ ]: