Merge branch 'algebra' of github.com:gumyr/build123d into algebra

This commit is contained in:
Bernhard 2023-03-19 19:38:52 +01:00
commit 3d61d4bef3
7 changed files with 75 additions and 47 deletions

View file

@ -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]

View file

@ -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")

View file

@ -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)
#

View file

@ -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):

View file

@ -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:

View file

@ -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"""

View file

@ -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):