Module openscad_py.point
Classes
class Point (coords: List[float])-
Expand source code
class Point: """Represents a point or vector in arbitrary number of dimensions""" def __init__(self, coords: List[float]): self.c = np.array(coords, dtype=NP_TYPE) @classmethod def c(cls, coords: TUnion[list[float], 'Point']) -> 'Point': """Ensure `coords` is an instance of Point (idempotent)""" if isinstance(coords, Point): return coords return Point(coords) def render(self) -> str: """Render the point / vector into OpenSCAD code""" return "[" + (",".join([str(c) for c in self.c])) + "]" def render_stl(self) -> str: """Render the point / vector into STL""" return " ".join([str(c) for c in self.c]) def scale(self, x: float) -> 'Point': """Scale the current point / vector by the scalar `x`""" return self.__class__(self.c * x) def add(self, p: 'Point') -> 'Point': """Add another point / vector `p` to the current one""" assert isinstance(p, Point) assert self.dim() == p.dim() return self.__class__(self.c + p.c) def sub(self, p: 'Point') -> 'Point': """Subtract another point / vector `p` from the current one""" assert isinstance(p, Point) assert self.dim() == p.dim() return self.__class__(self.c - p.c) def dim(self) -> int: """Return the number of dimensions""" return self.c.shape[0] def is_zero(self) -> bool: """Return whether all coordinates are very close to 0""" return np.all(np.abs(self.c) < EPSILON) def length(self) -> float: """Return the length of the vector""" return np.sqrt(np.square(self.c).sum()) def norm(self) -> 'Point': """Return a normalized version of the vector (scaled to length 1)""" l = self.length() if l == 0: raise Exception("Attempted to normalise 0 vector") return self.__class__(self.c / self.length()) def dot(self, p: 'Point') -> float: """Return the dot product of the current vector and `p`""" return np.dot(self.c, p.c) def cross(self, p: 'Point') -> 'Point': """Return the cross product of the current vector and `p`""" assert self.dim() == 3 assert p.dim() == 3 return Point([ self.c[1]*p.c[2] - self.c[2]*p.c[1], self.c[2]*p.c[0] - self.c[0]*p.c[2], self.c[0]*p.c[1] - self.c[1]*p.c[0] ]) def eq(self, p: 'Point') -> bool: """Return whether the current point / vector and `p` are equal""" return (self.c == p.c).all() def lt(self, p: 'Point') -> bool: """Return whether the current vector is smaller than `p` in each dimension""" return (self.c < p.c).all() def le(self, p: 'Point') -> bool: """Return whether the current vector is smaller or equal to `p` in each dimension""" return (self.c <= p.c).all() def gt(self, p: 'Point') -> bool: """Return whether the current vector is greater than `p` in each dimension""" return (self.c > p.c).all() def ge(self, p: 'Point') -> bool: """Return whether the current vector is greater or equal to `p` in each dimension""" return (self.c >= p.c).all() def allclose(self, p: 'Point') -> bool: """Return whether the current point / vector and `p` are close to each other""" return self.c.shape == p.c.shape and np.allclose(self.c, p.c) def angle(self, p: 'Point', mode: str = "deg") -> float: """Return the angle between two vectors in degrees or radians Arguments: - p: a Point object - mode: "deg" | "rad" """ r = self.dot(p) r = r / self.length() / p.length() r = math.acos(r) if mode == "rad": return r if mode == "deg": return r / math.pi * 180. raise ValueError("Unknown mode") def z_slope(self, mode: str = "deg") -> float: """Return the slope of a vector in degrees or radians Arguments: - mode: "deg" | "rad" """ r = self.c[2] / self.length() r = math.asin(r) if mode == "rad": return r if mode == "deg": return r / math.pi * 180. raise ValueError("Unknown mode") def rotate(self, coords, angle: float) -> 'Point': """Rotate the current vector Arguments: - coords: A list of 2 coordinate indices to rotate - angle: the angle to rotate by, in degrees """ assert len(coords) == 2 ca, cb = coords s = np.sin(angle / 180. * np.pi) c = np.cos(angle / 180. * np.pi) r = self.clone().reset_cache() r.c[ca] = c * self.c[ca] + s * self.c[cb] r.c[cb] = -s * self.c[ca] + c * self.c[cb] return r # Operator overloading def __add__(self, other): """Use `p1 + p2` to add two vectors""" return self.add(other) def __radd__(self, other): """Use `p1 + p2` to add two vectors""" assert isinstance(other, Point) return other.add(self) def __sub__(self, other): """Use `p1 - p2` to subtract two vectors""" return self.sub(other) def __rsub__(self, other): """Use `p1 - p2` to subtract two vectors""" assert isinstance(other, Point) return other.sub(self) def __mul__(self, other): """Use `p * x` to scale a vector""" return self.scale(other) def __rmul__(self, other): """Use `x * p` to scale a vector""" return self.scale(other) def __neg__(self): """Use `-p` to negate a vector""" return self.scale(-1.)Represents a point or vector in arbitrary number of dimensions
Static methods
def c(coords: list[float] | ForwardRef('Point')) ‑> Point-
Ensure
coordsis an instance of Point (idempotent)
Methods
def __add__(self, other)-
Expand source code
def __add__(self, other): """Use `p1 + p2` to add two vectors""" return self.add(other)Use
p1 + p2to add two vectors def __mul__(self, other)-
Expand source code
def __mul__(self, other): """Use `p * x` to scale a vector""" return self.scale(other)Use
p * xto scale a vector def __neg__(self)-
Expand source code
def __neg__(self): """Use `-p` to negate a vector""" return self.scale(-1.)Use
-pto negate a vector def __rmul__(self, other)-
Expand source code
def __rmul__(self, other): """Use `x * p` to scale a vector""" return self.scale(other)Use
x * pto scale a vector def __sub__(self, other)-
Expand source code
def __sub__(self, other): """Use `p1 - p2` to subtract two vectors""" return self.sub(other)Use
p1 - p2to subtract two vectors def add(self,
p: Point) ‑> Point-
Expand source code
def add(self, p: 'Point') -> 'Point': """Add another point / vector `p` to the current one""" assert isinstance(p, Point) assert self.dim() == p.dim() return self.__class__(self.c + p.c)Add another point / vector
pto the current one def allclose(self,
p: Point) ‑> bool-
Expand source code
def allclose(self, p: 'Point') -> bool: """Return whether the current point / vector and `p` are close to each other""" return self.c.shape == p.c.shape and np.allclose(self.c, p.c)Return whether the current point / vector and
pare close to each other def angle(self,
p: Point,
mode: str = 'deg') ‑> float-
Expand source code
def angle(self, p: 'Point', mode: str = "deg") -> float: """Return the angle between two vectors in degrees or radians Arguments: - p: a Point object - mode: "deg" | "rad" """ r = self.dot(p) r = r / self.length() / p.length() r = math.acos(r) if mode == "rad": return r if mode == "deg": return r / math.pi * 180. raise ValueError("Unknown mode")Return the angle between two vectors in degrees or radians
Arguments
- p: a Point object
- mode: "deg" | "rad"
def cross(self,
p: Point) ‑> Point-
Expand source code
def cross(self, p: 'Point') -> 'Point': """Return the cross product of the current vector and `p`""" assert self.dim() == 3 assert p.dim() == 3 return Point([ self.c[1]*p.c[2] - self.c[2]*p.c[1], self.c[2]*p.c[0] - self.c[0]*p.c[2], self.c[0]*p.c[1] - self.c[1]*p.c[0] ])Return the cross product of the current vector and
p def dim(self) ‑> int-
Expand source code
def dim(self) -> int: """Return the number of dimensions""" return self.c.shape[0]Return the number of dimensions
def dot(self,
p: Point) ‑> float-
Expand source code
def dot(self, p: 'Point') -> float: """Return the dot product of the current vector and `p`""" return np.dot(self.c, p.c)Return the dot product of the current vector and
p def eq(self,
p: Point) ‑> bool-
Expand source code
def eq(self, p: 'Point') -> bool: """Return whether the current point / vector and `p` are equal""" return (self.c == p.c).all()Return whether the current point / vector and
pare equal def ge(self,
p: Point) ‑> bool-
Expand source code
def ge(self, p: 'Point') -> bool: """Return whether the current vector is greater or equal to `p` in each dimension""" return (self.c >= p.c).all()Return whether the current vector is greater or equal to
pin each dimension def gt(self,
p: Point) ‑> bool-
Expand source code
def gt(self, p: 'Point') -> bool: """Return whether the current vector is greater than `p` in each dimension""" return (self.c > p.c).all()Return whether the current vector is greater than
pin each dimension def is_zero(self) ‑> bool-
Expand source code
def is_zero(self) -> bool: """Return whether all coordinates are very close to 0""" return np.all(np.abs(self.c) < EPSILON)Return whether all coordinates are very close to 0
def le(self,
p: Point) ‑> bool-
Expand source code
def le(self, p: 'Point') -> bool: """Return whether the current vector is smaller or equal to `p` in each dimension""" return (self.c <= p.c).all()Return whether the current vector is smaller or equal to
pin each dimension def length(self) ‑> float-
Expand source code
def length(self) -> float: """Return the length of the vector""" return np.sqrt(np.square(self.c).sum())Return the length of the vector
def lt(self,
p: Point) ‑> bool-
Expand source code
def lt(self, p: 'Point') -> bool: """Return whether the current vector is smaller than `p` in each dimension""" return (self.c < p.c).all()Return whether the current vector is smaller than
pin each dimension def norm(self) ‑> Point-
Expand source code
def norm(self) -> 'Point': """Return a normalized version of the vector (scaled to length 1)""" l = self.length() if l == 0: raise Exception("Attempted to normalise 0 vector") return self.__class__(self.c / self.length())Return a normalized version of the vector (scaled to length 1)
def render(self) ‑> str-
Expand source code
def render(self) -> str: """Render the point / vector into OpenSCAD code""" return "[" + (",".join([str(c) for c in self.c])) + "]"Render the point / vector into OpenSCAD code
def render_stl(self) ‑> str-
Expand source code
def render_stl(self) -> str: """Render the point / vector into STL""" return " ".join([str(c) for c in self.c])Render the point / vector into STL
def rotate(self, coords, angle: float) ‑> Point-
Expand source code
def rotate(self, coords, angle: float) -> 'Point': """Rotate the current vector Arguments: - coords: A list of 2 coordinate indices to rotate - angle: the angle to rotate by, in degrees """ assert len(coords) == 2 ca, cb = coords s = np.sin(angle / 180. * np.pi) c = np.cos(angle / 180. * np.pi) r = self.clone().reset_cache() r.c[ca] = c * self.c[ca] + s * self.c[cb] r.c[cb] = -s * self.c[ca] + c * self.c[cb] return rRotate the current vector
Arguments
- coords: A list of 2 coordinate indices to rotate
- angle: the angle to rotate by, in degrees
def scale(self, x: float) ‑> Point-
Expand source code
def scale(self, x: float) -> 'Point': """Scale the current point / vector by the scalar `x`""" return self.__class__(self.c * x)Scale the current point / vector by the scalar
x def sub(self,
p: Point) ‑> Point-
Expand source code
def sub(self, p: 'Point') -> 'Point': """Subtract another point / vector `p` from the current one""" assert isinstance(p, Point) assert self.dim() == p.dim() return self.__class__(self.c - p.c)Subtract another point / vector
pfrom the current one def z_slope(self, mode: str = 'deg') ‑> float-
Expand source code
def z_slope(self, mode: str = "deg") -> float: """Return the slope of a vector in degrees or radians Arguments: - mode: "deg" | "rad" """ r = self.c[2] / self.length() r = math.asin(r) if mode == "rad": return r if mode == "deg": return r / math.pi * 180. raise ValueError("Unknown mode")Return the slope of a vector in degrees or radians
Arguments
- mode: "deg" | "rad"