Documentation

This commit is contained in:
Elod Csirmaz 2024-11-30 22:45:02 +00:00
parent 9d24a2c85f
commit bb7155a5fd
25 changed files with 88 additions and 21 deletions

View file

@ -2,57 +2,73 @@
A Python OOP precompiler for OpenSCAD's language
OpenSCAD ( https://openscad.org/ ) uses a functional scripting language to define solid 3D CAD models.
[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.
It also contains convenience functions to define a wider range of primitives, vector operations, and a method
to export polyhedra directly to STL.
## Example
```
Cube([1, 1, 2]).move([0, 0, 1]).render()
```python
# example.py
from openscad_py import Cube
colored_moved_cube = Cube([1, 1, 1]).move([2, 0, 0]).color(r=1, g=0, b=0)
print(colored_moved_cube.render())
```
returns
prints the OpenSCAD code
```
translate(v=[0, 0, 1]) { cube(size=[1, 1, 2], center=false); }
# example.scad
color(c=[1,0,0,1.0]){ translate(v=[2.0,0.0,0.0]){
cube(size=[1.0,1.0,1.0], center=false);
} }
```
An easy way to write and render the OpenSCAD code would be
```
$ python3 example.py > example.scad
$ openscad example.scad
```
## Notable convenience functions
### Computational geometry
Usual computational geometry functions on the `Point` class that work in an arbitrary number of dimensions. Overloads for algebraic operators.
Usual computational geometry functions in the `Point` class that work in an arbitrary number of dimensions.
Overloads for algebraic operators. Examples:
```
```python
distance = (Point((0, 0, 1)) - Point((1, 0, 1))).length()
angle_between = Point((0, 0, 1) * 2).angle(Point((1, 0, 1)))
```
### Cylinders
`Cylinder.from_ends()` constructs a cylinder between two given points in space
`Cylinder.from_ends()` constructs a cylinder between two given points in space. Example:
```
```python
openscad_code = Cylinder.from_ends(radius=2, p1=(0, 0, 1), p2=(1, 0, 2)).render()
```
### Tubes and toroids from a point grid
`Polyhedron.tube()` creates a tube-like polyhedron from a 2D array of points
`Polyhedron.tube()` creates a tube-like Polyhedron object from a 2D array of points.
`Polyhedron.torus()` creates a toroid polyhedron from a 2D array of points
`Polyhedron.torus()` creates a toroid Polyhedron object from a 2D array of points.
### Tubes from a path
`PathTube` creates a tube-like or toroid polyhedron from an arbitrary path
`PathTube` creates a tube-like or toroid Polyhedron object from an arbitrary path. Example:
```
```python
PathTube(
points=[(0,0,0), (0,0,1), (1,0,2), (1,1,0), (0,.5,0)],
radius=.2,
@ -64,7 +80,9 @@ PathTube(
### Polyhedron from a height map
```
`Polyhedron.from_heightmap()` creates a Polyhedron object from a 2D matrix of heights. Example:
```python
Polyhedron.from_heightmap(
heights=[
[3, 3, 1, 1, 1],
@ -81,4 +99,24 @@ Polyhedron.from_heightmap(
### Direct STL export
`Polyhedron.render_stl()` exports any polyhedron into STL directly
`Polyhedron.render_stl()` exports a Polyhedron object into STL directly.
This works well with `tube()`, `torus()`, `from_heightmap()` and `PathTube` described above.
Note that the polyhedron object cannot be post-modified (e.g. by `union`, `difference`) - if so,
use OpenSCAD to render the object and export to STL.
## Overview and usage
In `openscad_py`, all objects (including derived ones) come with a large set of convenience methods
to apply transformations, implemented in the base [`Object` class](https://github.com/csirmaz/openscad-py/blob/main/openscad_py/object_.py).
This allows to freely specify transformations on any object:
```python
moved_cube = Cube([1, 1, 1]).move([2, 0, 0])
colored_moved_cube = Cube([1, 1, 1]).move([2, 0, 0]).color(r=1, g=0, b=0)
```
Once the desired object has been created, call `render()` on the final object to obtain the
OpenSCAD code.
For the full list of convenience functions, see
https://github.com/csirmaz/openscad-py/blob/main/openscad_py/object_.py .

7
example.py Normal file
View file

@ -0,0 +1,7 @@
from openscad_py import Cube
colored_moved_cube = Cube([1, 1, 1]).move([2, 0, 0]).color(r=1, g=0, b=0)
print(colored_moved_cube.render())

3
example.scad Normal file
View file

@ -0,0 +1,3 @@
color(c=[1,0,0,1.0]){ translate(v=[2.0,0.0,0.0]){
cube(size=[1.0,1.0,1.0], center=false);
} }

View file

@ -29,6 +29,7 @@ class Circle(Object):
return cls(r=r, fn=sides)
def render(self) -> str:
"""Render the object into OpenSCAD code"""
fnstr = '' if self.fn is None else f", $fn={self.fn}"
return f"circle(r={self.r}{fnstr});"

View file

@ -22,5 +22,6 @@ class Collection(Object):
return self.__class__(self.collection + [obj])
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return "\n".join([o.render() for o in self.collection])

View file

@ -13,6 +13,7 @@ class Color(Object):
self.child = child
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"color(c=[{','.join([str(c) for c in self.color])}]){{ {self.child.render()} }}"

View file

@ -20,5 +20,6 @@ class Cube(Object):
self.center = center
def render(self):
"""Render the object into OpenSCAD code"""
return f"cube(size={self.size.render()}, center={self._center()});"

View file

@ -22,6 +22,7 @@ class Cylinder(Object):
# $fa, $fs, $fn
def render(self):
"""Render the object into OpenSCAD code"""
return f"cylinder(h={self.height}, r1={self.r1}, r2={self.r2}, center={self._center()});"
@classmethod

View file

@ -19,5 +19,6 @@ class DeltaOffset(Object):
self.chamfer = chamfer
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"offset(delta={delta}, chamfer={'true' if self.chamfer else 'false'}){{\n{self.child.render()}\n}}"

View file

@ -15,5 +15,6 @@ class Difference(Object):
self.tool = Collection.c(tool) # what to remove
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"difference(){{ {self.subject.render()}\n{self.tool.render()} }}"

View file

@ -8,11 +8,12 @@ class Header:
def __init__(self, quality: str = 'draft'):
# See https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Circle_resolution:_$fa,_$fs,_and_$fn
self.quality = quality
def render(self):
# See https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Circle_resolution:_$fa,_$fs,_and_$fn
"""Return OpenSCAD code"""
if self.quality == 'draft':
return ""
if self.quality == 'mid':

View file

@ -14,6 +14,7 @@ class Intersection(Object):
self.child = Collection.c(child)
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"intersection(){{ {self.child.render()} }}"

View file

@ -20,6 +20,7 @@ class LinearExtrude(Object):
# twist, slices, scale (float/vector), $fn
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"linear_extrude(height={self.height}, center={self._center()}, convexity={self.convexity}){{\n{self.child.render()}\n}}"

View file

@ -18,6 +18,7 @@ class Object:
return Collection([self, obj])
def render(self) -> str:
"""Render the object into OpenSCAD code"""
raise Exception("abstract method")
def translate(self, v: TUnion[list, Point]) -> 'Object':

View file

@ -119,6 +119,7 @@ class PathTube(Object):
return Polyhedron.tube(points=points_rows, convexity=self.convexity, make_torus=self.make_torus)
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return self.process().render()

View file

@ -22,7 +22,7 @@ class Point:
return Point(coords)
def render(self) -> str:
"""Render the point into a SCAD script"""
"""Render the object into OpenSCAD code"""
return "[" + (",".join([str(c) for c in self.c])) + "]"
def render_stl(self) -> str:

View file

@ -16,5 +16,6 @@ class Polygon(Object):
self.convexity = convexity
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"polygon(points=[{','.join([p.render() for p in self.points])}], convexity={self.convexity});"

View file

@ -176,6 +176,7 @@ class Polyhedron(Object):
return cls(points=point_list, faces=faces, convexity=convexity)
def render(self) -> str:
"""Render the object into OpenSCAD code"""
faces_list = [f"[{','.join([str(x) for x in face])}]" for face in self.faces]
return f"polyhedron(points=[{','.join([p.render() for p in self.points])}], faces=[{','.join(faces_list)}], convexity={self.convexity});"

View file

@ -19,6 +19,7 @@ class RadialOffset(Object):
# $fa, $fs, and $fn
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"offset(r={self.r}){{\n{self.child.render()}\n}}"

View file

@ -16,6 +16,7 @@ class Rotate(Object):
self.child = child
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"rotate(a={self.a}, v={self.v.render()}){{\n{self.child.render()}\n}}"

View file

@ -21,6 +21,7 @@ class RotateExtrude(Object):
# $fa, $fs, $fn
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"rotate_extrude(angle={self.angle}, convexity={self.convexity}) {{\n{self.child.render()}\n}}"

View file

@ -16,6 +16,7 @@ class Scale(Object):
self.child = child
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"scale(v={self.v.render()}){{\n{self.child.render()}\n}}"

View file

@ -14,13 +14,12 @@ class Sphere(Object):
See https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language#sphere
"""
def __init__(self, r):
self.r = r
# $fa, $fs, $fn
def render(self):
"""Render the object into OpenSCAD code"""
return f"sphere(r={self.r});"

View file

@ -15,5 +15,6 @@ class Translate(Object):
self.child = child
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"translate(v={self.v.render()}){{\n{self.child.render()}\n}}"

View file

@ -15,6 +15,7 @@ class Union(Object):
self.child = Collection.c(child)
def render(self) -> str:
"""Render the object into OpenSCAD code"""
return f"union(){{ {self.child.render()} }}"
def union(self, objects: TUnion[list, Object]) -> Object: