mirror of
https://github.com/gumyr/build123d.git
synced 2026-01-30 20:30:41 -08:00
Merge branch 'algebra' of github.com:gumyr/build123d into algebra
This commit is contained in:
commit
3d61d4bef3
7 changed files with 75 additions and 47 deletions
|
|
@ -738,16 +738,19 @@ class WorkplaneList:
|
|||
- 1 point -> Vector
|
||||
- >1 points -> list[Vector]
|
||||
"""
|
||||
points_per_workplane = []
|
||||
workplane = WorkplaneList._get_context().workplanes[0]
|
||||
localized_pts = [
|
||||
workplane.from_local_coords(pt) if isinstance(pt, tuple) else pt
|
||||
for pt in points
|
||||
]
|
||||
if len(localized_pts) == 1:
|
||||
points_per_workplane.append(localized_pts[0])
|
||||
if WorkplaneList._get_context() is None:
|
||||
points_per_workplane = [Vector(p) for p in points]
|
||||
else:
|
||||
points_per_workplane.extend(localized_pts)
|
||||
points_per_workplane = []
|
||||
workplane = WorkplaneList._get_context().workplanes[0]
|
||||
localized_pts = [
|
||||
workplane.from_local_coords(pt) if isinstance(pt, tuple) else pt
|
||||
for pt in points
|
||||
]
|
||||
if len(localized_pts) == 1:
|
||||
points_per_workplane.append(localized_pts[0])
|
||||
else:
|
||||
points_per_workplane.extend(localized_pts)
|
||||
|
||||
if len(points_per_workplane) == 1:
|
||||
result = points_per_workplane[0]
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ from build123d.geometry import (
|
|||
)
|
||||
from build123d.topology import (
|
||||
Compound,
|
||||
Curve,
|
||||
Edge,
|
||||
Face,
|
||||
ShapeList,
|
||||
|
|
@ -94,7 +95,7 @@ class BuildLine(Builder):
|
|||
):
|
||||
self.initial_plane = workplane
|
||||
self.mode = mode
|
||||
self.line: Compound = None
|
||||
self.line: Curve = None
|
||||
super().__init__(workplane, mode=mode)
|
||||
|
||||
def __exit__(self, exception_type, exception_value, traceback):
|
||||
|
|
@ -183,25 +184,34 @@ class BuildLine(Builder):
|
|||
if self.line:
|
||||
self.line = self.line.fuse(*new_edges)
|
||||
else:
|
||||
self.line = Compound.make_compound(new_edges)
|
||||
self.line = Curve(Compound.make_compound(new_edges).wrapped)
|
||||
elif mode == Mode.SUBTRACT:
|
||||
if self.line is None:
|
||||
raise RuntimeError("No line to subtract from")
|
||||
self.line = self.line.cut(*new_edges)
|
||||
elif mode == Mode.INTERSECT:
|
||||
if self.line is None:
|
||||
raise RuntimeError("No line to intersect with")
|
||||
self.line = self.line.intersect(*new_edges)
|
||||
elif mode == Mode.REPLACE:
|
||||
self.line = Compound.make_compound(new_edges)
|
||||
self.line = Curve(Compound.make_compound(new_edges).wrapped)
|
||||
self.last_edges = ShapeList(new_edges)
|
||||
self.last_vertices = ShapeList(
|
||||
set(v for e in self.last_edges for v in e.vertices())
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_context(cls, caller=None) -> "BuildLine":
|
||||
def _get_context(cls, caller=None) -> BuildLine:
|
||||
"""Return the instance of the current builder"""
|
||||
|
||||
result = cls._current.get(None)
|
||||
if caller is not None and result is None:
|
||||
if hasattr(caller, "_applies_to"):
|
||||
raise RuntimeError(
|
||||
f"No valid context found, use one of {caller._applies_to}"
|
||||
)
|
||||
raise RuntimeError("No valid context found")
|
||||
# print(f"{result=}, {caller=}")
|
||||
# if caller is not None and result is None:
|
||||
# if hasattr(caller, "_applies_to"):
|
||||
# raise RuntimeError(
|
||||
# f"No valid context found, use one of {caller._applies_to}"
|
||||
# )
|
||||
# raise RuntimeError("No valid context found")
|
||||
|
||||
logger.info(
|
||||
"Context requested by %s",
|
||||
|
|
@ -238,7 +248,8 @@ class BaseLineObject(Wire):
|
|||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
|
||||
context._add_to_context(*curve.edges(), mode=mode)
|
||||
if context is not None:
|
||||
context._add_to_context(*curve.edges(), mode=mode)
|
||||
|
||||
if isinstance(curve, Edge):
|
||||
super().__init__(Wire.make_wire([curve]).wrapped)
|
||||
|
|
@ -268,7 +279,8 @@ class Bezier(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
polls = WorkplaneList.localize(*cntl_pnts)
|
||||
curve = Edge.make_bezier(*polls, weights=weights)
|
||||
|
|
@ -300,7 +312,8 @@ class CenterArc(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
center_point = WorkplaneList.localize(center)
|
||||
circle_workplane = copy.copy(WorkplaneList._get_context().workplanes[0])
|
||||
|
|
@ -484,7 +497,8 @@ class EllipticalCenterArc(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
center_pnt = WorkplaneList.localize(center)
|
||||
ellipse_workplane = copy.copy(WorkplaneList._get_context().workplanes[0])
|
||||
|
|
@ -533,7 +547,8 @@ class Helix(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
center_pnt = WorkplaneList.localize(center)
|
||||
helix = Wire.make_helix(
|
||||
|
|
@ -566,7 +581,8 @@ class JernArc(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
start = WorkplaneList.localize(start)
|
||||
self.start = start
|
||||
|
|
@ -602,11 +618,13 @@ class Line(BaseLineObject):
|
|||
_applies_to = [BuildLine._tag()]
|
||||
|
||||
def __init__(self, *pts: VectorLike, mode: Mode = Mode.ADD):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
|
||||
if len(pts) != 2:
|
||||
raise ValueError("Line requires two pts")
|
||||
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
pts = WorkplaneList.localize(*pts)
|
||||
|
||||
lines_pts = [Vector(p) for p in pts]
|
||||
|
|
@ -644,7 +662,8 @@ class PolarLine(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
start = WorkplaneList.localize(start)
|
||||
if direction:
|
||||
|
|
@ -692,7 +711,8 @@ class Polyline(BaseLineObject):
|
|||
|
||||
def __init__(self, *pts: VectorLike, close: bool = False, mode: Mode = Mode.ADD):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
if len(pts) < 3:
|
||||
raise ValueError("polyline requires three or more pts")
|
||||
|
|
@ -734,7 +754,8 @@ class RadiusArc(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
start, end = WorkplaneList.localize(start_point, end_point)
|
||||
# Calculate the sagitta from the radius
|
||||
|
|
@ -777,7 +798,8 @@ class SagittaArc(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
start, end = WorkplaneList.localize(start_point, end_point)
|
||||
mid_point = (end + start) * 0.5
|
||||
|
|
@ -819,7 +841,8 @@ class Spline(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
spline_pts = WorkplaneList.localize(*pts)
|
||||
|
||||
|
|
@ -875,7 +898,8 @@ class TangentArc(BaseLineObject):
|
|||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
if len(pts) != 2:
|
||||
raise ValueError("tangent_arc requires two points")
|
||||
|
|
@ -907,7 +931,8 @@ class ThreePointArc(BaseLineObject):
|
|||
|
||||
def __init__(self, *pts: VectorLike, mode: Mode = Mode.ADD):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context.validate_inputs(self)
|
||||
if context is not None:
|
||||
context.validate_inputs(self)
|
||||
|
||||
if len(pts) != 3:
|
||||
raise ValueError("ThreePointArc requires three points")
|
||||
|
|
|
|||
|
|
@ -300,9 +300,7 @@ class BasePartObject(Part):
|
|||
]
|
||||
context._add_to_context(*new_solids, mode=mode)
|
||||
|
||||
super().__init__(
|
||||
Compound.make_compound(new_solids).wrapped, is_alg=(context == None)
|
||||
)
|
||||
super().__init__(Compound.make_compound(new_solids).wrapped)
|
||||
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -340,9 +340,7 @@ class BaseSketchObject(Sketch):
|
|||
]
|
||||
context._add_to_context(*new_faces, mode=mode)
|
||||
|
||||
super().__init__(
|
||||
Compound.make_compound(new_faces).wrapped, is_alg=(context == None)
|
||||
)
|
||||
super().__init__(Compound.make_compound(new_faces).wrapped)
|
||||
|
||||
|
||||
class Circle(BaseSketchObject):
|
||||
|
|
|
|||
|
|
@ -1667,7 +1667,7 @@ class Plane:
|
|||
if isinstance(other, Location):
|
||||
return Plane(self.to_location() * other)
|
||||
|
||||
elif hasattr(other, "wrapped"): # Shape
|
||||
elif hasattr(other, "wrapped") and not isinstance(other, Vector): # Shape
|
||||
return self.to_location() * other
|
||||
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1098,7 +1098,6 @@ class Shape(NodeMixin):
|
|||
joints: dict[str, Joint] = None,
|
||||
parent: Compound = None,
|
||||
children: list[Shape] = None,
|
||||
is_alg: bool = True,
|
||||
):
|
||||
self.wrapped = downcast(obj) if obj else None
|
||||
self.for_construction = False
|
||||
|
|
@ -1118,6 +1117,10 @@ class Shape(NodeMixin):
|
|||
# parent must be set following children as post install accesses children
|
||||
self.parent = parent
|
||||
|
||||
# Faces can optionally record the plane it was created on for later extrusion
|
||||
if isinstance(self, Face):
|
||||
self.created_on: Plane = None
|
||||
|
||||
@property
|
||||
def location(self) -> Location:
|
||||
"""Get this Shape's Location"""
|
||||
|
|
@ -2865,9 +2868,10 @@ class ShapeList(list[T]):
|
|||
return ShapeList(list(self) + list(other))
|
||||
|
||||
def __sub__(self, other: ShapeList) -> ShapeList:
|
||||
hash_other = [hash(o) for o in other]
|
||||
hash_set = {hash(o): o for o in self if hash(o) not in hash_other}
|
||||
return ShapeList(hash_set.values())
|
||||
# hash_other = [hash(o) for o in other]
|
||||
# hash_set = {hash(o): o for o in self if hash(o) not in hash_other}
|
||||
# return ShapeList(hash_set.values())
|
||||
return ShapeList(set(self) - set(other))
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return slices of ShapeList as ShapeList"""
|
||||
|
|
|
|||
|
|
@ -1861,7 +1861,7 @@ class TestPlane(DirectApiTestCase):
|
|||
self.assertVectorAlmostEquals(
|
||||
p2.z_dir, (0, -math.sqrt(2) / 2, math.sqrt(2) / 2), 6
|
||||
)
|
||||
with self.assertRaises(AttributeError):
|
||||
with self.assertRaises(TypeError):
|
||||
p2 * Vector(1, 1, 1)
|
||||
|
||||
def test_plane_methods(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue