diff --git a/docs/algebra_definition.rst b/docs/algebra_definition.rst index e87c42c..7172bbc 100644 --- a/docs/algebra_definition.rst +++ b/docs/algebra_definition.rst @@ -29,7 +29,7 @@ Objects and arithmetic :math:`B^2 := \lbrace` ``Sketch``, ``Rectangle``, ``Circle``, ``Ellipse``, ``Rectangle``, ``Polygon``, ``RegularPolygon``, ``Text``, ``Trapezoid``, ``SlotArc``, ``SlotCenterPoint``, ``SlotCenterToCenter``, ``SlotOverall`` :math:`\rbrace` -:math:`B^1 := \lbrace` ``Curve``, ``Bezier``, ``FilletPolyline``, ``PolarLine``, ``Polyline``, ``Spline``, ``Helix``, ``CenterArc``, ``EllipticalCenterArc``, ``RadiusArc``, ``SagittaArc``, ``TangentArc``, ``ThreePointArc``, ``JernArc`` :math:`\rbrace` +:math:`B^1 := \lbrace` ``Curve``, ``Bezier``, ``FilletPolyline``, ``PolarLine``, ``Polyline``, ``Spline``, ``Helix``, ``CenterArc``, ``EllipticalCenterArc``, ``ParabolicCenterArc``, ``RadiusArc``, ``SagittaArc``, ``TangentArc``, ``ThreePointArc``, ``JernArc`` :math:`\rbrace` with :math:`B^3 \subset C^3, B^2 \subset C^2` and :math:`B^1 \subset C^1` diff --git a/docs/cheat_sheet.rst b/docs/cheat_sheet.rst index 8bd0d86..1f2ebb8 100644 --- a/docs/cheat_sheet.rst +++ b/docs/cheat_sheet.rst @@ -23,6 +23,7 @@ Cheat Sheet | :class:`~objects_curve.CenterArc` | :class:`~objects_curve.DoubleTangentArc` | :class:`~objects_curve.EllipticalCenterArc` + | :class:`~objects_curve.ParabolicCenterArc` | :class:`~objects_curve.FilletPolyline` | :class:`~objects_curve.Helix` | :class:`~objects_curve.IntersectingLine` diff --git a/docs/objects.rst b/docs/objects.rst index 85204bf..8b547b0 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -118,6 +118,13 @@ The following objects all can be used in BuildLine contexts. Note that +++ Elliptical arc defined by center, radii & angles + .. grid-item-card:: :class:`~objects_curve.ParabolicCenterArc` + + .. image:: assets/parabolic_center_arc_example.svg + + +++ + Parabolic arc defined by vertex, focal length & angles + .. grid-item-card:: :class:`~objects_curve.FilletPolyline` .. image:: assets/filletpolyline_example.svg diff --git a/docs/objects_1d.py b/docs/objects_1d.py index 1d72359..79baa74 100644 --- a/docs/objects_1d.py +++ b/docs/objects_1d.py @@ -125,6 +125,14 @@ svg.add_shape(elliptical_center_arc.line) svg.add_shape(dot.moved(Location(Vector((0, 0))))) svg.write("assets/elliptical_center_arc_example.svg") +with BuildLine() as parabolic_center_arc: + ParabolicCenterArc((0, 0), 0.5, 60, 0) +s = 100 / max(*parabolic_center_arc.line.bounding_box().size) +svg = ExportSVG(scale=s) +svg.add_shape(parabolic_center_arc.line) +svg.add_shape(dot.moved(Location(Vector((0, 0))))) +svg.write("assets/parabolic_center_arc_example.svg") + with BuildLine() as helix: Helix(1, 3, 1) scene = Compound(helix.line) + Compound.make_triad(0.5) diff --git a/src/build123d/__init__.py b/src/build123d/__init__.py index 2dcf0b0..2217685 100644 --- a/src/build123d/__init__.py +++ b/src/build123d/__init__.py @@ -88,6 +88,7 @@ __all__ = [ "DoubleTangentArc", "EllipticalCenterArc", "EllipticalStartArc", + "ParabolicCenterArc", "FilletPolyline", "Helix", "IntersectingLine", diff --git a/src/build123d/importers.py b/src/build123d/importers.py index d53628a..b4b5743 100644 --- a/src/build123d/importers.py +++ b/src/build123d/importers.py @@ -287,6 +287,7 @@ def import_svg_as_buildline_code( "QuadraticBezier": ["Bezier", "start", "control", "end"], "Arc": [ "EllipticalCenterArc", + "ParabolicCenterArc", # "EllipticalStartArc", "start", "end", diff --git a/src/build123d/objects_curve.py b/src/build123d/objects_curve.py index 71d788b..dc0b8e0 100644 --- a/src/build123d/objects_curve.py +++ b/src/build123d/objects_curve.py @@ -741,6 +741,61 @@ class EllipticalCenterArc(BaseEdgeObject): super().__init__(curve, mode=mode) +class ParabolicCenterArc(BaseEdgeObject): + """Line Object: Parabolic Center Arc + + Create a parabolic arc defined by a vertex point and focal length (distance from focus to vertex). + + Args: + vertex (VectorLike): parabola vertex + focal_length (float): focal length the parabola (distance from the vertex to focus along the x-axis of plane) + start_angle (float, optional): arc start angle. + Defaults to 0.0 + end_angle (float, optional): arc end angle. + Defaults to 90.0 + rotation (float, optional): angle to rotate arc. Defaults to 0.0 + angular_direction (AngularDirection, optional): arc direction. + Defaults to AngularDirection.COUNTER_CLOCKWISE + plane (Plane, optional): base plane. Defaults to Plane.XY + mode (Mode, optional): combination mode. Defaults to Mode.ADD + """ + + _applies_to = [BuildLine._tag] + + def __init__( + self, + vertex: VectorLike, + focal_length: float, + start_angle: float = 0.0, + end_angle: float = 90.0, + rotation: float = 0.0, + angular_direction: AngularDirection = AngularDirection.COUNTER_CLOCKWISE, + mode: Mode = Mode.ADD, + ): + context: BuildLine | None = BuildLine._get_context(self) + validate_inputs(context, self) + + vertex_pnt = WorkplaneList.localize(vertex) + if context is None: + parabola_workplane = Plane.XY + else: + parabola_workplane = copy_module.copy( + WorkplaneList._get_context().workplanes[0] + ) + parabola_workplane.origin = vertex_pnt + curve = Edge.make_parabola( + focal_length=focal_length, + plane=parabola_workplane, + start_angle=start_angle, + end_angle=end_angle, + angular_direction=angular_direction, + ).rotate( + Axis(parabola_workplane.origin, parabola_workplane.z_dir.to_dir()), rotation + ) + + super().__init__(curve, mode=mode) + + class Helix(BaseEdgeObject): """Line Object: Helix diff --git a/src/build123d/topology/one_d.py b/src/build123d/topology/one_d.py index f48c7bb..a9e0172 100644 --- a/src/build123d/topology/one_d.py +++ b/src/build123d/topology/one_d.py @@ -88,7 +88,7 @@ from OCP.BRepOffsetAPI import BRepOffsetAPI_MakeOffset from OCP.BRepPrimAPI import BRepPrimAPI_MakeHalfSpace from OCP.BRepProj import BRepProj_Projection from OCP.BRepTools import BRepTools, BRepTools_WireExplorer -from OCP.GC import GC_MakeArcOfCircle, GC_MakeArcOfEllipse +from OCP.GC import GC_MakeArcOfCircle, GC_MakeArcOfEllipse, GC_MakeArcOfParabola from OCP.GCPnts import ( GCPnts_AbscissaPoint, GCPnts_QuasiUniformDeflection, @@ -151,6 +151,7 @@ from OCP.gp import ( gp_Dir, gp_Dir2d, gp_Elips, + gp_Parab, gp_Pln, gp_Pnt, gp_Pnt2d, @@ -2204,6 +2205,42 @@ class Edge(Mixin1D[TopoDS_Edge]): return ellipse + @classmethod + def make_parabola( + cls, + focal_length: float, + plane: Plane = Plane.XY, + start_angle: float = 0.0, + end_angle: float = 90.0, + angular_direction: AngularDirection = AngularDirection.COUNTER_CLOCKWISE, + ) -> Edge: + """make parabola + + Makes an parabola centered at the origin of plane. + + Args: + focal_length (float): focal length the parabola (distance from the vertex to focus along the x-axis of plane) + plane (Plane, optional): base plane. Defaults to Plane.XY. + start_angle (float, optional): Defaults to 0.0. + end_angle (float, optional): Defaults to 90.0. + angular_direction (AngularDirection, optional): arc direction. + Defaults to AngularDirection.COUNTER_CLOCKWISE. + + Returns: + Edge: full or partial parabola + """ + parabola_gp = gp_Parab(plane.to_gp_ax2(), focal_length) + + parabola_geom = GC_MakeArcOfParabola( + parabola_gp, + start_angle * DEG2RAD, + end_angle * DEG2RAD, + angular_direction == AngularDirection.COUNTER_CLOCKWISE, + ).Value() + parabola = cls(BRepBuilderAPI_MakeEdge(parabola_geom).Edge()) + + return parabola + @classmethod def make_helix( cls,