From b813ef0aaa3819152dbc0085f9dc33ef7e6c5d3f Mon Sep 17 00:00:00 2001 From: Roger Maitland Date: Wed, 1 Feb 2023 14:50:20 -0500 Subject: [PATCH] Adding buildline to rtd --- docs/buildline.rst | 319 +++++++++++++++++++++++++++++++++++++ docs/buildline_examples.py | 175 ++++++++++++++++++++ 2 files changed, 494 insertions(+) create mode 100644 docs/buildline.rst create mode 100644 docs/buildline_examples.py diff --git a/docs/buildline.rst b/docs/buildline.rst new file mode 100644 index 0000000..4c7ff37 --- /dev/null +++ b/docs/buildline.rst @@ -0,0 +1,319 @@ +######### +BuildLine +######### + +BuildLine is a python context manager that is used to create one dimensional +objects - objects with the property of length but not area - that are typically +used as part of a BuildSketch sketch or a BuildPart path. + +The complete API for BuildLine is located here in the +:ref:`Builder API Reference `. + +******************* +Basic Functionality +******************* + +The following is a simple BuildLine example: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 1] + :end-before: [Ex. 1] + +The ``with`` statement creates the ``BuildLine`` context manager with the +identifier ``example_1``. The objects and operations that are within the +scope (i.e. indented) of this context will contribute towards the object +being created by the context manager. For BuildLine, this object is +``line`` and it's referenced as ``example_1.line``. + +The first object in this example is a ``Line`` object which is used to create +a straight line from coordinates (0,0) to (2,0) on the default XY plane. +The second object is a ``ThreePointArc`` that starts and ends at the two +ends of the line. + +.. image:: assets/buildline_example_1.svg + :align: center + +*********** +Constraints +*********** + +Building with constraints enables the designer to capture design intent and +add a high degree of robustness to their designs. The following sections +describe creating positional and tangential constraints as well as using +object attributes to enable this type of design. + +@ ``position_at`` Operator +=========================== + +In the previous example, the ``ThreePointArc`` started and ended at the +two ends of the ``Line`` but this was done by referring to the same +point ``(0,0)`` and ``(2,0)``. This can be improved upon by specifying +constraints that lock the arc to those two end points, as follows: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 2] + :end-before: [Ex. 2] + +Here instance variables ``l1`` and ``l2`` are assigned to the two BuildLine +objects and the ``ThreePointArc`` references the beginning of the straight +line with ``l1 @ 0`` and the end with ``l1 @ 1``. The ``@`` operator takes +a float (or integer) parameter between 0 and 1 and determines a position +at this fractional position along the line's length. + +This example can be improved on further by calculating the mid-point +of the arc as follows: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 3] + :end-before: [Ex. 3] + +Here ``l1 @ 0.5`` finds the center of ``l1`` while ``l1 @ 0.5 + (0, 1)`` does +a vector addition to generate the point ``(1,1)``. + +To make the design even more parametric, the height of the arc can be calculated +from ``l1`` as follows: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 4] + :end-before: [Ex. 4] + +The arc height is now calculated as ``(0, l1.length / 2)`` by using the ``length`` +property of ``Edge`` and ``Wire`` shapes. At this point the ``ThreePointArc`` is +fully parametric and able to generate the same shape for any horizontal line. + +% ``tangent_at`` Operator +========================= + +The other operator that is commonly used within BuildLine is ``%`` the tangent at +operator. Here is another example: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 5] + :end-before: [Ex. 5] + +which generates: + +.. image:: assets/buildline_example_5.svg + :align: center + +The ``JernArc`` has the following parameters: + +* ``start=l2 @ 1`` - start the arc at the end of line ``l2``, +* ``tangent=l2 % 1`` - the tangent of the arc at the start point is equal to the ``l2``\'s, + tangent at its end +* ``radius=0.5`` - the radius of the arc, and +* ``arc_size=90`` the angular size of the arc. + +The final line starts at the end of ``l3`` and ends at a point calculated from the length +of ``l2`` and the radius of arc ``l3``. + +Building with constraints as shown here will ensure that your designs both fully represent +design intent and are robust to design changes. + +************************ +BuildLine to BuildSketch +************************ + +As mentioned previously, one of the two primary reasons to create BuildLine objects is to +use them in BuildSketch. When a BuildLine context manager exits and is within the scope of a +BuildSketch context manager it will transfer the generated line to BuildSketch. The BuildSketch +:class:`~build_sketch.MakeFace` or :class:`~build_sketch.MakeHull` operations are then used +to transform the line (specifically a list of Edges) into a Face - the native BuildSketch +objects. + +Here is an example of using BuildLine to create an object that otherwise might be +difficult to create: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 6] + :end-before: [Ex. 6] + +which generates: + +.. image:: assets/buildline_example_6.svg + :align: center + +.. note:: SVG import to BuildLine + + The BuildLine code used in this example was generated by translating a SVG file + into BuildLine source code with the :meth:`~direct_api.SVG.translate_to_buildline_code` + method. For example: + + .. code:: + + svg_code, builder_name = SVG.translate_to_buildline_code("club.svg") + + would translate the "club.svg" image file's paths into BuildLine code much like + that shown above. From there it's easy for a user to add constraints or otherwise + enhance the original image and use it in their design. + +********************** +BuildLine to BuildPart +********************** + +The other primary reasons to use BuildLine is to create paths for BuildPart +:class:`~build_part.Sweep` operations. Here some curved and straight segments +define a path: + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 7] + :end-before: [Ex. 7] + +which generates: + +.. image:: assets/buildline_example_7.svg + :align: center + +There are few things to note from this example: + +* The @ and % operators are used to create a plane normal to the beginning of the + path with which to create the circular section used by the sweep operation + (this plane is not one of the ordinal planes). +* Both the path generated by BuildLine and the section generated by BuildSketch + have been transferred to BuildPart when each of them exit. +* The BuildPart ``Sweep`` operation is using the path and section previously + transferred to it (as "pending" objects) as parameters of the sweep. The + ``Sweep`` operation "consumes" these pending objects as to not interfere with + subsequence operations. + +***************** +BuildLine Objects +***************** + +The following objects all can be used in BuildLine contexts. + +.. grid:: 3 + + .. grid-item-card:: :class:`~build_line.Bezier` + + .. image:: assets/bezier_curve_example.svg + + +++ + Curve defined by control points and weights + + .. grid-item-card:: :class:`~build_line.CenterArc` + + .. image:: assets/center_arc_example.svg + + +++ + Arc defined by center, radius, & angles + + .. grid-item-card:: :class:`~build_line.EllipticalCenterArc` + + .. image:: assets/elliptical_center_arc_example.svg + + +++ + Elliptical arc defined by center, radii & angles + + .. grid-item-card:: :class:`~build_line.Helix` + + .. image:: assets/helix_example.svg + + +++ + Helix defined pitch, radius and height + + .. grid-item-card:: :class:`~build_line.JernArc` + + .. image:: assets/jern_arc_example.svg + + +++ + Arc define by start point, tangent, radius and angle + + .. grid-item-card:: :class:`~build_line.Line` + + .. image:: assets/line_example.svg + + +++ + Line defined by end points + + .. grid-item-card:: :class:`~build_line.PolarLine` + + .. image:: assets/polar_line_example.svg + + +++ + Line defined by start, angle and length + + .. grid-item-card:: :class:`~build_line.Polyline` + + .. image:: assets/polyline_example.svg + + +++ + Multiple line segments defined by points + + .. grid-item-card:: :class:`~build_line.RadiusArc` + + .. image:: assets/radius_arc_example.svg + + +++ + Arc define by two points and a radius + + .. grid-item-card:: :class:`~build_line.SagittaArc` + + .. image:: assets/sagitta_arc_example.svg + + +++ + Arc define by two points and a sagitta + + .. grid-item-card:: :class:`~build_line.Spline` + + .. image:: assets/spline_example.svg + + +++ + Curve define by points + + .. grid-item-card:: :class:`~build_line.TangentArc` + + .. image:: assets/tangent_arc_example.svg + + +++ + Curve define by two points and a tangent + + .. grid-item-card:: :class:`~build_line.ThreePointArc` + + .. image:: assets/three_point_arc_example.svg + + +++ + Curve define by three points + + +*********************** +Working on other Planes +*********************** + +So far all of the examples were created on ``Plane.XY`` - the default plane - which is equivalent +to global coordinates. Sometimes it's convenient to work on another plane, especially when +creating paths for BuildPart ``Sweep`` operations. + +.. literalinclude:: buildline_examples.py + :start-after: [Ex. 8] + :end-before: [Ex. 8] + +which generates: + +.. image:: assets/buildline_example_8.svg + :align: center + +Here the BuildLine object is created on ``Plane.YZ`` just by specifying the working plane +during BuildLine initialization. + +There are three rules to keep in mind when working with alternate planes in BuildLine: + +#. ``BuildLine`` accepts a single ``Plane`` to work with as opposed to other Builders + that accept more than one workplane. +#. Values entered as tuples such as ``(1, 2)`` or ``(1, 2, 3)`` will be localized to the + current workplane. This rule applies to points and to the use of tuples to modify + locations calculated with the ``@`` and ``%`` operators such as ``l1 @ 1 + (1, 1)``. + For example, if the workplane is ``Plane.YZ`` the local value of ``(1, 2)`` would + be converted to ``(0, 1, 2)`` in global coordinates. Three tuples are converted as + well - ``(1, 2, 3)`` on ``Plane.YZ`` would be ``(3, 1, 2)`` in global coordinates. + Providing values in local coordinates allows the designer to automate such + conversions. +#. Values entered using the ``Vector`` class or those generated by the ``@`` operator + are considered global values and are not localized. For example: + ``Line(Vector(1, 2, 3), Vector(4, 5, 6))`` will generate the same line independent + of the current workplane. It's unlikely that users will need to use ``Vector`` + values but the option is there. + +Finally, BuildLine's workplane need not be one of the predefined ordinal planes, it +could be one created from a surface of a BuildPart part that is currently under +construction. \ No newline at end of file diff --git a/docs/buildline_examples.py b/docs/buildline_examples.py new file mode 100644 index 0000000..bbb8a34 --- /dev/null +++ b/docs/buildline_examples.py @@ -0,0 +1,175 @@ +# [Setup] +from build123d import * + +# [Setup] +svg_opts1 = {"pixel_scale": 100, "show_axes": False, "show_hidden": False} +svg_opts2 = {"pixel_scale": 50, "show_axes": True, "show_hidden": False} + +# [Ex. 1] +with BuildLine() as example_1: + Line((0, 0), (2, 0)) + ThreePointArc((0, 0), (1, 1), (2, 0)) +# [Ex. 1] +example_1.line.export_svg( + "assets/buildline_example_1.svg", (0, 0, 100), (0, 1, 0), svg_opts=svg_opts1 +) +# [Ex. 2] +with BuildLine() as example_2: + l1 = Line((0, 0), (2, 0)) + l2 = ThreePointArc(l1 @ 0, (1, 1), l1 @ 1) +# [Ex. 2] + +# [Ex. 3] +with BuildLine() as example_3: + l1 = Line((0, 0), (2, 0)) + l2 = ThreePointArc(l1 @ 0, l1 @ 0.5 + (0, 1), l1 @ 1) +# [Ex. 3] + +# [Ex. 4] +with BuildLine() as example_4: + l1 = Line((0, 0), (2, 0)) + l2 = ThreePointArc(l1 @ 0, l1 @ 0.5 + (0, l1.length / 2), l1 @ 1) +# [Ex. 4] + +# [Ex. 5] +with BuildLine() as example_5: + l1 = Line((0, 0), (5, 0)) + l2 = Line(l1 @ 1, l1 @ 1 + (0, l1.length - 1)) + l3 = JernArc(start=l2 @ 1, tangent=l2 % 1, radius=0.5, arc_size=90) + l4 = Line(l3 @ 1, (0, l2.length + l3.radius)) +# [Ex. 5] +example_5.line.export_svg( + "assets/buildline_example_5.svg", (0, 0, 100), (0, 1, 0), svg_opts=svg_opts2 +) + + +# [Ex. 6] +with BuildSketch() as example_6: + with BuildLine() as club_outline: + l0 = Line((0, -188), (76, -188)) + b0 = Bezier(l0 @ 1, (61, -185), (33, -173), (17, -81)) + b1 = Bezier(b0 @ 1, (49, -128), (146, -145), (167, -67)) + b2 = Bezier(b1 @ 1, (187, 9), (94, 52), (32, 18)) + b3 = Bezier(b2 @ 1, (92, 57), (113, 188), (0, 188)) + Mirror(about=Plane.YZ) + MakeFace() + # [Ex. 6] + Scale(by=2 / example_6.sketch.bounding_box().ylen) +example_6.sketch.export_svg( + "assets/buildline_example_6.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts1 +) + +# [Ex. 7] +with BuildPart() as example_7: + with BuildLine() as example_7_path: + l1 = RadiusArc((0, 0), (1, 1), 2) + l2 = Spline(l1 @ 1, (2, 3), (3, 3), tangents=(l1 % 1, (0, -1))) + l3 = Line(l2 @ 1, (3, 0)) + with BuildSketch(Plane(origin=l1 @ 0, z_dir=l1 % 0)) as example_7_section: + Circle(0.1) + Sweep() +# [Ex. 7] +example_7.part.export_svg( + "assets/buildline_example_7.svg", (100, -50, 100), (0, 0, 1), svg_opts=svg_opts1 +) + +# [Ex. 8] +with BuildLine(Plane.YZ) as example_8: + l1 = Line((0, 0), (5, 0)) + l2 = Line(l1 @ 1, l1 @ 1 + (0, l1.length - 1)) + l3 = JernArc(start=l2 @ 1, tangent=l2 % 1, radius=0.5, arc_size=90) + l4 = Line(l3 @ 1, (0, l2.length + l3.radius)) +# [Ex. 8] +example_8.line.export_svg( + "assets/buildline_example_8.svg", (100, -50, 100), (0, 0, 1), svg_opts=svg_opts2 +) + +pts = [(0, 0), (2 / 3, 2 / 3), (0, 4 / 3), (-4 / 3, 0), (0, -2), (4, 0), (0, 3)] +wts = [1.0, 1.0, 2.0, 3.0, 4.0, 2.0, 1.0] +with BuildLine() as bezier_curve: + Bezier(*pts, weights=wts) +bezier_curve.line.export_svg( + "assets/bezier_curve_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as center_arc: + CenterArc((0, 0), 3, 0, 90) +center_arc.line.export_svg( + "assets/center_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as elliptical_center_arc: + EllipticalCenterArc((0, 0), 2, 3, 0, 90) +elliptical_center_arc.line.export_svg( + "assets/elliptical_center_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as helix: + Helix(1, 3, 1) +helix.line.export_svg( + "assets/helix_example.svg", (1, -1, 1), (0, 0, 1), svg_opts=svg_opts2 +) + +with BuildLine() as jern_arc: + JernArc((1, 1), (1, 0.5), 2, 100) +jern_arc.line.export_svg( + "assets/jern_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as line: + Line((1, 1), (3, 3)) +line.line.export_svg( + "assets/line_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as polar_line: + PolarLine((1, 1), 2.5, 60) +polar_line.line.export_svg( + "assets/polar_line_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as polyline: + Polyline((1, 1), (1.5, 2.5), (3, 3)) +polyline.line.export_svg( + "assets/polyline_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as radius_arc: + RadiusArc((1, 1), (3, 3), 2) +radius_arc.line.export_svg( + "assets/radius_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as sagitta_arc: + SagittaArc((1, 1), (3, 1), 1) +sagitta_arc.line.export_svg( + "assets/sagitta_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as spline: + Spline((1, 1), (2, 1.5), (1, 2), (2, 2.5), (1, 3)) +spline.line.export_svg( + "assets/spline_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as tangent_arc: + TangentArc((1, 1), (3, 3), tangent=(1, 0)) +tangent_arc.line.export_svg( + "assets/tangent_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +with BuildLine() as three_point_arc: + ThreePointArc((1, 1), (1.5, 2), (3, 3)) +three_point_arc.line.export_svg( + "assets/three_point_arc_example.svg", (0, 0, 1), (0, 1, 0), svg_opts=svg_opts2 +) + +if "show_object" in locals(): + # show_object(example_1.line, name="Ex. 1") + # show_object(example_2.line, name="Ex. 2") + # show_object(example_3.line, name="Ex. 3") + # show_object(example_4.line, name="Ex. 4") + # show_object(example_5.line, name="Ex. 5") + # show_object(example_6.line, name="Ex. 6") + show_object(example_8_path.line, name="Ex. 8 path") + show_object(example_8.part, name="Ex. 8")