This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ---------- 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