mirror of
https://github.com/csirmaz/openscad-py.git
synced 2025-06-24 03:28:34 +02:00
Many new operations and transformations
This commit is contained in:
parent
787a3689e0
commit
453fdd68c4
1 changed files with 155 additions and 24 deletions
179
openscad_py.py
179
openscad_py.py
|
@ -1,5 +1,5 @@
|
|||
|
||||
from typing import Union
|
||||
from typing import Union as TUnion
|
||||
import math
|
||||
import numpy as np
|
||||
|
||||
|
@ -14,7 +14,7 @@ class Point:
|
|||
self.c = np.array(coords, dtype=NP_TYPE)
|
||||
|
||||
@classmethod
|
||||
def c(cls, coords: Union[list, 'Point']) -> 'Point':
|
||||
def c(cls, coords: TUnion[list, 'Point']) -> 'Point':
|
||||
"""Ensure coords is an instance of Point"""
|
||||
if isinstance(coords, Point):
|
||||
return coords
|
||||
|
@ -22,7 +22,7 @@ class Point:
|
|||
|
||||
def render(self) -> str:
|
||||
"""Render the point into a SCAD script"""
|
||||
return ",".join([str(c) for c in self.c])
|
||||
return "[" + (",".join([str(c) for c in self.c])) + "]"
|
||||
|
||||
def scale(self, x: float) -> 'Point':
|
||||
"""Scale the current vector/point by a scalar"""
|
||||
|
@ -136,36 +136,58 @@ class Object:
|
|||
def _center(self) -> str:
|
||||
return ('true' if self.center else 'false')
|
||||
|
||||
def add(self, obj):
|
||||
def _add(self, obj):
|
||||
"""Add an object, forming a collection"""
|
||||
return Collection([self, obj])
|
||||
|
||||
def render(self) -> str:
|
||||
raise Exception("abstract method")
|
||||
|
||||
def translate(self, v: Union[list, Point]):
|
||||
def translate(self, v: TUnion[list, Point]) -> 'Object':
|
||||
"""Apply a translation"""
|
||||
return Translate(v=v, child=self)
|
||||
|
||||
def move(self, v: Union[list, Point]):
|
||||
def move(self, v: TUnion[list, Point]) -> 'Object':
|
||||
"""Apply a translation"""
|
||||
return Translate(v=v, child=self)
|
||||
|
||||
def rotate(self, a, v: Union[list, Point]):
|
||||
def rotate(self, a, v: TUnion[list, Point]) -> 'Object':
|
||||
"""Apply a rotation"""
|
||||
return Rotate(a=a, v=v, child=self)
|
||||
|
||||
def scale(self, v: TUnion[list, Point, float]) -> 'Object':
|
||||
"""Apply scaling. Accepts a single float for uniform scaling"""
|
||||
return Scale(v=v, child=self)
|
||||
|
||||
def color(self, r, g, b, a=1.) -> 'Object':
|
||||
"""Apply a color"""
|
||||
return Color(r=r, g=g, b=b, a=a, child=self)
|
||||
|
||||
def extrude(self, height, convexity = 10, center: bool = False) -> 'Object':
|
||||
"""Apply a linear extrusion"""
|
||||
return LinearExtrude(height=height, child=self, convexity=convexity, center=center)
|
||||
|
||||
def diff(self, tool: TUnion[list, 'Object']) -> 'Object':
|
||||
"""Remove from the object using a difference operator"""
|
||||
return Difference(subject=self, tool=tool)
|
||||
|
||||
def union(self, objects: TUnion[list, 'Object']) -> 'Object':
|
||||
return Union(child=Collection.c(objects)._add(self))
|
||||
|
||||
|
||||
class Cube(Object):
|
||||
"""A 3D primitive, cube"""
|
||||
|
||||
def __init__(self, size: Union[list, Point], center: bool = False):
|
||||
def __init__(self, size: TUnion[list, Point], center: bool = False):
|
||||
self.size = Point.c(size)
|
||||
self.center = center
|
||||
|
||||
def render(self):
|
||||
return f"cube(size=[{self.size.render()}], center={self._center()});"
|
||||
return f"cube(size={self.size.render()}, center={self._center()});"
|
||||
|
||||
|
||||
class Sphere(Object):
|
||||
"""A 3D primitive, sphere"""
|
||||
|
||||
def __init__(self, r):
|
||||
self.r = r
|
||||
|
@ -176,6 +198,7 @@ class Sphere(Object):
|
|||
|
||||
|
||||
class Cylinder(Object):
|
||||
"""A 3D primitive, cylinder"""
|
||||
|
||||
def __init__(self, h, r=None, r1=None, r2=None, center: bool = False):
|
||||
self.height = h
|
||||
|
@ -188,7 +211,7 @@ class Cylinder(Object):
|
|||
return f"cylinder(h={self.height}, r1={self.r1}, r2={self.r2}, center={self._center()});"
|
||||
|
||||
@classmethod
|
||||
def from_ends(cls, radius: float, p1: Union[list, Point], p2: Union[list, Point]) -> Object:
|
||||
def from_ends(cls, radius: float, p1: TUnion[list, Point], p2: TUnion[list, Point]) -> Object:
|
||||
"""Construct a cylinder between two points"""
|
||||
p1 = Point.c(p1)
|
||||
p2 = Point.c(p2)
|
||||
|
@ -205,9 +228,42 @@ class Cylinder(Object):
|
|||
rangle = 0
|
||||
r = z
|
||||
return cls(h=length, r=radius, center=False).rotate(a=rangle, v=r).move(p1)
|
||||
|
||||
|
||||
|
||||
class Circle(Object):
|
||||
"""A 2D primitive, circle"""
|
||||
|
||||
def __init__(self, r, fn=None):
|
||||
self.r = r
|
||||
self.fn = fn
|
||||
# $fa, $fs, $fn
|
||||
|
||||
@classmethod
|
||||
def triangle(cls, r):
|
||||
"""Create a regular triangle"""
|
||||
return cls(r=r, fn=3)
|
||||
|
||||
@classmethod
|
||||
def regular_polygon(cls, r, sides: int):
|
||||
"""Create a regular polygon"""
|
||||
return cls(r=r, fn=sides)
|
||||
|
||||
def render(self) -> str:
|
||||
fnstr = '' if self.fn is None else f", $fn={self.fn}"
|
||||
return f"circle(r={self.r}{fnstr});"
|
||||
|
||||
|
||||
class Polygon(Object):
|
||||
"""A 2D primitive, polygon"""
|
||||
|
||||
def __init__(self, points, paths=None, convexity=1):
|
||||
assert paths is None # not implemented yet
|
||||
self.points = [Point.c(p) for p in points]
|
||||
self.convexity = convexity
|
||||
|
||||
def render(self) -> str:
|
||||
return f"polygon(points=[{','.join([p.render() for p in self.points])}], convexity={self.convexity});"
|
||||
|
||||
# TODO polyhedron(points=[[],], faces[[p,],], convexity=)
|
||||
# TODO https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
|
||||
|
||||
|
@ -216,35 +272,110 @@ class Collection(Object):
|
|||
|
||||
def __init__(self, coll: list):
|
||||
self.collection = coll
|
||||
|
||||
@classmethod
|
||||
def c(cls, coll: TUnion[list, Object]) -> Object:
|
||||
"""Cast lists to collections"""
|
||||
if isinstance(coll, Object):
|
||||
return coll
|
||||
return cls(coll)
|
||||
|
||||
def add(self, obj):
|
||||
self.collection.append(obj)
|
||||
|
||||
def render(self):
|
||||
def _add(self, obj):
|
||||
return self.__class__(self.collection + [obj])
|
||||
|
||||
def render(self) -> str:
|
||||
return "\n".join([o.render() for o in self.collection])
|
||||
|
||||
|
||||
class Translate(Object):
|
||||
"""Represents a translation transformation applied to an object"""
|
||||
|
||||
def __init__(self, v: Union[list, Point], child: Object):
|
||||
def __init__(self, v: TUnion[list, Point], child: Object):
|
||||
self.v = Point.c(v)
|
||||
self.child = child
|
||||
|
||||
def render(self):
|
||||
return f"translate(v=[{self.v.render()}]){{\n{self.child.render()}\n}}"
|
||||
def render(self) -> str:
|
||||
return f"translate(v={self.v.render()}){{\n{self.child.render()}\n}}"
|
||||
|
||||
|
||||
class Rotate(Object):
|
||||
"""Represents a rotation transformation applied to an object"""
|
||||
|
||||
def __init__(self, a, v: Union[list, Point], child: Object):
|
||||
def __init__(self, a, v: TUnion[list, Point], child: Object):
|
||||
self.a = a
|
||||
self.v = Point.c(v)
|
||||
self.child = child
|
||||
|
||||
def render(self):
|
||||
return f"rotate(a={self.a}, v=[{self.v.render()}]){{\n{self.child.render()}\n}}"
|
||||
|
||||
|
||||
|
||||
def render(self) -> str:
|
||||
return f"rotate(a={self.a}, v={self.v.render()}){{\n{self.child.render()}\n}}"
|
||||
|
||||
|
||||
class Scale(Object):
|
||||
|
||||
def __init__(self, v: TUnion[list, Point, float], child: Object):
|
||||
if isinstance(v, float):
|
||||
v = [v, v, v]
|
||||
self.v = Point.c(v)
|
||||
self.child = child
|
||||
|
||||
def render(self) -> str:
|
||||
return f"scale(v={self.v.render()}){{\n{self.child.render()}\n}}"
|
||||
|
||||
|
||||
class Color(Object):
|
||||
|
||||
def __init__(self, child: Object, r, g, b, a=1.):
|
||||
self.color = [r, g, b, a]
|
||||
self.child = child
|
||||
|
||||
def render(self) -> str:
|
||||
return f"color(c=[{','.join([str(c) for c in self.color])}]){{ {self.child.render()} }}"
|
||||
|
||||
|
||||
class LinearExtrude(Object):
|
||||
"""Represents a linear extrusion applied to an object"""
|
||||
|
||||
def __init__(self, height, child: Object, convexity = 10, center: bool = False):
|
||||
self.height = height
|
||||
self.child = child
|
||||
self.convexity = convexity
|
||||
self.center = center
|
||||
# twist, slices, scale (float/vector), $fn
|
||||
|
||||
def render(self) -> str:
|
||||
return f"linear_extrude(height={self.height}, center={self._center()}, convexity={self.convexity}){{\n{self.child.render()}\n}}"
|
||||
|
||||
|
||||
class Union(Object):
|
||||
"""Represents a union applied to an object (usually a collection of objects)"""
|
||||
|
||||
def __init__(self, child: TUnion[Object, list]):
|
||||
self.child = Collection.c(child)
|
||||
|
||||
def render(self) -> str:
|
||||
return f"union(){{ {self.child.render()} }}"
|
||||
|
||||
def union(self, objects: TUnion[list, Object]) -> Object:
|
||||
return self.__class__(self.child._add(objects))
|
||||
|
||||
|
||||
class Intersection(Object):
|
||||
"""Represents an intersection applied to an object (usually a collection of objects)"""
|
||||
|
||||
def __init__(self, child: TUnion[Object, list]):
|
||||
self.child = Collection.c(child)
|
||||
|
||||
def render(self) -> str:
|
||||
return f"intersection(){{ {self.child.render()} }}"
|
||||
|
||||
|
||||
class Difference(Object):
|
||||
"""Represents a difference"""
|
||||
|
||||
def __init__(self, subject: Object, tool: TUnion[list, Object]):
|
||||
self.subject = subject
|
||||
self.tool = Collection.c(tool) # what to remove
|
||||
|
||||
def render(self) -> str:
|
||||
return f"difference(){{ {self.subject.render()}\n{self.tool.render()} }}"
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue