mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Completed docstrings
This commit is contained in:
parent
da093c25d6
commit
9b6831ee95
2 changed files with 163 additions and 174 deletions
|
|
@ -14,55 +14,28 @@ f1.wrapped.TShape() == f2.wrapped.TShape() <=== TRUE
|
|||
Thanks. Playing around a bit more, it seems like translate() makes the underlying TShapes unequal, but Shape.moved() preserves TShape. This returns true, which could be useful: x1 = cq.Workplane().box(3,4,5) x2 = cq.Workplane(x1.findSolid().moved(cq.Location(cq.Vector(1,2,3),cq.Vector(4,5,6),7))) f1 = x1.faces(">Z").val() f2 = x2.faces(">Z").val() f1.wrapped.TShape() == f2.wrapped.TShape() <=== TRUE
|
||||
|
||||
"""
|
||||
import logging
|
||||
from functools import partial
|
||||
from math import pi, sin, cos, radians, sqrt
|
||||
from pstats import SortKey
|
||||
from typing import Union, Iterable, Sequence, Callable
|
||||
import builtins
|
||||
from math import radians
|
||||
from typing import Union
|
||||
from enum import Enum, auto
|
||||
import cadquery as cq
|
||||
from cadquery.hull import find_hull
|
||||
from cadquery import (
|
||||
Edge,
|
||||
Face,
|
||||
Wire,
|
||||
Vector,
|
||||
Shape,
|
||||
Location,
|
||||
Vertex,
|
||||
Compound,
|
||||
Solid,
|
||||
Plane,
|
||||
)
|
||||
from cadquery.occ_impl.shapes import VectorLike, Real
|
||||
from OCP.gp import gp_Vec, gp_Pnt, gp_Ax1, gp_Dir, gp_Trsf
|
||||
import cq_warehouse.extensions
|
||||
|
||||
|
||||
class Rotation(Location):
|
||||
def __init__(self, about_x: float = 0, about_y: float = 0, about_z: float = 0):
|
||||
self.about_x = about_x
|
||||
self.about_y = about_y
|
||||
self.about_z = about_z
|
||||
|
||||
# Compute rotation matrix.
|
||||
rx = gp_Trsf()
|
||||
rx.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), radians(about_x))
|
||||
ry = gp_Trsf()
|
||||
ry.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), radians(about_y))
|
||||
rz = gp_Trsf()
|
||||
rz.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radians(about_z))
|
||||
super().__init__(Location(rx * ry * rz).wrapped)
|
||||
|
||||
|
||||
RotationLike = Union[tuple[float, float, float], Rotation]
|
||||
|
||||
z_axis = (Vector(0, 0, 0), Vector(0, 0, 1))
|
||||
|
||||
_context_stack = []
|
||||
|
||||
|
||||
#
|
||||
# Operators
|
||||
#
|
||||
def __matmul__custom(e: Union[Edge, Wire], p: float):
|
||||
return e.positionAt(p)
|
||||
|
||||
|
|
@ -75,81 +48,12 @@ Edge.__matmul__ = __matmul__custom
|
|||
Edge.__mod__ = __mod__custom
|
||||
Wire.__matmul__ = __matmul__custom
|
||||
Wire.__mod__ = __mod__custom
|
||||
line = Edge.makeLine(Vector(0, 0, 0), Vector(10, 0, 0))
|
||||
# print(f"position of line at 1/2: {line @ 0.5=}")
|
||||
# print(f"tangent of line at 1/2: {line % 0.5=}")
|
||||
|
||||
context_stack = []
|
||||
|
||||
|
||||
def by_x(obj: Shape) -> float:
|
||||
return obj.Center().x
|
||||
|
||||
|
||||
def _by_x_shape(self) -> float:
|
||||
return self.Center().x
|
||||
|
||||
|
||||
Shape.by_x = _by_x_shape
|
||||
|
||||
|
||||
def by_y(obj: Shape) -> float:
|
||||
return obj.Center().y
|
||||
|
||||
|
||||
def _by_y_shape(self) -> float:
|
||||
return self.Center().y
|
||||
|
||||
|
||||
Shape.by_y = _by_y_shape
|
||||
|
||||
|
||||
def by_z(obj: Shape) -> float:
|
||||
return obj.Center().z
|
||||
|
||||
|
||||
def _by_z_shape(self) -> float:
|
||||
return self.Center().z
|
||||
|
||||
|
||||
Shape.by_z = _by_z_shape
|
||||
|
||||
|
||||
def by_length(obj: Union[Edge, Wire]) -> float:
|
||||
return obj.Length()
|
||||
|
||||
|
||||
def _by_length_edge_or_wire(self) -> float:
|
||||
return self.Length()
|
||||
|
||||
|
||||
Edge.by_length = _by_length_edge_or_wire
|
||||
Wire.by_length = _by_length_edge_or_wire
|
||||
|
||||
|
||||
def by_radius(obj: Union[Edge, Wire]) -> float:
|
||||
return obj.radius()
|
||||
|
||||
|
||||
def _by_radius_edge_or_wire(self) -> float:
|
||||
return self.radius()
|
||||
|
||||
|
||||
Edge.by_radius = _by_radius_edge_or_wire
|
||||
Wire.by_radius = _by_radius_edge_or_wire
|
||||
|
||||
|
||||
def by_area(obj: cq.Shape) -> float:
|
||||
return obj.Area()
|
||||
|
||||
|
||||
def _by_area_shape(self) -> float:
|
||||
return self.Area()
|
||||
|
||||
|
||||
Shape.by_area = _by_area_shape
|
||||
|
||||
|
||||
#
|
||||
# ENUMs
|
||||
#
|
||||
class Select(Enum):
|
||||
ALL = auto()
|
||||
LAST = auto()
|
||||
|
|
@ -232,24 +136,6 @@ class BuildAssembly:
|
|||
pass
|
||||
|
||||
|
||||
def _null(self):
|
||||
return self
|
||||
|
||||
|
||||
Solid.null = _null
|
||||
Compound.null = _null
|
||||
|
||||
|
||||
def pts_to_locations(*pts: Union[Vector, Location]) -> list[Location]:
|
||||
if pts:
|
||||
locations = [
|
||||
pt if isinstance(pt, Location) else Location(Vector(pt)) for pt in pts
|
||||
]
|
||||
else:
|
||||
locations = [Location(Vector())]
|
||||
return locations
|
||||
|
||||
|
||||
class SortBy(Enum):
|
||||
X = auto()
|
||||
Y = auto()
|
||||
|
|
@ -261,6 +147,28 @@ class SortBy(Enum):
|
|||
DISTANCE = auto()
|
||||
|
||||
|
||||
#
|
||||
# DirectAPI Classes
|
||||
#
|
||||
class Rotation(Location):
|
||||
def __init__(self, about_x: float = 0, about_y: float = 0, about_z: float = 0):
|
||||
self.about_x = about_x
|
||||
self.about_y = about_y
|
||||
self.about_z = about_z
|
||||
|
||||
# Compute rotation matrix.
|
||||
rx = gp_Trsf()
|
||||
rx.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), radians(about_x))
|
||||
ry = gp_Trsf()
|
||||
ry.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), radians(about_y))
|
||||
rz = gp_Trsf()
|
||||
rz.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radians(about_z))
|
||||
super().__init__(Location(rx * ry * rz).wrapped)
|
||||
|
||||
|
||||
RotationLike = Union[tuple[float, float, float], Rotation]
|
||||
|
||||
|
||||
class ShapeList(list):
|
||||
axis_map = {
|
||||
Axis.X: ((1, 0, 0), (-1, 0, 0)),
|
||||
|
|
|
|||
153
build_part.py
153
build_part.py
|
|
@ -6,13 +6,11 @@ TODO:
|
|||
"""
|
||||
from math import radians, tan
|
||||
from typing import Union
|
||||
import cadquery as cq
|
||||
from cadquery import (
|
||||
Edge,
|
||||
Face,
|
||||
Wire,
|
||||
Vector,
|
||||
Shape,
|
||||
Location,
|
||||
Vertex,
|
||||
Compound,
|
||||
|
|
@ -23,34 +21,43 @@ from cadquery.occ_impl.shapes import VectorLike
|
|||
import cq_warehouse.extensions
|
||||
from build123d_common import *
|
||||
|
||||
logging.basicConfig(
|
||||
filename="build123D.log",
|
||||
encoding="utf-8",
|
||||
level=logging.DEBUG,
|
||||
# level=logging.CRITICAL,
|
||||
format="%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)s - %(funcName)20s() ] - %(message)s",
|
||||
)
|
||||
# logging.basicConfig(
|
||||
# filename="build123D.log",
|
||||
# encoding="utf-8",
|
||||
# level=logging.DEBUG,
|
||||
# # level=logging.CRITICAL,
|
||||
# format="%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)s - %(funcName)20s() ] - %(message)s",
|
||||
# )
|
||||
|
||||
|
||||
class BuildPart:
|
||||
"""BuildPart
|
||||
|
||||
Create 3D parts (objects with the property of volume) from sketches or 3D objects.
|
||||
|
||||
Args:
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADDITION.
|
||||
workplane (Plane, optional): initial plane to work on. Defaults to Plane.named("XY").
|
||||
"""
|
||||
|
||||
@property
|
||||
def workplane_count(self) -> int:
|
||||
"""Number of active workplanes"""
|
||||
return len(self.workplanes)
|
||||
|
||||
@property
|
||||
def pending_faces_count(self) -> int:
|
||||
"""Number of pending faces"""
|
||||
return len(self.pending_faces.values())
|
||||
|
||||
@property
|
||||
def pending_edges_count(self) -> int:
|
||||
"""Number of pending edges"""
|
||||
return len(self.pending_edges.values())
|
||||
|
||||
@property
|
||||
def pending_solids_count(self) -> int:
|
||||
return len(self.pending_solids.values())
|
||||
|
||||
@property
|
||||
def pending_location_count(self) -> int:
|
||||
"""Number of current locations"""
|
||||
return len(self.locations)
|
||||
|
||||
def __init__(
|
||||
|
|
@ -60,11 +67,9 @@ class BuildPart:
|
|||
):
|
||||
self.part: Compound = None
|
||||
self.workplanes: list[Plane] = [workplane]
|
||||
self.locations: list[Location] = [Location(workplane.origin)]
|
||||
self.pending_faces: dict[int : list[Face]] = {0: []}
|
||||
self.pending_edges: dict[int : list[Edge]] = {0: []}
|
||||
self.pending_solids: dict[int : list[Solid]] = {0: []}
|
||||
self.locations: list[Location] = [Location(workplane.origin)]
|
||||
self.last_operation: dict[CqObject : list[Shape]] = {}
|
||||
self.mode = mode
|
||||
self.last_vertices = []
|
||||
self.last_edges = []
|
||||
|
|
@ -72,30 +77,57 @@ class BuildPart:
|
|||
self.last_solids = []
|
||||
|
||||
def __enter__(self):
|
||||
"""Upon entering BuildPart, add current BuildPart instance to context stack"""
|
||||
context_stack.append(self)
|
||||
return self
|
||||
|
||||
def __exit__(self, exception_type, exception_value, traceback):
|
||||
pass
|
||||
"""Upon exiting BuildPart - do nothing"""
|
||||
|
||||
def workplane(self, *workplanes: Plane, replace=True):
|
||||
"""Create Workplane(s)
|
||||
|
||||
Add a sequence of planes as workplanes possibly replacing existing workplanes.
|
||||
|
||||
Args:
|
||||
workplanes (Plane): a sequence of planes to add as workplanes
|
||||
replace (bool, optional): replace existing workplanes. Defaults to True.
|
||||
"""
|
||||
if replace:
|
||||
self.workplanes = []
|
||||
# self.locations: list[Location] = {0: []}
|
||||
for plane in workplanes:
|
||||
self.workplanes.append(plane)
|
||||
# self.locations[len(self.workplanes) - 1] = [Location()]
|
||||
|
||||
def vertices(self, select: Select = Select.ALL) -> VertexList[Vertex]:
|
||||
"""Return Vertices from Part
|
||||
|
||||
Return either all or the vertices created during the last operation.
|
||||
|
||||
Args:
|
||||
select (Select, optional): Vertex selector. Defaults to Select.ALL.
|
||||
|
||||
Returns:
|
||||
VertexList[Vertex]: Vertices extracted
|
||||
"""
|
||||
vertex_list = []
|
||||
if select == Select.ALL:
|
||||
for e in self.part.Edges():
|
||||
vertex_list.extend(e.Vertices())
|
||||
for edge in self.part.Edges():
|
||||
vertex_list.extend(edge.Vertices())
|
||||
elif select == Select.LAST:
|
||||
vertex_list = self.last_vertices
|
||||
return VertexList(set(vertex_list))
|
||||
|
||||
def edges(self, select: Select = Select.ALL) -> ShapeList[Edge]:
|
||||
"""Return Edges from Part
|
||||
|
||||
Return either all or the edges created during the last operation.
|
||||
|
||||
Args:
|
||||
select (Select, optional): Edge selector. Defaults to Select.ALL.
|
||||
|
||||
Returns:
|
||||
ShapeList[Edge]: Edges extracted
|
||||
"""
|
||||
if select == Select.ALL:
|
||||
edge_list = self.part.Edges()
|
||||
elif select == Select.LAST:
|
||||
|
|
@ -103,6 +135,16 @@ class BuildPart:
|
|||
return ShapeList(edge_list)
|
||||
|
||||
def faces(self, select: Select = Select.ALL) -> ShapeList[Face]:
|
||||
"""Return Faces from Part
|
||||
|
||||
Return either all or the faces created during the last operation.
|
||||
|
||||
Args:
|
||||
select (Select, optional): Face selector. Defaults to Select.ALL.
|
||||
|
||||
Returns:
|
||||
ShapeList[Face]: Faces extracted
|
||||
"""
|
||||
if select == Select.ALL:
|
||||
face_list = self.part.Faces()
|
||||
elif select == Select.LAST:
|
||||
|
|
@ -110,6 +152,16 @@ class BuildPart:
|
|||
return ShapeList(face_list)
|
||||
|
||||
def solids(self, select: Select = Select.ALL) -> ShapeList[Solid]:
|
||||
"""Return Solids from Part
|
||||
|
||||
Return either all or the solids created during the last operation.
|
||||
|
||||
Args:
|
||||
select (Select, optional): Solid selector. Defaults to Select.ALL.
|
||||
|
||||
Returns:
|
||||
ShapeList[Solid]: Solids extracted
|
||||
"""
|
||||
if select == Select.ALL:
|
||||
solid_list = self.part.Solids()
|
||||
elif select == Select.LAST:
|
||||
|
|
@ -118,9 +170,12 @@ class BuildPart:
|
|||
|
||||
@staticmethod
|
||||
def get_context() -> "BuildPart":
|
||||
"""Return the current BuildPart instance. Used by Object and Operation
|
||||
classes to refer to the current context."""
|
||||
return context_stack[-1]
|
||||
|
||||
def add_to_pending(self, *objects: Union[Edge, Face]):
|
||||
"""Add objects to BuildPart pending lists"""
|
||||
for obj in objects:
|
||||
for i, workplane in enumerate(self.workplanes):
|
||||
for loc in self.locations:
|
||||
|
|
@ -141,6 +196,25 @@ class BuildPart:
|
|||
*objects: Union[Edge, Wire, Face, Solid, Compound],
|
||||
mode: Mode = Mode.ADDITION,
|
||||
):
|
||||
"""Add objects to BuildPart instance
|
||||
|
||||
Core method to interface with BuildPart instance. Input sequence of objects is
|
||||
parsed into lists of edges, faces, and solids. Edges and faces are added to pending
|
||||
lists. Solids are combined with current part.
|
||||
|
||||
Each operation generates a list of vertices, edges, faces, and solids that have
|
||||
changed during this operation. These lists are only guaranteed to be valid up until
|
||||
the next operation as subsequent operations can element these objects.
|
||||
|
||||
Args:
|
||||
objects (Union[Edge, Wire, Face, Solid, Compound]): sequence of objects to add
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADDITION.
|
||||
|
||||
Raises:
|
||||
ValueError: Nothing to subtract from
|
||||
ValueError: Nothing to intersect with
|
||||
ValueError: Invalid mode
|
||||
"""
|
||||
if context_stack and mode != Mode.PRIVATE:
|
||||
# Sort the provided objects into edges, faces and solids
|
||||
new_faces = [obj for obj in objects if isinstance(obj, Face)]
|
||||
|
|
@ -192,6 +266,7 @@ class BuildPart:
|
|||
self.add_to_pending(*new_faces)
|
||||
|
||||
def get_and_clear_locations(self) -> list:
|
||||
"""Return position and planes from current points and workplanes and clear locations."""
|
||||
position_planes = []
|
||||
for workplane in self.workplanes:
|
||||
position_planes.extend(
|
||||
|
|
@ -200,14 +275,13 @@ class BuildPart:
|
|||
for location in self.locations
|
||||
]
|
||||
)
|
||||
# Clear used locations
|
||||
self.locations = [Location(Vector())]
|
||||
return position_planes
|
||||
|
||||
|
||||
"""
|
||||
Operations
|
||||
"""
|
||||
#
|
||||
# Operations
|
||||
#
|
||||
|
||||
|
||||
class ChamferPart(Compound):
|
||||
|
|
@ -426,7 +500,6 @@ class Loft(Solid):
|
|||
|
||||
|
||||
class PushPointsToPart:
|
||||
def __init__(self, *pts: Union[VectorLike, Location]):
|
||||
"""Part Operation: Push Points
|
||||
|
||||
Push the sequence of tuples, Vectors or Locations to builder internal structure,
|
||||
|
|
@ -435,6 +508,8 @@ class PushPointsToPart:
|
|||
Args:
|
||||
pts (Union[VectorLike, Location]): sequence of points
|
||||
"""
|
||||
|
||||
def __init__(self, *pts: Union[VectorLike, Location]):
|
||||
new_locations = [
|
||||
pt if isinstance(pt, Location) else Location(Vector(pt)) for pt in pts
|
||||
]
|
||||
|
|
@ -546,7 +621,8 @@ class Sweep(Compound):
|
|||
multisection (bool, optional): sweep multiple on path. Defaults to False.
|
||||
make_solid (bool, optional): create solid instead of face. Defaults to True.
|
||||
is_frenet (bool, optional): use freenet algorithm. Defaults to False.
|
||||
transition (Transition, optional): discontinuity handling option. Defaults to Transition.RIGHT.
|
||||
transition (Transition, optional): discontinuity handling option.
|
||||
Defaults to Transition.RIGHT.
|
||||
normal (VectorLike, optional): fixed normal. Defaults to None.
|
||||
binormal (Union[Edge, Wire], optional): guide rotation along path. Defaults to None.
|
||||
mode (Mode, optional): combination. Defaults to Mode.ADDITION.
|
||||
|
|
@ -572,7 +648,7 @@ class Sweep(Compound):
|
|||
binormal_mode = binormal
|
||||
|
||||
new_solids = []
|
||||
for i, workplane in enumerate(BuildPart.get_context().workplanes):
|
||||
for i in range(BuildPart.get_context().workplane_count):
|
||||
if not multisection:
|
||||
for face in BuildPart.get_context().pending_faces[i]:
|
||||
new_solids.append(
|
||||
|
|
@ -620,9 +696,9 @@ class WorkplanesFromFaces:
|
|||
BuildPart.get_context().workplane(*new_planes, replace=replace)
|
||||
|
||||
|
||||
"""
|
||||
Objects
|
||||
"""
|
||||
#
|
||||
# Objects
|
||||
#
|
||||
|
||||
|
||||
class AddToPart(Compound):
|
||||
|
|
@ -681,7 +757,8 @@ class Box(Compound):
|
|||
width (float): box size
|
||||
height (float): box size
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes. Defaults to (True, True, True).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADDITION.
|
||||
"""
|
||||
|
||||
|
|
@ -722,7 +799,8 @@ class Cone(Compound):
|
|||
height (float): cone size
|
||||
arc_size (float, optional): angular size of cone. Defaults to 360.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes. Defaults to (True, True, True).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADDITION.
|
||||
"""
|
||||
|
||||
|
|
@ -768,7 +846,8 @@ class Cylinder(Compound):
|
|||
height (float): cylinder size
|
||||
arc_size (float, optional): angular size of cone. Defaults to 360.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes. Defaults to (True, True, True).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADDITION.
|
||||
"""
|
||||
|
||||
|
|
@ -809,7 +888,8 @@ class Sphere(Compound):
|
|||
arc_size2 (float, optional): angular size of sphere. Defaults to 90.
|
||||
arc_size3 (float, optional): angular size of sphere. Defaults to 360.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes. Defaults to (True, True, True).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADDITION.
|
||||
"""
|
||||
|
||||
|
|
@ -852,7 +932,8 @@ class Torus(Compound):
|
|||
major_arc_size (float, optional): angular size or torus. Defaults to 0.
|
||||
minor_arc_size (float, optional): angular size or torus. Defaults to 360.
|
||||
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes. Defaults to (True, True, True).
|
||||
centered (tuple[bool, bool, bool], optional): center about axes.
|
||||
Defaults to (True, True, True).
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADDITION.
|
||||
"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue