Skip to content

Commit

Permalink
Changing base types Issue #792
Browse files Browse the repository at this point in the history
  • Loading branch information
gumyr committed Nov 17, 2024
1 parent e06337a commit 60b2444
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 40 deletions.
82 changes: 51 additions & 31 deletions src/build123d/objects_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,55 @@
from build123d.topology import Edge, Face, Wire, Curve


class BaseLineObject(Wire):
"""BaseLineObject
def _add_curve_to_context(curve, mode: Mode):
"""Helper function to add a curve to the context.
Args:
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)

Base class for all BuildLine objects

class BaseLineObject(Wire):
"""BaseLineObject specialized for Wire.
Args:
curve (Union[Edge,Wire]): edge to create
curve (Wire): wire to create.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""

_applies_to = [BuildLine._tag]

def __init__(
self,
curve: Union[Edge, Wire],
mode: Mode = Mode.ADD,
):
context: BuildLine = BuildLine._get_context(self, log=False)
def __init__(self, curve: Wire, 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)

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 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(BaseLineObject):
class Bezier(BaseEdgeObject):
"""Line Object: Bezier Curve
Create a rational (with weights) or non-rational bezier curve. The first and last
Expand Down Expand Up @@ -99,7 +119,7 @@ def __init__(
super().__init__(curve, mode=mode)


class CenterArc(BaseLineObject):
class CenterArc(BaseEdgeObject):
"""Line Object: Center Arc
Add center arc to the line.
Expand Down Expand Up @@ -150,7 +170,7 @@ def __init__(
super().__init__(arc, mode=mode)


class DoubleTangentArc(BaseLineObject):
class DoubleTangentArc(BaseEdgeObject):
"""Line Object: Double Tangent Arc
Create an arc defined by a point/tangent pair and another line which the other end
Expand Down Expand Up @@ -249,7 +269,7 @@ def func(radius, perpendicular_bisector):
super().__init__(double.wire(), mode=mode)


class EllipticalStartArc(BaseLineObject):
class EllipticalStartArc(BaseEdgeObject):
"""Line Object: Elliptical Start Arc
Makes an arc of an ellipse from the start point.
Expand Down Expand Up @@ -355,7 +375,7 @@ def __init__(
# context: BuildLine = BuildLine._get_context(self)


class EllipticalCenterArc(BaseLineObject):
class EllipticalCenterArc(BaseEdgeObject):
"""Line Object: Elliptical Center Arc
Makes an arc of an ellipse from a center point.
Expand Down Expand Up @@ -409,7 +429,7 @@ def __init__(
super().__init__(curve, mode=mode)


class Helix(BaseLineObject):
class Helix(BaseEdgeObject):
"""Line Object: Helix
Add a helix to the line.
Expand Down Expand Up @@ -545,7 +565,7 @@ def __init__(
super().__init__(new_wire, mode=mode)


class JernArc(BaseLineObject):
class JernArc(BaseEdgeObject):
"""JernArc
Circular tangent arc with given radius and arc_size
Expand Down Expand Up @@ -605,7 +625,7 @@ def __init__(
super().__init__(arc, mode=mode)


class Line(BaseLineObject):
class Line(BaseEdgeObject):
"""Line Object: Line
Add a straight line defined by two end points.
Expand Down Expand Up @@ -638,7 +658,7 @@ def __init__(
super().__init__(new_edge, mode=mode)


class IntersectingLine(BaseLineObject):
class IntersectingLine(BaseEdgeObject):
"""Intersecting Line Object: Line
Add a straight line that intersects another line at a given parameter and angle.
Expand Down Expand Up @@ -679,7 +699,7 @@ def __init__(
super().__init__(new_edge, mode=mode)


class PolarLine(BaseLineObject):
class PolarLine(BaseEdgeObject):
"""Line Object: Polar Line
Add line defined by a start point, length and angle.
Expand Down Expand Up @@ -780,7 +800,7 @@ def __init__(
super().__init__(Wire.combine(new_edges)[0], mode=mode)


class RadiusArc(BaseLineObject):
class RadiusArc(BaseEdgeObject):
"""Line Object: Radius Arc
Add an arc defined by two end points and a radius
Expand Down Expand Up @@ -832,7 +852,7 @@ def __init__(
super().__init__(arc, mode=mode)


class SagittaArc(BaseLineObject):
class SagittaArc(BaseEdgeObject):
"""Line Object: Sagitta Arc
Add an arc defined by two points and the height of the arc (sagitta).
Expand Down Expand Up @@ -874,7 +894,7 @@ def __init__(
super().__init__(arc, mode=mode)


class Spline(BaseLineObject):
class Spline(BaseEdgeObject):
"""Line Object: Spline
Add a spline through the provided points optionally constrained by tangents.
Expand Down Expand Up @@ -932,7 +952,7 @@ def __init__(
super().__init__(spline, mode=mode)


class TangentArc(BaseLineObject):
class TangentArc(BaseEdgeObject):
"""Line Object: Tangent Arc
Add an arc defined by two points and a tangent.
Expand Down Expand Up @@ -974,7 +994,7 @@ def __init__(
super().__init__(arc, mode=mode)


class ThreePointArc(BaseLineObject):
class ThreePointArc(BaseEdgeObject):
"""Line Object: Three Point Arc
Add an arc generated by three points.
Expand Down
33 changes: 24 additions & 9 deletions tests/test_build_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ def test_bezier(self):
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]
with BuildLine() as bz:
Bezier(*pts, weights=wts)
b1 = Bezier(*pts, weights=wts)
self.assertAlmostEqual(bz.wires()[0].length, 225.86389406824566, 5)
self.assertTrue(isinstance(b1, Edge))

def test_double_tangent_arc(self):
l1 = Line((10, 0), (30, 20))
Expand Down Expand Up @@ -140,6 +141,7 @@ def test_double_tangent_arc(self):
l9 = EllipticalCenterArc((15, 0), 10, 5, start_angle=90, end_angle=270)
l10 = DoubleTangentArc((0, 0, 0), (1, 0, 0), l9, keep=Keep.BOTH)
self.assertEqual(len(l10.edges()), 2)
self.assertTrue(isinstance(l10, Edge))

with self.assertRaises(ValueError):
DoubleTangentArc((0, 0, 0), (0, 0, 1), l9)
Expand Down Expand Up @@ -168,6 +170,7 @@ def test_elliptical_center_arc(self):
self.assertGreaterEqual(bbox.min.Y, 0)
self.assertLessEqual(bbox.max.X, 10)
self.assertLessEqual(bbox.max.Y, 5)
self.assertTrue(isinstance(e1, Edge))

def test_filletpolyline(self):
with BuildLine(Plane.YZ):
Expand All @@ -185,6 +188,7 @@ def test_filletpolyline(self):
self.assertEqual(len(p.edges()), 8)
self.assertEqual(len(p.edges().filter_by(GeomType.CIRCLE)), 4)
self.assertEqual(len(p.edges().filter_by(GeomType.LINE)), 4)
self.assertTrue(isinstance(p, Wire))

with self.assertRaises(ValueError):
FilletPolyline((0, 0), radius=0.1)
Expand All @@ -200,6 +204,7 @@ def test_intersecting_line(self):
l3 = Line((0, 0), (10, 10))
l4 = IntersectingLine((0, 10), (1, -1), l3)
self.assertTupleAlmostEquals((l4 @ 1).to_tuple(), (5, 5, 0), 5)
self.assertTrue(isinstance(l4, Edge))

with self.assertRaises(ValueError):
IntersectingLine((0, 10), (1, 1), l3)
Expand All @@ -209,33 +214,36 @@ def test_jern_arc(self):
j1 = JernArc((1, 0), (0, 1), 1, 90)
self.assertTupleAlmostEquals((jern.line @ 1).to_tuple(), (0, 1, 0), 5)
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:
off1 = JernArc((1, 0), (0, 1), 1, 90)
self.assertTupleAlmostEquals((offset_l.line @ 1).to_tuple(), (0, 1, 1), 5)
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))
with BuildLine(plane_iso) as iso_l:
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.length, pi)

with BuildLine() as full_l:
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)
self.assertTrue(l1.is_closed)
self.assertFalse(l2.is_closed)
circle_face = Face(l1)
circle_face = Face(Wire([l1]))
self.assertAlmostEqual(circle_face.area, pi, 5)
self.assertTupleAlmostEquals(circle_face.center().to_tuple(), (0, 1, 0), 5)
self.assertTupleAlmostEquals(l1.vertex().to_tuple(), l2.start.to_tuple(), 5)

l1 = JernArc((0, 0), (1, 0), 1, 90)
self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (1, 1, 0), 5)
self.assertTrue(isinstance(l1, Edge))

def test_polar_line(self):
"""Test 2D and 3D polar lines"""
Expand Down Expand Up @@ -267,15 +275,17 @@ def test_polar_line(self):

l1 = PolarLine((0, 0), 10, direction=(1, 1))
self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (10, 10, 0), 5)
self.assertTrue(isinstance(l1, Edge))

with self.assertRaises(ValueError):
PolarLine((0, 0), 1)

def test_spline(self):
"""Test spline with no tangents"""
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.assertTrue(isinstance(s1, Edge))

def test_radius_arc(self):
"""Test center arc as arc and circle"""
Expand Down Expand Up @@ -304,9 +314,12 @@ def test_radius_arc(self):
self.assertAlmostEqual(arc4.length, 2 * r * pi * 0.6, 6)
self.assertGreater(arc4.bounding_box().max.X, c.bounding_box().max.X)

self.assertTrue(isinstance(arc1, Edge))

def test_sagitta_arc(self):
l1 = SagittaArc((0, 0), (1, 0), 0.1)
self.assertAlmostEqual((l1 @ 0.5).Y, 0.1, 5)
self.assertTrue(isinstance(l1, Edge))

def test_center_arc(self):
"""Test center arc as arc and circle"""
Expand All @@ -327,18 +340,20 @@ def test_center_arc(self):
self.assertTupleAlmostEquals((arc.edges()[0] @ 0.5).to_tuple(), (0, 0, 0), 5)

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.assertTrue(isinstance(arc, Edge))

def test_polyline(self):
"""Test edge generation and close"""
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(
(test.edges()[0] @ 0 - test.edges()[-1] @ 1).length, 0, 5
)
self.assertEqual(len(test.edges()), 4)
self.assertAlmostEqual(test.wires()[0].length, 4)
self.assertTrue(isinstance(p1, Wire))

def test_polyline_with_list(self):
"""Test edge generation and close"""
Expand Down

0 comments on commit 60b2444

Please sign in to comment.