kopia lustrzana https://github.com/fellesverkstedet/fabmodules
497 wiersze
16 KiB
Python
497 wiersze
16 KiB
Python
# cad_shapes.py
|
|
# Standard library of shapes and operations
|
|
|
|
# Assembled by Matt Keeter (with code from Neil Gershenfeld)
|
|
# matt.keeter@cba.mit.edu
|
|
|
|
# kokompe.cba.mit.edu
|
|
|
|
###############################################################################
|
|
|
|
# 2D shapes:
|
|
# circle(x0, y0, r)
|
|
# rectangle(x0, x1, y0, y1)
|
|
# right_triangle(x0, y0, l)
|
|
# triangle(x0, y0, x1, y1, x2, y2) [clockwise order]
|
|
# polygon(x, y, r, n)
|
|
# tab(x, y, width, height, angle = 0, chamfer = 0.2)
|
|
|
|
# 3D shapes:
|
|
# extrusion(part, z0, z1)
|
|
# cylinder(x0, y0, z0, z1, r)
|
|
# sphere(x0, y0, z0, r)
|
|
# cube(x0, x1, y0, y1, z0, z1)
|
|
# cone(x0, y0, z0, z1, r0)
|
|
# pyramid(x0, x1, y0, y1, z0, z1)
|
|
|
|
# Logic operations:
|
|
# add(part1, part2)
|
|
# subtract(part1, part2)
|
|
# intersect(part1, part2)
|
|
# invert(part)
|
|
|
|
# Translation:
|
|
# move(part, dx, dy, dz = 0)
|
|
|
|
# Rotation:
|
|
# rotate(part, angle)
|
|
# rotate_90(part)
|
|
# rotate_180(part)
|
|
# rotate_270(part)
|
|
# rotate_x(part, angle)
|
|
# rotate_y(part, angle)
|
|
# rotate_z(part, angle)
|
|
|
|
# Reflection:
|
|
# reflect_x(part,x0 = 0)
|
|
# reflect_y(part,y0 = 0)
|
|
# reflect_z(part,z0 = 0)
|
|
# reflect_xy(part)
|
|
# reflect_xz(part)
|
|
# reflect_yz(part)
|
|
|
|
# Scaling:
|
|
# scale_x(part, x0, sx)
|
|
# scale_y(part, y0, sy)
|
|
# scale_z(part, z0, sz)
|
|
# scale_xy(part, x0, y0, sxy)
|
|
# scale_xyz(part, x0, y0, z0, sxyz)
|
|
|
|
# Distortion:
|
|
# attract(part, value, x0, y0, z0=0)
|
|
|
|
# Coscaling:
|
|
# coscale_x_y(part, x0, y0, y1, angle0, angle1, amplitude, offset)
|
|
# coscale_x_z(part, x0, z0, z1, angle0, angle1, amplitude, offset)
|
|
# coscale_xy_z(part, x0, y0, z0, z1, angle0, angle1, amplitude, offset)
|
|
|
|
# Tapering:
|
|
# taper_x_y(part, x0, y0, y1, s0, s1)
|
|
# taper_x_z(part, x0, z0, z1, s0, s1)
|
|
# taper_xy_z(part, x0, y0, z0, z1, s0, s1)
|
|
|
|
# Shearing:
|
|
# shear_x_y(part, y0, y1, dx0, dx1)
|
|
# shear_x_z(part, z0, z1, dx0, dx1)
|
|
# coshear_x_z(part, z0, z1, angle0, angle1, amplitude, offset)
|
|
|
|
# Color:
|
|
# color(color,part)
|
|
#
|
|
# Colors are implemented as an integer bit-field:
|
|
# R = bits 0 through 7
|
|
# G = bits 8 through 15
|
|
# B = bits 16 through 23
|
|
#
|
|
# The following colors are pre-defined:
|
|
# red, green, blue, gray, white, teal, pink,
|
|
# yellow, brown, navy, black
|
|
|
|
###############################################################################
|
|
from math_string import MString
|
|
|
|
from math import pi, sin, cos
|
|
import re
|
|
|
|
###############################################################################
|
|
# 2D Shapes
|
|
###############################################################################
|
|
def circle(x0, y0, r):
|
|
return move(MString("(pow(X,2) + pow(Y,2) <= %f)" % (r*r)), x0, y0)
|
|
|
|
def rectangle(x0, x1, y0, y1):
|
|
return MString("((X >= %f) && (X <= %f) && " % (x0, x1) +
|
|
"(Y >= %f) && (Y <= %f))" % (y0, y1))
|
|
|
|
def right_triangle(x, y, L):
|
|
tri = MString("(X < -Y) && (X > -{0}) && (Y > -{0})".format(L))
|
|
return move(tri, x + L, y + L)
|
|
|
|
def triangle(x0, y0, x1, y1, x2, y2): # points in clockwise order
|
|
tri = ("((((({y1})-({y0}))*(X-({x0}))-(({x1})-({x0}))*(Y-({y0}))) >= 0) && " + \
|
|
"(((({y2})-({y1}))*(X-({x1}))-(({x2})-({x1}))*(Y-({y1}))) >= 0) && " + \
|
|
"(((({y0})-({y2}))*(X-({x2}))-(({x0})-({x2}))*(Y-({y2}))) >= 0))").format(
|
|
x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2)
|
|
return MString(tri)
|
|
|
|
def polygon(x, y, r, n):
|
|
if n <= 2:
|
|
return '0'
|
|
part = circle(0, 0, r)
|
|
cutoff = 'Y > -%f' % (cos(pi/n) * r)
|
|
for i in range(n):
|
|
part = intersect(part, rotate(cutoff, i*360./n))
|
|
return move(MString(part), x, y)
|
|
|
|
|
|
def tab(x, y, width, height, angle = 0, chamfer = 0.2):
|
|
tab = rectangle(-width/2, width/2, 0, height)
|
|
cutout = triangle(width/2 - chamfer*height, height,
|
|
width/2, height,
|
|
width/2, height - chamfer*height)
|
|
cutout = add(cutout, reflect_x(cutout))
|
|
tab = subtract(tab, cutout)
|
|
|
|
tab = rotate(tab, angle)
|
|
tab = move(tab, x, y)
|
|
return MString(tab)
|
|
|
|
def slot(x, y, width, height, angle = 0, chamfer = 0.2):
|
|
slot = rectangle(-width/2, width/2, -height, 0)
|
|
inset = triangle(width/2, 0,
|
|
width/2 + height * chamfer, 0,
|
|
width/2, -chamfer*height)
|
|
inset = add(inset, reflect_x(inset))
|
|
slot = add(slot, inset)
|
|
|
|
slot = rotate(slot, angle)
|
|
slot = move(slot, x, y)
|
|
return MString(slot)
|
|
|
|
###############################################################################
|
|
# 3D Shapes
|
|
###############################################################################
|
|
def extrusion(part, z0, z1):
|
|
return MString("((%s) && (Z >= %f) && (Z <= %f))" % (part, z0, z1))
|
|
|
|
def cylinder(x0, y0, z0, z1, r):
|
|
return extrusion(circle(x0, y0, r), z0, z1)
|
|
|
|
def sphere(x0, y0, z0, r):
|
|
return move("(pow(X,2) + pow(Y,2) + pow(Z,2) <= %f)" % (r * r), x0, y0, z0)
|
|
|
|
def cube(x0, x1, y0, y1, z0, z1):
|
|
return MString("((X >= %f) && (X <= %f) && " % (x0, x1) +
|
|
" (Y >= %f) && (Y <= %f) && " % (y0, y1) +\
|
|
" (Z >= %f) && (Z <= %f))" % (z0, z1))
|
|
|
|
def cone(x0, y0, z0, z1, r0):
|
|
cyl = cylinder(x0, y0, z0, z1, r0)
|
|
return taper_xy_z(cyl, x0, y0, z0, z1, 1.0, 0.0)
|
|
|
|
def pyramid(x0, x1, y0, y1, z0, z1):
|
|
c = cube(x0, x1, y0, y1, z0, z1)
|
|
return taper_xy_z(c, (x0+x1)/2., (y0+y1)/2., z0, z1, 1.0, 0.0)
|
|
|
|
###############################################################################
|
|
# Logic Operations
|
|
###############################################################################
|
|
def add(part1, part2):
|
|
return MString("(%s) || (%s)" % (part1, part2))
|
|
|
|
def subtract(part1, part2):
|
|
return MString("(%s) && !(%s)" % (part1, part2))
|
|
|
|
def intersect(part1, part2):
|
|
return MString("(%s) && (%s)" % (part1, part2))
|
|
|
|
def invert(part):
|
|
return MString("!(%s)" % part)
|
|
|
|
###############################################################################
|
|
# Translation
|
|
###############################################################################
|
|
def move(part,dx,dy,dz = 0):
|
|
if dx:
|
|
part = part.replace('X','(X-%f)' % dx)
|
|
if dy:
|
|
part = part.replace('Y','(Y-%f)' % dy)
|
|
if dz:
|
|
part = part.replace('Z','(Z-%f)' % dz)
|
|
return part
|
|
|
|
###############################################################################
|
|
# Rotation
|
|
###############################################################################
|
|
def rotate(part, angle):
|
|
if angle == 90:
|
|
return rotate_90(part)
|
|
elif angle == 180:
|
|
return rotate_180(part)
|
|
elif angle == 270:
|
|
return rotate_270(part)
|
|
elif angle == 0:
|
|
return part
|
|
|
|
angle = str(angle*pi/180)
|
|
|
|
# find all already-rotated points and increment that
|
|
# rotation instead of stacking more and more trig
|
|
# functions
|
|
xrot_regex = re.compile(r'\( cos\(([\-\d.]+)\)\*X\+sin\(\1\)\*Y\)')
|
|
yrot_regex = re.compile(r'\(-sin\(([\-\d.]+)\)\*X\+cos\(\1\)\*Y\)')
|
|
|
|
def update_angle_x(match, angle):
|
|
match = match.groups()
|
|
angle = str(float(angle) + float(match[0]))
|
|
return '(cos({0})*x+sin({0})*y)'.format(angle)
|
|
|
|
def update_angle_y(match, angle):
|
|
match = match.groups()
|
|
angle = str(float(angle) + float(match[0]))
|
|
return '(-sin({0})*x+cos({0})*y)'.format(angle)
|
|
|
|
part = xrot_regex.sub(lambda x: update_angle_x(x, angle), str(part))
|
|
part = yrot_regex.sub(lambda x: update_angle_y(x, angle), str(part))
|
|
|
|
# replace all bare X, Y as before
|
|
# move all upper to lower case
|
|
part = part.replace('X','(cos({0})*X+sin({0})*y)'.format(angle))
|
|
part = part.replace('Y','(-sin({0})*X+cos({0})*y)'.format(angle))
|
|
part = part.replace('y','Y')
|
|
part = part.replace('x','X')
|
|
|
|
return MString(part)
|
|
|
|
def rotate_90(part):
|
|
part = reflect_y(part)
|
|
part = reflect_xy(part)
|
|
return part
|
|
|
|
def rotate_180(part):
|
|
part = rotate_90(part)
|
|
part = rotate_90(part)
|
|
return part
|
|
|
|
def rotate_270(part):
|
|
part = rotate_90(part)
|
|
part = rotate_90(part)
|
|
part = rotate_90(part)
|
|
return part
|
|
|
|
def rotate_x(part, angle):
|
|
angle = angle*pi/180
|
|
part = part.replace('Y','(cos(angle)*Y+sin(angle)*z)')
|
|
part = part.replace('Z','(-sin(angle)*Y+cos(angle)*z)')
|
|
part = part.replace('z','Z')
|
|
part = part.replace('angle',str(angle))
|
|
return part
|
|
|
|
def rotate_y(part, angle):
|
|
angle = angle*pi/180
|
|
part = part.replace('X','(cos(angle)*X+sin(angle)*z)')
|
|
part = part.replace('Z','(-sin(angle)*X+cos(angle)*z)')
|
|
part = part.replace('z','Z')
|
|
part = part.replace('angle',str(angle))
|
|
return part
|
|
|
|
def rotate_z(part, angle):
|
|
return rotate(part, angle)
|
|
###############################################################################
|
|
# Reflection
|
|
###############################################################################
|
|
def reflect_x(part, x0 = 0):
|
|
return part.replace('X','(%f-X)' % x0)
|
|
|
|
def reflect_y(part, y0 = 0):
|
|
return part.replace('Y','(%f-Y)' % y0)
|
|
|
|
def reflect_z(part,z0):
|
|
return part.replace('Z','(%f-Z)' % z0)
|
|
return part
|
|
|
|
def reflect_xy(part):
|
|
part = part.replace('X','temp')
|
|
part = part.replace('Y','X')
|
|
part = part.replace('temp','Y')
|
|
return part
|
|
|
|
def reflect_xz(part):
|
|
part = part.replace('X','temp')
|
|
part = part.replace('Z','X')
|
|
part = part.replace('temp','Z')
|
|
return part
|
|
|
|
def reflect_yz(part):
|
|
part = part.replace('Y','temp')
|
|
part = part.replace('Z','Y')
|
|
part = part.replace('temp','Z')
|
|
return part
|
|
###############################################################################
|
|
# Scaling
|
|
###############################################################################
|
|
def scale_x(part, x0, sx):
|
|
part = part.replace('X','((x0) + (X-(x0))/(sx))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('sx',str(sx))
|
|
return part
|
|
|
|
def scale_y(part, y0, sy):
|
|
part = part.replace('Y','((y0) + (Y-(y0))/(sy))')
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('sy',str(sy))
|
|
return part
|
|
|
|
def scale_z(part, z0, sz):
|
|
part = part.replace('Z','((z0) + (Z-(z0))/(sz))')
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('sz',str(sz))
|
|
return part
|
|
|
|
def scale_xy(part, x0, y0, sxy):
|
|
part = part.replace('X','((x0) + (X-(x0))/(sxy))')
|
|
part = part.replace('Y','((y0) + (Y-(y0))/(sxy))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('sxy',str(sxy))
|
|
return part
|
|
|
|
def scale_xyz(part, x0, y0, z0, sxyz):
|
|
part = part.replace('X','((x0) + (X-(x0))/(sxyz))')
|
|
part = part.replace('Y','((y0) + (Y-(y0))/(sxyz))')
|
|
part = part.replace('Z','((z0) + (Z-(z0))/(sxyz))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('sxyz',str(sxyz))
|
|
return part
|
|
|
|
###############################################################################
|
|
# Distortion:
|
|
###############################################################################
|
|
|
|
def attract(part, radius, x0, y0, z0=0):
|
|
part = part.replace('X',
|
|
('({x0}+(X-{x0})*(1+{r}*exp(-pow(X-{x0},2)+'+
|
|
'pow(yt-{y0},2)+pow(zt-{z0},2)/{r})))').format(
|
|
x0=x0,y0=y0,z0=z0,r=radius))
|
|
part = part.replace('Y',
|
|
('({y0}+(Y-{y0})*(1+{r}*exp(-pow(xt-{x0},2)+'+
|
|
'pow(Y-{y0},2)+pow(zt-{z0},2)/{r})))').format(
|
|
x0=x0,y0=y0,z0=z0,r=radius))
|
|
part = part.replace('Z',
|
|
('({z0}+(Z-{z0})*(1+{r}*exp(-pow(xt-{x0},2)+'+
|
|
'pow(yt-{y0},2)+pow(Z-{z0},2)/{r})))').format(
|
|
x0=x0,y0=y0,z0=z0,r=radius))
|
|
part = part.replace('xt','X')
|
|
part = part.replace('yt','Y')
|
|
part = part.replace('zt','Z')
|
|
return part
|
|
|
|
###############################################################################
|
|
# Coscaling
|
|
###############################################################################
|
|
|
|
def coscale_x_y(part, x0, y0, y1, angle0, angle1, amplitude, offset):
|
|
phase0 = pi*angle0/180.
|
|
phase1 = pi*angle1/180.
|
|
part = part.replace('X','((x0) + (X-(x0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Y-(y0))/((y1)-(y0)))))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('y1',str(y1))
|
|
part = part.replace('phase0',str(phase0))
|
|
part = part.replace('phase1',str(phase1))
|
|
part = part.replace('amplitude',str(amplitude))
|
|
part = part.replace('offset',str(offset))
|
|
return part
|
|
|
|
def coscale_x_z(part, x0, z0, z1, angle0, angle1, amplitude, offset):
|
|
phase0 = pi*angle0/180.
|
|
phase1 = pi*angle1/180.
|
|
part = part.replace('X','((x0) + (X-(x0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0)))))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('z1',str(z1))
|
|
part = part.replace('phase0',str(phase0))
|
|
part = part.replace('phase1',str(phase1))
|
|
part = part.replace('amplitude',str(amplitude))
|
|
part = part.replace('offset',str(offset))
|
|
return part
|
|
|
|
def coscale_xy_z(part, x0, y0, z0, z1, angle0, angle1, amplitude, offset):
|
|
phase0 = pi*angle0/180.
|
|
phase1 = pi*angle1/180.
|
|
part = part.replace('X','((x0) + (X-(x0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0)))))')
|
|
part = part.replace('Y','((y0) + (Y-(y0))/((offset) + (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0)))))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('z1',str(z1))
|
|
part = part.replace('phase0',str(phase0))
|
|
part = part.replace('phase1',str(phase1))
|
|
part = part.replace('amplitude',str(amplitude))
|
|
part = part.replace('offset',str(offset))
|
|
return part
|
|
|
|
###############################################################################
|
|
# Tapering
|
|
###############################################################################
|
|
def taper_x_y(part, x0, y0, y1, s0, s1):
|
|
part = part.replace('X','((x0) + (X-(x0))*((y1)-(y0))/((s1)*(Y-(y0)) + (s0)*((y1)-Y)))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('y1',str(y1))
|
|
part = part.replace('s0',str(s0))
|
|
part = part.replace('s1',str(s1))
|
|
return part
|
|
|
|
def taper_x_z(part, x0, z0, z1, s0, s1):
|
|
part = part.replace('X','((x0) + (X-(x0))*((z1)-(z0))/((s1)*(Z-(z0)) + (s0)*((z1)-Z)))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('z1',str(z1))
|
|
part = part.replace('s0',str(s0))
|
|
part = part.replace('s1',str(s1))
|
|
return part
|
|
|
|
def taper_xy_z(part, x0, y0, z0, z1, s0, s1):
|
|
part = part.replace('X','((x0) + (X-(x0))*((z1)-(z0))/((s1)*(Z-(z0)) + (s0)*((z1)-Z)))')
|
|
part = part.replace('Y','((y0) + (Y-(y0))*((z1)-(z0))/((s1)*(Z-(z0)) + (s0)*((z1)-Z)))')
|
|
part = part.replace('x0',str(x0))
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('z1',str(z1))
|
|
part = part.replace('s0',str(s0))
|
|
part = part.replace('s1',str(s1))
|
|
return part
|
|
|
|
###############################################################################
|
|
# Shearing
|
|
###############################################################################
|
|
def shear_x_y(part, y0, y1, dx0, dx1):
|
|
part = part.replace('X','(X - (dx0) - ((dx1)-(dx0))*(Y-(y0))/((y1)-(y0)))')
|
|
part = part.replace('y0',str(y0))
|
|
part = part.replace('y1',str(y1))
|
|
part = part.replace('dx0',str(dx0))
|
|
part = part.replace('dx1',str(dx1))
|
|
return part
|
|
|
|
def shear_x_z(part, z0, z1, dx0, dx1):
|
|
part = part.replace('X','(X - (dx0) - ((dx1)-(dx0))*(Z-(z0))/((z1)-(z0)))')
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('z1',str(z1))
|
|
part = part.replace('dx0',str(dx0))
|
|
part = part.replace('dx1',str(dx1))
|
|
return part
|
|
|
|
def coshear_x_z(part, z0, z1, angle0, angle1, amplitude, offset):
|
|
phase0 = pi*angle0/180.
|
|
phase1 = pi*angle1/180.
|
|
part = part.replace('X','(X - (offset) - (amplitude)*cos((phase0) + ((phase1)-(phase0))*(Z-(z0))/((z1)-(z0))))')
|
|
part = part.replace('z0',str(z0))
|
|
part = part.replace('z1',str(z1))
|
|
part = part.replace('phase0',str(phase0))
|
|
part = part.replace('phase1',str(phase1))
|
|
part = part.replace('amplitude',str(amplitude))
|
|
part = part.replace('offset',str(offset))
|
|
return part
|
|
|
|
|
|
###############################################################################
|
|
# Color
|
|
###############################################################################
|
|
red = (225 << 0)
|
|
green = (225 << 8)
|
|
blue = (225 << 16)
|
|
gray = (128 << 16) + (128 << 8) + (128 << 0)
|
|
white = (255 << 16) + (255 << 8) + (255 << 0)
|
|
teal = (255 << 16) + (255 << 8)
|
|
pink = (255 << 16) + (255 << 0)
|
|
yellow = (255 << 8) + (255 << 0)
|
|
brown = (45 << 16) + (82 << 8) + (145 << 0)
|
|
navy = (128 << 16) + (0 << 8) + (0 << 0)
|
|
tan = (60 << 16) + (90 << 8) + (125 << 0)
|
|
black = 0
|
|
|
|
def color(color, part):
|
|
return MString('(%s * (%s))' % (color, part)) |