# ---------- Primitives ----------------- | |
def circle(r=None, d=None): | |
return _base_object("circle", r=r, d=d) | |
def square(*args, center=True): | |
if len(args) == 1: | |
size = str(args[0]) | |
elif len(args) == 2: | |
size = str(args) | |
else: | |
raise Exception("Must specify either one or two lengths") | |
return _base_object("square", size, center=center) | |
def polygon(points, paths=None, convexity=None): | |
return _base_object("polygon", points, paths=paths, convexity=convexity) | |
def text(text, **kwargs): | |
return _base_object("text", text, **kwargs) | |
def sphere(r=None, d=None): | |
return _base_object("sphere", r=r, d=d) | |
def cube(*args, center=True): | |
if len(args) == 1: | |
size = str(args[0]) | |
elif len(args) == 3: | |
size = str(args) | |
else: | |
raise Exception("Must specify either one or three lengths") | |
return _base_object("cube", size, center=center) | |
def cyclinder(h=None, r1=None, c2=None, center=True, **kwargs): | |
return _base_object("cyclinder", h=h, r1=r1, r2=r2, center=center, **kwargs) | |
def polyhedron(points, faces=None, convexity=None): | |
return _base_object("polyhedron", points, faces=faces, convexity=convexity) | |
def surface(**kwargs): | |
kwargs['center'] = kwargs.get('center', True) | |
return _base_object("surface", **kwargs) | |
def import_stl(filename, **kwargs): | |
return _base_object("import", filename, **kwargs) | |
# ---------- Utils ---------------------- | |
def repl(world, render=False): | |
if render: | |
world = world.render() | |
with open("repl.scad", "w") as f: | |
print(world.scad(), file=f) | |
# ---------- Internals ------------------ | |
def _base_object(name, *args, **kwargs): | |
cmd = name + '(' | |
args = list(args) | |
for k, v in kwargs.items(): | |
if v is None: | |
continue | |
elif type(v) is bool: | |
v = str(v).lower() | |
else: | |
v = str(v) | |
args.append(k + "=" + v) | |
cmd += ', '.join(map(str, args)) | |
cmd += ');' | |
return _OpenSCADObject(cmd, '', []) | |
class _OpenSCADObject: | |
def __init__(self, prefix, postfix, objects): | |
self.prefix = prefix | |
self.postfix = postfix | |
# Filter other types out of objects, e.g., sum(...) will introduce an int. | |
self.objects = [obj for obj in objects if isinstance(obj, _OpenSCADObject)] | |
self.module_name = None | |
def scad(self): | |
lines, _ = self._traverse() | |
self._clear_name() | |
lines.append("m0();") | |
return '\n'.join(lines) | |
def _traverse(self, c=0): | |
""" Returns (lines, new counter) """ | |
if self.module_name is not None: | |
return ([], c) | |
self.module_name = "m" + str(c) | |
c += 1 | |
lines = [] | |
for i, x in enumerate(self.objects): | |
l, c = x._traverse(c) | |
lines.extend(l) | |
lines.append("module " + self.module_name + "() {") | |
lines.append(" "*4 + self.prefix) | |
for obj in self.objects: | |
lines.append(" "*8 + obj.module_name + "();") | |
if self.postfix: | |
lines.append(" "*4 + self.postfix) | |
lines.append("}") | |
lines.append("") | |
return (lines, c) | |
def _clear_name(self): | |
self.module_name = None | |
for x in self.objects: | |
x._clear_name() | |
def __add__(self, x): | |
return self.union(x) | |
def __radd__(self, x): | |
return self.union(x) | |
def __mul__(self, x): | |
return self.intersection(x) | |
def __sub__(self, x): | |
return self.difference(x) | |
def __pow__(self, x): | |
return self.minkowski(x) | |
def union(self, *others): | |
return _OpenSCADObject("union() {", "}", (self,) + others) | |
def difference(self, *others): | |
return _OpenSCADObject("difference() {", "}", (self,) + others) | |
def intersection(self, *others): | |
return _OpenSCADObject("intersection() {", "}", (self,) + others) | |
def translate(self, x=0, y=0, z=0): | |
return _OpenSCADObject("translate([{}, {}, {}])".format(x, y, z), "", (self,)) | |
def up(self, x): | |
return self.translate(z=x) | |
def down(self, x): | |
return self.translate(z=-x) | |
def right(self, x): | |
return self.translate(x=x) | |
def left(self, x): | |
return self.translate(x=-x) | |
def forward(self, x): | |
return self.translate(y=x) | |
def back(self, x): | |
return self.translate(y=-x) | |
def rotate(self, x=0, y=0, z=0): | |
return _OpenSCADObject("rotate([{}, {}, {}])".format(x, y, z), "", (self,)) | |
def scale(self, *args, **kwargs): | |
if len(args) == 1: | |
prefix = "scale({})".format(args[0]) | |
elif len(args) == 3: | |
prefix = "scale([{}, {}, {}])".format(*args) | |
else: | |
x = kwargs.get('x', 1) | |
y = kwargs.get('y', 1) | |
z = kwargs.get('z', 1) | |
prefix = "scale([{}, {}, {}])".format(x, y, z) | |
return _OpenSCADObject(prefix, "", (self,)) | |
def resize(self, x=0, y=0, z=0): | |
# differ from spec: negative values get 0,auto=false | |
return _OpenSCADObject("resize([{}, {}, {}], auto=[{}, {}, {}])".format(max(0,x), max(0,y), max(0,z), str(x<0).lower(), str(y<0).lower(), str(z<0).lower()), "", (self,)) | |
def mirror(self, x=0, y=0, z=0): | |
return _OpenSCADObject("mirror([{}, {}, {}])".format(x, y, z), "", (self,)) | |
def multmatrix(self, m): | |
return _OpenSCADObject("multmatrix(m={})".format(m), "", (self,)) | |
def color(self, r=1, g=1, b=1, a=1): | |
if type(r) is str: | |
prefix = 'color("{}", {})'.format(r, g) | |
else: | |
prefix = "color([{}, {}, {}, {}])".format(r, g, b, a) | |
return _OpenSCADObject(prefix, "", (self,)) | |
def offset(self, **kwargs): | |
return self._dictargs("offset", kwargs) | |
def hull(self, *others): | |
return _OpenSCADObject("hull() {", "}", (self,) + others) | |
def minkowski(self, *others): | |
return _OpenSCADObject("minkowski() {", "}", (self,) + others) | |
def disable(self): | |
return _OpenSCADObject("*", "", (self,)) | |
def show_only(self): | |
return _OpenSCADObject("!", "", (self,)) | |
def highlight(self): | |
return _OpenSCADObject("#", "", (self,)) | |
def debug(self): | |
return self.highlight() | |
def transparent(self): | |
return _OpenSCADObject("%", "", (self,)) | |
def background(self): | |
return self.transparent() | |
def linear_extrude(self, **kwargs): | |
kwargs['center'] = kwargs.get('center', True) | |
return self._dictargs("linear_extrude", kwargs) | |
def rotate_extrude(self, **kwargs): | |
return self._dictargs("rotate_extrude", kwargs) | |
def projection(self, **kwargs): | |
return self._dictargs("projection", kwargs) | |
def render(self, **kwargs): | |
return self._dictargs("render", kwargs) | |
def set_special(self, **kwargs): | |
return _OpenSCADObject(" ".join("${} = {};".format(*i) for i in kwargs.items()), "", (self,)) | |
def _dictargs(self, name, args): | |
return _OpenSCADObject("{}({})".format(name, ', '.join("{}={}".format(*i) for i in args.items())), "", (self,)) | |
def shell(self, thickness, resolution=None): | |
expand = self.minkowski(cube(1)) - self | |
trace = cube(thickness*2) if resolution is None else sphere(thickness).set_special(fs=resolution) | |
return expand.minkowski(trace) * self |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment