Changing base types Issue #792

This commit is contained in:
gumyr 2024-11-17 11:41:14 -05:00
parent e06337a0e8
commit 60b244442d
2 changed files with 78 additions and 43 deletions

View file

@ -40,35 +40,55 @@ from build123d.geometry import Axis, Plane, Vector, VectorLike, TOLERANCE
from build123d.topology import Edge, Face, Wire, Curve from build123d.topology import Edge, Face, Wire, Curve
class BaseLineObject(Wire): def _add_curve_to_context(curve, mode: Mode):
"""BaseLineObject """Helper function to add a curve to the context.
Base class for all BuildLine objects
Args: Args:
curve (Union[Edge,Wire]): edge to create curve (Union[Wire, Edge]): curve to add to the context (either a Wire or an Edge).
mode (Mode): combination mode.
"""
context: BuildLine = BuildLine._get_context(log=False)
if context is not None and isinstance(context, BuildLine):
if isinstance(curve, Wire):
context._add_to_context(*curve.edges(), mode=mode)
elif isinstance(curve, Edge):
context._add_to_context(curve, mode=mode)
class BaseLineObject(Wire):
"""BaseLineObject specialized for Wire.
Args:
curve (Wire): wire to create.
mode (Mode, optional): combination mode. Defaults to Mode.ADD. mode (Mode, optional): combination mode. Defaults to Mode.ADD.
""" """
_applies_to = [BuildLine._tag] _applies_to = [BuildLine._tag]
def __init__( def __init__(self, curve: Wire, mode: Mode = Mode.ADD):
self, # Use the helper function to handle adding the curve to the context
curve: Union[Edge, Wire], _add_curve_to_context(curve, mode)
mode: Mode = Mode.ADD, super().__init__(curve.wrapped)
):
context: BuildLine = BuildLine._get_context(self, log=False)
if context is not None and isinstance(context, BuildLine):
context._add_to_context(*curve.edges(), mode=mode)
if isinstance(curve, Edge):
super().__init__(Wire([curve]).wrapped)
else:
super().__init__(curve.wrapped)
class Bezier(BaseLineObject): class BaseEdgeObject(Edge):
"""BaseEdgeObject specialized for Edge.
Args:
curve (Edge): edge to create.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
_applies_to = [BuildLine._tag]
def __init__(self, curve: Edge, mode: Mode = Mode.ADD):
# Use the helper function to handle adding the curve to the context
_add_curve_to_context(curve, mode)
super().__init__(curve.wrapped)
class Bezier(BaseEdgeObject):
"""Line Object: Bezier Curve """Line Object: Bezier Curve
Create a rational (with weights) or non-rational bezier curve. The first and last Create a rational (with weights) or non-rational bezier curve. The first and last
@ -99,7 +119,7 @@ class Bezier(BaseLineObject):
super().__init__(curve, mode=mode) super().__init__(curve, mode=mode)
class CenterArc(BaseLineObject): class CenterArc(BaseEdgeObject):
"""Line Object: Center Arc """Line Object: Center Arc
Add center arc to the line. Add center arc to the line.
@ -150,7 +170,7 @@ class CenterArc(BaseLineObject):
super().__init__(arc, mode=mode) super().__init__(arc, mode=mode)
class DoubleTangentArc(BaseLineObject): class DoubleTangentArc(BaseEdgeObject):
"""Line Object: Double Tangent Arc """Line Object: Double Tangent Arc
Create an arc defined by a point/tangent pair and another line which the other end Create an arc defined by a point/tangent pair and another line which the other end
@ -249,7 +269,7 @@ class DoubleTangentArc(BaseLineObject):
super().__init__(double.wire(), mode=mode) super().__init__(double.wire(), mode=mode)
class EllipticalStartArc(BaseLineObject): class EllipticalStartArc(BaseEdgeObject):
"""Line Object: Elliptical Start Arc """Line Object: Elliptical Start Arc
Makes an arc of an ellipse from the start point. Makes an arc of an ellipse from the start point.
@ -355,7 +375,7 @@ class EllipticalStartArc(BaseLineObject):
# context: BuildLine = BuildLine._get_context(self) # context: BuildLine = BuildLine._get_context(self)
class EllipticalCenterArc(BaseLineObject): class EllipticalCenterArc(BaseEdgeObject):
"""Line Object: Elliptical Center Arc """Line Object: Elliptical Center Arc
Makes an arc of an ellipse from a center point. Makes an arc of an ellipse from a center point.
@ -409,7 +429,7 @@ class EllipticalCenterArc(BaseLineObject):
super().__init__(curve, mode=mode) super().__init__(curve, mode=mode)
class Helix(BaseLineObject): class Helix(BaseEdgeObject):
"""Line Object: Helix """Line Object: Helix
Add a helix to the line. Add a helix to the line.
@ -545,7 +565,7 @@ class FilletPolyline(BaseLineObject):
super().__init__(new_wire, mode=mode) super().__init__(new_wire, mode=mode)
class JernArc(BaseLineObject): class JernArc(BaseEdgeObject):
"""JernArc """JernArc
Circular tangent arc with given radius and arc_size Circular tangent arc with given radius and arc_size
@ -605,7 +625,7 @@ class JernArc(BaseLineObject):
super().__init__(arc, mode=mode) super().__init__(arc, mode=mode)
class Line(BaseLineObject): class Line(BaseEdgeObject):
"""Line Object: Line """Line Object: Line
Add a straight line defined by two end points. Add a straight line defined by two end points.
@ -638,7 +658,7 @@ class Line(BaseLineObject):
super().__init__(new_edge, mode=mode) super().__init__(new_edge, mode=mode)
class IntersectingLine(BaseLineObject): class IntersectingLine(BaseEdgeObject):
"""Intersecting Line Object: Line """Intersecting Line Object: Line
Add a straight line that intersects another line at a given parameter and angle. Add a straight line that intersects another line at a given parameter and angle.
@ -679,7 +699,7 @@ class IntersectingLine(BaseLineObject):
super().__init__(new_edge, mode=mode) super().__init__(new_edge, mode=mode)
class PolarLine(BaseLineObject): class PolarLine(BaseEdgeObject):
"""Line Object: Polar Line """Line Object: Polar Line
Add line defined by a start point, length and angle. Add line defined by a start point, length and angle.
@ -780,7 +800,7 @@ class Polyline(BaseLineObject):
super().__init__(Wire.combine(new_edges)[0], mode=mode) super().__init__(Wire.combine(new_edges)[0], mode=mode)
class RadiusArc(BaseLineObject): class RadiusArc(BaseEdgeObject):
"""Line Object: Radius Arc """Line Object: Radius Arc
Add an arc defined by two end points and a radius Add an arc defined by two end points and a radius
@ -832,7 +852,7 @@ class RadiusArc(BaseLineObject):
super().__init__(arc, mode=mode) super().__init__(arc, mode=mode)
class SagittaArc(BaseLineObject): class SagittaArc(BaseEdgeObject):
"""Line Object: Sagitta Arc """Line Object: Sagitta Arc
Add an arc defined by two points and the height of the arc (sagitta). Add an arc defined by two points and the height of the arc (sagitta).
@ -874,7 +894,7 @@ class SagittaArc(BaseLineObject):
super().__init__(arc, mode=mode) super().__init__(arc, mode=mode)
class Spline(BaseLineObject): class Spline(BaseEdgeObject):
"""Line Object: Spline """Line Object: Spline
Add a spline through the provided points optionally constrained by tangents. Add a spline through the provided points optionally constrained by tangents.
@ -932,7 +952,7 @@ class Spline(BaseLineObject):
super().__init__(spline, mode=mode) super().__init__(spline, mode=mode)
class TangentArc(BaseLineObject): class TangentArc(BaseEdgeObject):
"""Line Object: Tangent Arc """Line Object: Tangent Arc
Add an arc defined by two points and a tangent. Add an arc defined by two points and a tangent.
@ -974,7 +994,7 @@ class TangentArc(BaseLineObject):
super().__init__(arc, mode=mode) super().__init__(arc, mode=mode)
class ThreePointArc(BaseLineObject): class ThreePointArc(BaseEdgeObject):
"""Line Object: Three Point Arc """Line Object: Three Point Arc
Add an arc generated by three points. Add an arc generated by three points.

View file

@ -104,8 +104,9 @@ class BuildLineTests(unittest.TestCase):
pts = [(0, 0), (20, 20), (40, 0), (0, -40), (-60, 0), (0, 100), (100, 0)] pts = [(0, 0), (20, 20), (40, 0), (0, -40), (-60, 0), (0, 100), (100, 0)]
wts = [1.0, 1.0, 2.0, 3.0, 4.0, 2.0, 1.0] wts = [1.0, 1.0, 2.0, 3.0, 4.0, 2.0, 1.0]
with BuildLine() as bz: with BuildLine() as bz:
Bezier(*pts, weights=wts) b1 = Bezier(*pts, weights=wts)
self.assertAlmostEqual(bz.wires()[0].length, 225.86389406824566, 5) self.assertAlmostEqual(bz.wires()[0].length, 225.86389406824566, 5)
self.assertTrue(isinstance(b1, Edge))
def test_double_tangent_arc(self): def test_double_tangent_arc(self):
l1 = Line((10, 0), (30, 20)) l1 = Line((10, 0), (30, 20))
@ -140,6 +141,7 @@ class BuildLineTests(unittest.TestCase):
l9 = EllipticalCenterArc((15, 0), 10, 5, start_angle=90, end_angle=270) l9 = EllipticalCenterArc((15, 0), 10, 5, start_angle=90, end_angle=270)
l10 = DoubleTangentArc((0, 0, 0), (1, 0, 0), l9, keep=Keep.BOTH) l10 = DoubleTangentArc((0, 0, 0), (1, 0, 0), l9, keep=Keep.BOTH)
self.assertEqual(len(l10.edges()), 2) self.assertEqual(len(l10.edges()), 2)
self.assertTrue(isinstance(l10, Edge))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
DoubleTangentArc((0, 0, 0), (0, 0, 1), l9) DoubleTangentArc((0, 0, 0), (0, 0, 1), l9)
@ -168,6 +170,7 @@ class BuildLineTests(unittest.TestCase):
self.assertGreaterEqual(bbox.min.Y, 0) self.assertGreaterEqual(bbox.min.Y, 0)
self.assertLessEqual(bbox.max.X, 10) self.assertLessEqual(bbox.max.X, 10)
self.assertLessEqual(bbox.max.Y, 5) self.assertLessEqual(bbox.max.Y, 5)
self.assertTrue(isinstance(e1, Edge))
def test_filletpolyline(self): def test_filletpolyline(self):
with BuildLine(Plane.YZ): with BuildLine(Plane.YZ):
@ -185,6 +188,7 @@ class BuildLineTests(unittest.TestCase):
self.assertEqual(len(p.edges()), 8) self.assertEqual(len(p.edges()), 8)
self.assertEqual(len(p.edges().filter_by(GeomType.CIRCLE)), 4) self.assertEqual(len(p.edges().filter_by(GeomType.CIRCLE)), 4)
self.assertEqual(len(p.edges().filter_by(GeomType.LINE)), 4) self.assertEqual(len(p.edges().filter_by(GeomType.LINE)), 4)
self.assertTrue(isinstance(p, Wire))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
FilletPolyline((0, 0), radius=0.1) FilletPolyline((0, 0), radius=0.1)
@ -200,6 +204,7 @@ class BuildLineTests(unittest.TestCase):
l3 = Line((0, 0), (10, 10)) l3 = Line((0, 0), (10, 10))
l4 = IntersectingLine((0, 10), (1, -1), l3) l4 = IntersectingLine((0, 10), (1, -1), l3)
self.assertTupleAlmostEquals((l4 @ 1).to_tuple(), (5, 5, 0), 5) self.assertTupleAlmostEquals((l4 @ 1).to_tuple(), (5, 5, 0), 5)
self.assertTrue(isinstance(l4, Edge))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
IntersectingLine((0, 10), (1, 1), l3) IntersectingLine((0, 10), (1, 1), l3)
@ -209,33 +214,36 @@ class BuildLineTests(unittest.TestCase):
j1 = JernArc((1, 0), (0, 1), 1, 90) j1 = JernArc((1, 0), (0, 1), 1, 90)
self.assertTupleAlmostEquals((jern.line @ 1).to_tuple(), (0, 1, 0), 5) self.assertTupleAlmostEquals((jern.line @ 1).to_tuple(), (0, 1, 0), 5)
self.assertAlmostEqual(j1.radius, 1) self.assertAlmostEqual(j1.radius, 1)
self.assertAlmostEqual(j1.length, pi/2) self.assertAlmostEqual(j1.length, pi / 2)
with BuildLine(Plane.XY.offset(1)) as offset_l: with BuildLine(Plane.XY.offset(1)) as offset_l:
off1 = JernArc((1, 0), (0, 1), 1, 90) off1 = JernArc((1, 0), (0, 1), 1, 90)
self.assertTupleAlmostEquals((offset_l.line @ 1).to_tuple(), (0, 1, 1), 5) self.assertTupleAlmostEquals((offset_l.line @ 1).to_tuple(), (0, 1, 1), 5)
self.assertAlmostEqual(off1.radius, 1) self.assertAlmostEqual(off1.radius, 1)
self.assertAlmostEqual(off1.length, pi/2) self.assertAlmostEqual(off1.length, pi / 2)
plane_iso = Plane(origin=(0, 0, 0), x_dir=(1, 1, 0), z_dir=(1, -1, 1)) plane_iso = Plane(origin=(0, 0, 0), x_dir=(1, 1, 0), z_dir=(1, -1, 1))
with BuildLine(plane_iso) as iso_l: with BuildLine(plane_iso) as iso_l:
iso1 = JernArc((0, 0), (0, 1), 1, 180) iso1 = JernArc((0, 0), (0, 1), 1, 180)
self.assertTupleAlmostEquals((iso_l.line @ 1).to_tuple(), (-sqrt(2), -sqrt(2), 0), 5) self.assertTupleAlmostEquals(
(iso_l.line @ 1).to_tuple(), (-sqrt(2), -sqrt(2), 0), 5
)
self.assertAlmostEqual(iso1.radius, 1) self.assertAlmostEqual(iso1.radius, 1)
self.assertAlmostEqual(iso1.length, pi) self.assertAlmostEqual(iso1.length, pi)
with BuildLine() as full_l: with BuildLine() as full_l:
l1 = JernArc(start=(0, 0, 0), tangent=(1, 0, 0), radius=1, arc_size=360) l1 = JernArc(start=(0, 0, 0), tangent=(1, 0, 0), radius=1, arc_size=360)
l2 = JernArc(start=(0, 0, 0), tangent=(1, 0, 0), radius=1, arc_size=300) l2 = JernArc(start=(0, 0, 0), tangent=(1, 0, 0), radius=1, arc_size=300)
self.assertTrue(l1.is_closed) self.assertTrue(l1.is_closed)
self.assertFalse(l2.is_closed) self.assertFalse(l2.is_closed)
circle_face = Face(l1) circle_face = Face(Wire([l1]))
self.assertAlmostEqual(circle_face.area, pi, 5) self.assertAlmostEqual(circle_face.area, pi, 5)
self.assertTupleAlmostEquals(circle_face.center().to_tuple(), (0, 1, 0), 5) self.assertTupleAlmostEquals(circle_face.center().to_tuple(), (0, 1, 0), 5)
self.assertTupleAlmostEquals(l1.vertex().to_tuple(), l2.start.to_tuple(), 5) self.assertTupleAlmostEquals(l1.vertex().to_tuple(), l2.start.to_tuple(), 5)
l1 = JernArc((0, 0), (1, 0), 1, 90) l1 = JernArc((0, 0), (1, 0), 1, 90)
self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (1, 1, 0), 5) self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (1, 1, 0), 5)
self.assertTrue(isinstance(l1, Edge))
def test_polar_line(self): def test_polar_line(self):
"""Test 2D and 3D polar lines""" """Test 2D and 3D polar lines"""
@ -267,6 +275,7 @@ class BuildLineTests(unittest.TestCase):
l1 = PolarLine((0, 0), 10, direction=(1, 1)) l1 = PolarLine((0, 0), 10, direction=(1, 1))
self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (10, 10, 0), 5) self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (10, 10, 0), 5)
self.assertTrue(isinstance(l1, Edge))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
PolarLine((0, 0), 1) PolarLine((0, 0), 1)
@ -274,8 +283,9 @@ class BuildLineTests(unittest.TestCase):
def test_spline(self): def test_spline(self):
"""Test spline with no tangents""" """Test spline with no tangents"""
with BuildLine() as test: with BuildLine() as test:
Spline((0, 0), (1, 1), (2, 0)) s1 = Spline((0, 0), (1, 1), (2, 0))
self.assertTupleAlmostEquals((test.edges()[0] @ 1).to_tuple(), (2, 0, 0), 5) self.assertTupleAlmostEquals((test.edges()[0] @ 1).to_tuple(), (2, 0, 0), 5)
self.assertTrue(isinstance(s1, Edge))
def test_radius_arc(self): def test_radius_arc(self):
"""Test center arc as arc and circle""" """Test center arc as arc and circle"""
@ -304,9 +314,12 @@ class BuildLineTests(unittest.TestCase):
self.assertAlmostEqual(arc4.length, 2 * r * pi * 0.6, 6) self.assertAlmostEqual(arc4.length, 2 * r * pi * 0.6, 6)
self.assertGreater(arc4.bounding_box().max.X, c.bounding_box().max.X) self.assertGreater(arc4.bounding_box().max.X, c.bounding_box().max.X)
self.assertTrue(isinstance(arc1, Edge))
def test_sagitta_arc(self): def test_sagitta_arc(self):
l1 = SagittaArc((0, 0), (1, 0), 0.1) l1 = SagittaArc((0, 0), (1, 0), 0.1)
self.assertAlmostEqual((l1 @ 0.5).Y, 0.1, 5) self.assertAlmostEqual((l1 @ 0.5).Y, 0.1, 5)
self.assertTrue(isinstance(l1, Edge))
def test_center_arc(self): def test_center_arc(self):
"""Test center arc as arc and circle""" """Test center arc as arc and circle"""
@ -327,18 +340,20 @@ class BuildLineTests(unittest.TestCase):
self.assertTupleAlmostEquals((arc.edges()[0] @ 0.5).to_tuple(), (0, 0, 0), 5) self.assertTupleAlmostEquals((arc.edges()[0] @ 0.5).to_tuple(), (0, 0, 0), 5)
arc = CenterArc((-100, 0), 100, 0, 360) arc = CenterArc((-100, 0), 100, 0, 360)
self.assertTrue(Face(arc.wires()[0]).is_coplanar(Plane.XY)) self.assertTrue(Face(Wire([arc])).is_coplanar(Plane.XY))
self.assertTupleAlmostEquals(arc.bounding_box().max, (0, 100, 0), 5) self.assertTupleAlmostEquals(arc.bounding_box().max, (0, 100, 0), 5)
self.assertTrue(isinstance(arc, Edge))
def test_polyline(self): def test_polyline(self):
"""Test edge generation and close""" """Test edge generation and close"""
with BuildLine() as test: with BuildLine() as test:
Polyline((0, 0), (1, 0), (1, 1), (0, 1), close=True) p1 = Polyline((0, 0), (1, 0), (1, 1), (0, 1), close=True)
self.assertAlmostEqual( self.assertAlmostEqual(
(test.edges()[0] @ 0 - test.edges()[-1] @ 1).length, 0, 5 (test.edges()[0] @ 0 - test.edges()[-1] @ 1).length, 0, 5
) )
self.assertEqual(len(test.edges()), 4) self.assertEqual(len(test.edges()), 4)
self.assertAlmostEqual(test.wires()[0].length, 4) self.assertAlmostEqual(test.wires()[0].length, 4)
self.assertTrue(isinstance(p1, Wire))
def test_polyline_with_list(self): def test_polyline_with_list(self):
"""Test edge generation and close""" """Test edge generation and close"""