diff --git a/README.md b/README.md index 1efc5b3..6153210 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # openscad-py -Python OOP precompiler for OpenSCAD's language + +A Python OOP precompiler for OpenSCAD's language + +OpenSCAD ( https://openscad.org/ ) uses a functional scripting language to define solid 3D CAD models. +As such, it is a prefix language (modifiers go before the things they modify). + +OpenSCADPy allows one to write OpenSCAD scripts using an object representation, which uses method calls +to describe modifications. This way, modifications are written after the objects they modify in a postfix +fashion, more closely resembling a procedural ordering of steps in the creation of the models. +It also contains convenience functions to define a wider range of primitives, as well as some vector operations. + +## Example + +``` +Cube([1, 1, 2]).move([0, 0, 1]) +``` + +becomes + + +``` +translate(v=[0, 0, 1]) { cube(size=[1, 1, 2], center=false); } +``` + diff --git a/openscad_py.py b/openscad_py.py new file mode 100644 index 0000000..9c0a4e0 --- /dev/null +++ b/openscad_py.py @@ -0,0 +1,96 @@ + +from typing import Union + + +class Point: + """Represents a 3D point of vector""" + + def __init__(self, coords): + self.coords = coords + + @classmethod + def c(cls, coords: Union[list, Point]) -> Point: + """Ensure coords is an instance of Point""" + if isinstance(coords, Point): + return coords + return Point(coords) + + def render(self) -> str: + return ",".join([str(c) for c in self.coords]) + + +class Object: + """Abstract class for an SCAD object""" + + def __init__(self): + pass + + def _center(self) -> str: + return ('true' if self.center else 'false') + + def add(self, obj): + return Collection([self, action]) + + def render(self) -> str: + raise Exception("abstract method") + + def move(self, v: Union[list, Point]): + return Translate(v, self) + + +class Cube(Object): + + def __init__(self, size: Union[list, Point], center: bool = False): + self.size = Point.c(position) + self.center = center + + def render(self): + return f"cube(size=[{self.size.render()}], center={self._center()});" + + +def Sphere(Object): + + def __init__(self, r): + self.r = r + # $fa, $fs, $fn + + def render(self): + return f"sphere(r={self.r});" + + +def Cylinder(Object): + + def __init__(self, h, r=None, r1=None, r2=None, center: bool = False): + self.height = h + self.r1 = r if r1 is None else r1 + self.r2 = r if r2 is None else r2 + # $fa, $fs, $fn + + def render(self): + return f"cylinder(h={self.height}, r1={self.r1}, r2={self.r2}, center={self._center()});" + + +# polyhedron(points=[[],], faces[[p,],], convexity=) + + +class Collection(Object): + + def __init__(self, coll: list): + self.collection = coll + + def add(self, obj): + self.collection.append(obj) + + def render(self): + return "\n".join([o.render() for o in self.collection]) + + +class Translate(Object): + + def __init__(self, v: Union[list, Point], child: Object): + self.v = Point.c(v) + + def render(self): + return f"translate(v=[{self.v.render()}]){{\n{self.child.render()}\n}}" + +