diff --git a/docs/assets/examples/bicycle_tire.png b/docs/assets/examples/bicycle_tire.png new file mode 100644 index 0000000..bba9acb Binary files /dev/null and b/docs/assets/examples/bicycle_tire.png differ diff --git a/docs/assets/examples/cast_bearing_unit.png b/docs/assets/examples/cast_bearing_unit.png new file mode 100644 index 0000000..e0bebf1 Binary files /dev/null and b/docs/assets/examples/cast_bearing_unit.png differ diff --git a/docs/assets/examples/fast_grid_holes.png b/docs/assets/examples/fast_grid_holes.png new file mode 100644 index 0000000..f402e15 Binary files /dev/null and b/docs/assets/examples/fast_grid_holes.png differ diff --git a/docs/assets/stepper_drawing.svg b/docs/assets/stepper_drawing.svg new file mode 100644 index 0000000..6504639 --- /dev/null +++ b/docs/assets/stepper_drawing.svg @@ -0,0 +1,771 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/cheat_sheet.rst b/docs/cheat_sheet.rst index ecdd42d..4b88edb 100644 --- a/docs/cheat_sheet.rst +++ b/docs/cheat_sheet.rst @@ -15,6 +15,8 @@ Cheat Sheet .. grid-item-card:: 1D - BuildLine + | :class:`~objects_curve.ArcArcTangentArc` + | :class:`~objects_curve.ArcArcTangentLine` | :class:`~objects_curve.Bezier` | :class:`~objects_curve.CenterArc` | :class:`~objects_curve.DoubleTangentArc` @@ -24,6 +26,8 @@ Cheat Sheet | :class:`~objects_curve.IntersectingLine` | :class:`~objects_curve.JernArc` | :class:`~objects_curve.Line` + | :class:`~objects_curve.PointArcTangentArc` + | :class:`~objects_curve.PointArcTangentLine` | :class:`~objects_curve.PolarLine` | :class:`~objects_curve.Polyline` | :class:`~objects_curve.RadiusArc` @@ -99,6 +103,7 @@ Cheat Sheet | :func:`~operations_generic.add` | :func:`~operations_generic.chamfer` + | :func:`~operations_part.draft` | :func:`~operations_part.extrude` | :func:`~operations_generic.fillet` | :func:`~operations_part.loft` @@ -228,7 +233,7 @@ Cheat Sheet +----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ | :class:`~build_enums.Extrinsic` | XYZ, XZY, YZX, YXZ, ZXY, ZYX, XYX, XZX, YZY, YXY, ZXZ, ZYZ | +----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ - | :class:`~build_enums.FontStyle` | REGULAR, BOLD, BOLDITALIC, ITALIC | + | :class:`~build_enums.FontStyle` | REGULAR, BOLD, BOLDITALIC, ITALIC | +----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ | :class:`~build_enums.FrameMethod` | CORRECTED, FRENET | +----------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/docs/examples_1.rst b/docs/examples_1.rst index 987a870..7da07de 100644 --- a/docs/examples_1.rst +++ b/docs/examples_1.rst @@ -24,11 +24,21 @@ Most of the examples show the builder and algebra modes. :link: examples-benchy :link-type: ref + .. grid-item-card:: Bicycle Tire |Builder| + :img-top: assets/examples/bicycle_tire.png + :link: examples-bicycle_tire + :link-type: ref + .. grid-item-card:: Canadian Flag Blowing in The Wind |Builder| |Algebra| :img-top: assets/examples/example_canadian_flag_01.png :link: examples-canadian_flag :link-type: ref + .. grid-item-card:: Cast Bearing Unit |Builder| + :img-top: assets/examples/cast_bearing_unit.png + :link: examples-cast_bearing_unit + :link-type: ref + .. grid-item-card:: Circuit Board With Holes |Builder| |Algebra| :img-top: assets/examples/thumbnail_circuit_board_01.png :link: examples-circuit_board @@ -39,6 +49,11 @@ Most of the examples show the builder and algebra modes. :link: clock_face :link-type: ref + .. grid-item-card:: Fast Grid Holes |Algebra| + :img-top: assets/examples/fast_grid_holes.png + :link: fast_grid_holes + :link-type: ref + .. grid-item-card:: Handle |Builder| |Algebra| :img-top: assets/examples/handle.png :link: handle @@ -154,6 +169,24 @@ modify it by replacing chimney with a BREP version. .. ---------------------------------------------------------------------------------------------- +.. _examples-bicycle_tire: + +Bicycle Tire +-------------------------------- +.. image:: assets/examples/bicycle_tire.png + :align: center + +This example demonstrates how to model a realistic bicycle tire with a +patterned tread using build123d. The key concept showcased here is the +use of wrap_faces to project 2D planar geometry onto a curved 3D +surface. + +.. dropdown:: |Builder| Reference Implementation (Builder Mode) + + .. literalinclude:: ../examples/bicycle_tire.py + :start-after: [Code] + :end-before: [End] + .. _examples-build123d_logo: Former build123d Logo @@ -181,6 +214,23 @@ The builder mode example also generates the SVG file `logo.svg`. :end-before: [End] +.. _examples-cast_bearing_unit: + +Cast Bearing Unit +----------------- +.. image:: assets/examples/cast_bearing_unit.png + :align: center + +This example demonstrates the creation of a castable flanged bearing housing +using the `draft` operation to add appropriate draft angles for mold release. + + +.. dropdown:: |Builder| Reference Implementation (Builder Mode) + + .. literalinclude:: ../examples/cast_bearing_unit.py + :start-after: [Code] + :end-before: [End] + .. _examples-canadian_flag: Canadian Flag Blowing in The Wind @@ -280,6 +330,32 @@ a detailed and visually appealing clock design. :class:`~build_common.PolarLocations` are used to position features on the clock face. +.. _fast_grid_holes: + +Fast Grid Holes +--------------- +.. image:: assets/examples/fast_grid_holes.png + :align: center + +.. dropdown:: |Algebra| Reference Implementation (Algebra Mode) + + .. literalinclude:: ../examples/fast_grid_holes.py + :start-after: [Code] + :end-before: [End] + +This example demonstrates an efficient approach to creating a large number of holes +(625 in this case) in a planar part using build123d. + +Instead of modeling and subtracting 3D solids for each hole—which is computationally +expensive—this method constructs a 2D Face from an outer perimeter wire and a list of +hole wires. The entire face is then extruded in a single operation to form the final +3D object. This approach significantly reduces modeling time and complexity. + +The hexagonal hole pattern is generated using HexLocations, and each location is +populated with a hexagonal wire. These wires are passed directly to the Face constructor +as holes. On a typical Linux laptop, this script completes in approximately 1.02 seconds, +compared to substantially longer runtimes for boolean subtraction of individual holes in 3D. + .. _handle: @@ -473,7 +549,7 @@ Stud Wall .. image:: assets/examples/stud_wall.png :align: center -This example demonstrates creatings custom `Part` objects and putting them into +This example demonstrates creating custom `Part` objects and putting them into assemblies. The custom object is a `Stud` used in the building industry while the assembly is a `StudWall` created from copies of `Stud` objects for efficiency. Both the `Stud` and `StudWall` objects use `RigidJoints` to define snap points which @@ -527,7 +603,7 @@ Toy Truck --------- .. image:: assets/examples/toy_truck.png :align: center - + .. image:: assets/examples/toy_truck_picture.jpg :align: center @@ -537,11 +613,11 @@ Toy Truck :start-after: [Code] :end-before: [End] -This example demonstrates how to design a toy truck using BuildPart and -BuildSketch in Builder mode. The model includes a detailed body, cab, grill, -and bumper, showcasing techniques like sketch reuse, symmetry, tapered -extrusions, selective filleting, and the use of joints for part assembly. -Ideal for learning complex part construction and hierarchical modeling in +This example demonstrates how to design a toy truck using BuildPart and +BuildSketch in Builder mode. The model includes a detailed body, cab, grill, +and bumper, showcasing techniques like sketch reuse, symmetry, tapered +extrusions, selective filleting, and the use of joints for part assembly. +Ideal for learning complex part construction and hierarchical modeling in build123d. .. _vase: diff --git a/docs/joints.rst b/docs/joints.rst index e6a7e7c..07cc623 100644 --- a/docs/joints.rst +++ b/docs/joints.rst @@ -25,7 +25,7 @@ in pairs - a :class:`~topology.Joint` can only be connected to another :class:`~ Objects may have many joints bound to them each with an identifying label. All :class:`~topology.Joint` objects have a ``symbol`` property that can be displayed to help visualize -their position and orientation (the `ocp-vscode `_ viewer +their position and orientation (the `ocp-vscode `_ viewer has built-in support for displaying joints). .. note:: @@ -41,16 +41,16 @@ The following sections provide more detail on the available joints and describes Rigid Joint *********** -A rigid joint positions two components relative to each another with no freedom of movement. When a +A rigid joint positions two components relative to each another with no freedom of movement. When a :class:`~joints.RigidJoint` is instantiated it's assigned a ``label``, a part to bind to (``to_part``), -and a ``joint_location`` which defines both the position and orientation of the joint (see +and a ``joint_location`` which defines both the position and orientation of the joint (see :class:`~geometry.Location`) - as follows: .. code-block:: python RigidJoint(label="outlet", to_part=pipe, joint_location=path.location_at(1)) -Once a joint is bound to a part this way, the :meth:`~topology.Joint.connect_to` method can be used to +Once a joint is bound to a part this way, the :meth:`~topology.Joint.connect_to` method can be used to repositioning another part relative to ``self`` which stay fixed - as follows: .. code-block:: python @@ -74,7 +74,7 @@ flanges are attached to the ends of a curved pipe: Note how the locations of the joints are determined by the :meth:`~topology.Mixin1D.location_at` method and how the ``-`` negate operator is used to reverse the direction of the location without changing its -poosition. Also note that the ``WeldNeckFlange`` class predefines two joints, one at the pipe end and +position. Also note that the ``WeldNeckFlange`` class predefines two joints, one at the pipe end and one at the face end - both of which are shown in the above image (generated by ocp-vscode with the ``render_joints=True`` flag set in the ``show`` function). @@ -105,7 +105,7 @@ Revolute Joint Component rotates around axis like a hinge. The :ref:`joint_tutorial` covers Revolute Joints in detail. -During instantiation of a :class:`~joints.RevoluteJoint` there are three parameters not present with +During instantiation of a :class:`~joints.RevoluteJoint` there are three parameters not present with Rigid Joints: ``axis``, ``angle_reference``, and ``range`` that allow the circular motion to be fully defined. @@ -114,7 +114,7 @@ which allows one to change the relative position of joined parts by changing a s .. autoclass:: RevoluteJoint -.. +.. :exclude-members: connect_to .. method:: connect_to(other: RigidJoint, *, angle: float = None) @@ -151,7 +151,7 @@ of the limits will raise an exception. .. autoclass:: LinearJoint -.. +.. :exclude-members: connect_to .. method:: connect_to(other: RevoluteJoint, *, position: float = None, angle: float = None) @@ -164,10 +164,10 @@ of the limits will raise an exception. Cylindrical Joint ***************** -A :class:`~joints.CylindricalJoint` allows a component to rotate around and moves along a single axis -like a screw combining the functionality of a :class:`~joints.LinearJoint` and a -:class:`~joints.RevoluteJoint` joint. The ``connect_to`` for these joints have both ``position`` and -``angle`` parameters as shown below extracted from the joint tutorial. +A :class:`~joints.CylindricalJoint` allows a component to rotate around and moves along a single axis +like a screw combining the functionality of a :class:`~joints.LinearJoint` and a +:class:`~joints.RevoluteJoint` joint. The ``connect_to`` for these joints have both ``position`` and +``angle`` parameters as shown below extracted from the joint tutorial. .. code-block::python @@ -176,7 +176,7 @@ like a screw combining the functionality of a :class:`~joints.LinearJoint` and a .. autoclass:: CylindricalJoint -.. +.. :exclude-members: connect_to .. method:: connect_to(other: RigidJoint, *, position: float = None, angle: float = None) @@ -195,13 +195,13 @@ is found within a rod end as shown here: .. literalinclude:: rod_end.py :emphasize-lines: 40-44,51,53 -Note how limits are defined during the instantiation of the ball joint when ensures that the pin or bolt +Note how limits are defined during the instantiation of the ball joint when ensures that the pin or bolt within the rod end does not interfere with the rod end itself. The ``connect_to`` sets the three angles (only two are significant in this example). .. autoclass:: BallJoint -.. +.. :exclude-members: connect_to .. method:: connect_to(other: RigidJoint, *, angles: RotationLike = None) diff --git a/docs/operations.rst b/docs/operations.rst index 924a0c7..e7532b6 100644 --- a/docs/operations.rst +++ b/docs/operations.rst @@ -21,51 +21,53 @@ The following table summarizes all of the available operations. Operations marke applicable to BuildLine and Algebra Curve, 2D to BuildSketch and Algebra Sketch, 3D to BuildPart and Algebra Part. -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| Operation | Description | 0D | 1D | 2D | 3D | Example | -+==============================================+====================================+====+====+====+====+========================+ -| :func:`~operations_generic.add` | Add object to builder | | ✓ | ✓ | ✓ | :ref:`16 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.bounding_box` | Add bounding box as Shape | | ✓ | ✓ | ✓ | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.chamfer` | Bevel Vertex or Edge | | | ✓ | ✓ | :ref:`9 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.extrude` | Draw 2D Shape into 3D | | | | ✓ | :ref:`3 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.fillet` | Radius Vertex or Edge | | | ✓ | ✓ | :ref:`9 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_sketch.full_round` | Round-off Face along given Edge | | | ✓ | | :ref:`ttt-24-spo-06` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.loft` | Create 3D Shape from sections | | | | ✓ | :ref:`24 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.make_brake_formed` | Create sheet metal parts | | | | ✓ | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_sketch.make_face` | Create a Face from Edges | | | ✓ | | :ref:`4 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_sketch.make_hull` | Create Convex Hull from Edges | | | ✓ | | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.mirror` | Mirror about Plane | | ✓ | ✓ | ✓ | :ref:`15 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.offset` | Inset or outset Shape | | ✓ | ✓ | ✓ | :ref:`25 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.project` | Project points, lines or Faces | ✓ | ✓ | ✓ | | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.project_workplane` | Create workplane for projection | | | | | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.revolve` | Swing 2D Shape about Axis | | | | ✓ | :ref:`23 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.scale` | Change size of Shape | | ✓ | ✓ | ✓ | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.section` | Generate 2D slices from 3D Shape | | | | ✓ | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.split` | Divide object by Plane | | ✓ | ✓ | ✓ | :ref:`27 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_generic.sweep` | Extrude 1/2D section(s) along path | | | ✓ | ✓ | :ref:`14 ` | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_part.thicken` | Expand 2D section(s) | | | | ✓ | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ -| :func:`~operations_sketch.trace` | Convert lines to faces | | | ✓ | | | -+----------------------------------------------+------------------------------------+----+----+----+----+------------------------+ ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| Operation | Description | 0D | 1D | 2D | 3D | Example | ++==============================================+====================================+====+====+====+====+===================================+ +| :func:`~operations_generic.add` | Add object to builder | | ✓ | ✓ | ✓ | :ref:`16 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.bounding_box` | Add bounding box as Shape | | ✓ | ✓ | ✓ | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.chamfer` | Bevel Vertex or Edge | | | ✓ | ✓ | :ref:`9 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.draft` | Add a draft taper to a part | | | | ✓ | :ref:`examples-cast_bearing_unit` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.extrude` | Draw 2D Shape into 3D | | | | ✓ | :ref:`3 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.fillet` | Radius Vertex or Edge | | | ✓ | ✓ | :ref:`9 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_sketch.full_round` | Round-off Face along given Edge | | | ✓ | | :ref:`ttt-24-spo-06` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.loft` | Create 3D Shape from sections | | | | ✓ | :ref:`24 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.make_brake_formed` | Create sheet metal parts | | | | ✓ | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_sketch.make_face` | Create a Face from Edges | | | ✓ | | :ref:`4 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_sketch.make_hull` | Create Convex Hull from Edges | | | ✓ | | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.mirror` | Mirror about Plane | | ✓ | ✓ | ✓ | :ref:`15 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.offset` | Inset or outset Shape | | ✓ | ✓ | ✓ | :ref:`25 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.project` | Project points, lines or Faces | ✓ | ✓ | ✓ | | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.project_workplane` | Create workplane for projection | | | | | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.revolve` | Swing 2D Shape about Axis | | | | ✓ | :ref:`23 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.scale` | Change size of Shape | | ✓ | ✓ | ✓ | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.section` | Generate 2D slices from 3D Shape | | | | ✓ | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.split` | Divide object by Plane | | ✓ | ✓ | ✓ | :ref:`27 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_generic.sweep` | Extrude 1/2D section(s) along path | | | ✓ | ✓ | :ref:`14 ` | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_part.thicken` | Expand 2D section(s) | | | | ✓ | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ +| :func:`~operations_sketch.trace` | Convert lines to faces | | | ✓ | | | ++----------------------------------------------+------------------------------------+----+----+----+----+-----------------------------------+ The following table summarizes all of the selectors that can be used within the scope of a Builder. Note that they will extract objects from the builder that is @@ -104,6 +106,7 @@ Reference .. autofunction:: operations_generic.add .. autofunction:: operations_generic.bounding_box .. autofunction:: operations_generic.chamfer +.. autofunction:: operations_part.draft .. autofunction:: operations_part.extrude .. autofunction:: operations_generic.fillet .. autofunction:: operations_sketch.full_round diff --git a/docs/rod_end.py b/docs/rod_end.py index e0a4364..335743f 100644 --- a/docs/rod_end.py +++ b/docs/rod_end.py @@ -3,9 +3,7 @@ from bd_warehouse.thread import IsoThread from ocp_vscode import * # Create the thread so the min radius is available below -thread = IsoThread( - major_diameter=8, pitch=1.25, length=20, end_finishes=("fade", "raw") -) +thread = IsoThread(major_diameter=6, pitch=1, length=20, end_finishes=("fade", "raw")) inner_radius = 15.89 / 2 inner_gap = 0.2 @@ -52,4 +50,4 @@ with BuildPart() as ball: rod_end.part.joints["socket"].connect_to(ball.part.joints["ball"], angles=(5, 10, 0)) -show(rod_end.part, ball.part) +show(rod_end.part, ball.part, s2) diff --git a/docs/tech_drawing_tutorial.rst b/docs/tech_drawing_tutorial.rst new file mode 100644 index 0000000..227caab --- /dev/null +++ b/docs/tech_drawing_tutorial.rst @@ -0,0 +1,73 @@ +.. _tech_drawing_tutorial: + +########################## +Technical Drawing Tutorial +########################## + +This example demonstrates how to generate a standard technical drawing of a 3D part +using `build123d`. It creates orthographic and isometric views of a Nema 23 stepper +motor and exports the result as an SVG file suitable for printing or inspection. + +Overview +-------- + +A technical drawing represents a 3D object in 2D using a series of standardized views. +These include: + +- **Plan (Top View)** – as seen from directly above (Z-axis down) +- **Front Elevation** – looking at the object head-on (Y-axis forward) +- **Side Elevation (Right Side)** – viewed from the right (X-axis) +- **Isometric Projection** – a 3D perspective view to help visualize depth + +Each view is aligned to a position on the page and optionally scaled or annotated. + +How It Works +------------ + +The script uses the `project_to_viewport` method to project the 3D part geometry into 2D. +A helper function, `project_to_2d`, sets up the viewport (camera origin and up direction) +and places the result onto a virtual drawing sheet. + +The steps involved are: + +1. Load or construct a 3D part (in this case, a stepper motor). +2. Define a `TechnicalDrawing` border and title block using A4 page size. +3. Generate each of the standard views and apply transformations to place them. +4. Add dimensions using `ExtensionLine` and labels using `Text`. +5. Export the drawing using `ExportSVG`, separating visible and hidden edges by layer + and style. + +Result +------ + +.. image:: /assets/stepper_drawing.svg + :alt: Stepper motor technical drawing + :class: align-center + :width: 80% + +Try It Yourself +--------------- + +You can modify the script to: + +- Replace the part with your own `Part` model +- Adjust camera angles and scale +- Add other views (bottom, rear) +- Enhance with more labels and dimensions + +Code +---- + +.. literalinclude:: technical_drawing.py + :language: python + :start-after: [code] + :end-before: [end] + +Dependencies +------------ + +This example depends on the following packages: + +- `build123d` +- `bd_warehouse` (for the `StepperMotor` part) +- `ocp_vscode` (for local preview) diff --git a/docs/technical_drawing.py b/docs/technical_drawing.py new file mode 100644 index 0000000..55a6efc --- /dev/null +++ b/docs/technical_drawing.py @@ -0,0 +1,188 @@ +""" + +name: technical_drawing.py +by: gumyr +date: May 23, 2025 + +desc: + + Generate a multi-view technical drawing of a part, including isometric and + orthographic projections. + + This module demonstrates how to create a standard technical drawing using + `build123d`. It includes: + - Projection of a 3D part to 2D views (plan, front, side, isometric) + - Drawing borders and dimensioning using extension lines + - SVG export of visible and hidden geometry + - Example part: Nema 23 stepper motor from `bd_warehouse.open_builds` + + The following standard views are generated: + - Plan View (Top) + - Front Elevation + - Side Elevation (Right Side) + - Isometric Projection + + The resulting drawing is exported as an SVG and can be previewed using + the `ocp_vscode` viewer. + +license: + + Copyright 2025 gumyr + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +# [code] +from datetime import date + +from bd_warehouse.open_builds import StepperMotor +from build123d import * +from ocp_vscode import show + + +def project_to_2d( + part: Part, + viewport_origin: VectorLike, + viewport_up: VectorLike, + page_origin: VectorLike, + scale_factor: float = 1.0, +) -> tuple[ShapeList[Edge], ShapeList[Edge]]: + """project_to_2d + + Helper function to generate 2d views translated on the 2d page. + + Args: + part (Part): 3d object + viewport_origin (VectorLike): location of viewport + viewport_up (VectorLike): direction of the viewport Y axis + page_origin (VectorLike): center of 2d object on page + scale_factor (float, optional): part scalar. Defaults to 1.0. + + Returns: + tuple[ShapeList[Edge], ShapeList[Edge]]: visible & hidden edges + """ + scaled_part = part if scale_factor == 1.0 else scale(part, scale_factor) + visible, hidden = scaled_part.project_to_viewport( + viewport_origin, viewport_up, look_at=(0, 0, 0) + ) + visible = [Pos(*page_origin) * e for e in visible] + hidden = [Pos(*page_origin) * e for e in hidden] + + return ShapeList(visible), ShapeList(hidden) + + +# The object that appearing in the drawing +stepper: Part = StepperMotor("Nema23") + +# Create a standard technical drawing border on A4 paper +border = TechnicalDrawing( + designed_by="build123d", + design_date=date.fromisoformat("2025-05-23"), + page_size=PageSize.A4, + title="Nema 23 Stepper", + sub_title="Units: mm", + drawing_number="BD-1", + sheet_number=1, + drawing_scale=1, +) +page_size = border.bounding_box().size + +# Specify the drafting options for extension lines +drafting_options = Draft(font_size=3.5, decimal_precision=1, display_units=False) + +# Lists used to store the 2d visible and hidden lines +visible_lines, hidden_lines = [], [] + +# Isometric Projection - A 3D view where the part is rotated to reveal three +# dimensions equally. +iso_v, iso_h = project_to_2d( + stepper, + (100, 100, 100), + (0, 0, 1), + page_size * 0.3, + 0.75, +) +visible_lines.extend(iso_v) +hidden_lines.extend(iso_h) + +# Plan View (Top) - The view from directly above the part (looking down along +# the Z-axis). +vis, _ = project_to_2d( + stepper, + (0, 0, 100), + (0, 1, 0), + (page_size.X * -0.3, page_size.Y * 0.25), +) +visible_lines.extend(vis) + +# Dimension the top of the stepper +top_bbox = Curve(vis).bounding_box() +perimeter = Pos(*top_bbox.center()) * Rectangle(top_bbox.size.X, top_bbox.size.Y) +d1 = ExtensionLine( + border=perimeter.edges().sort_by(Axis.X)[-1], offset=1 * CM, draft=drafting_options +) +d2 = ExtensionLine( + border=perimeter.edges().sort_by(Axis.Y)[0], offset=1 * CM, draft=drafting_options +) +# Add a label +l1 = Text("Plan View", 6) +l1.position = vis.sort_by(Axis.Y)[-1].center() + (0, 5 * MM) + +# Front Elevation - The primary view, typically looking along the Y-axis, +# showing the height. +vis, _ = project_to_2d( + stepper, + (0, -100, 0), + (0, 0, 1), + (page_size.X * -0.3, page_size.Y * -0.125), +) +visible_lines.extend(vis) +d3 = ExtensionLine( + border=vis.sort_by(Axis.Y)[-1], offset=-5 * MM, draft=drafting_options +) +l2 = Text("Front Elevation", 6) +l2.position = vis.group_by(Axis.Y)[0].sort_by(Edge.length)[-1].center() + (0, -5 * MM) + +# Side Elevation - Often refers to the Right Side View, looking along the X-axis. +vis, _ = project_to_2d( + stepper, + (100, 0, 0), + (0, 0, 1), + (0, page_size.Y * 0.15), +) +visible_lines.extend(vis) +side_bbox = Curve(vis).bounding_box() +perimeter = Pos(*side_bbox.center()) * Rectangle(side_bbox.size.X, side_bbox.size.Y) +d4 = ExtensionLine( + border=perimeter.edges().sort_by(Axis.X)[-1], offset=1 * CM, draft=drafting_options +) +l3 = Text("Side Elevation", 6) +l3.position = vis.group_by(Axis.Y)[0].sort_by(Edge.length)[-1].center() + (0, -5 * MM) + + +# Initialize the SVG exporter +exporter = ExportSVG(unit=Unit.MM) +# Define visible and hidden line layers +exporter.add_layer("Visible") +exporter.add_layer("Hidden", line_color=(99, 99, 99), line_type=LineType.ISO_DOT) +# Add the objects to the appropriate layer +exporter.add_shape(visible_lines, layer="Visible") +exporter.add_shape(hidden_lines, layer="Hidden") +exporter.add_shape(border, layer="Visible") +exporter.add_shape([d1, d2, d3, d4], layer="Visible") +exporter.add_shape([l1, l2, l3], layer="Visible") +# Write the file +exporter.write(f"assets/stepper_drawing.svg") + +show(border, visible_lines, d1, d2, d3, d4, l1, l2, l3) +# [end] diff --git a/docs/topology_selection/examples/nema-17-bracket.step b/docs/topology_selection/examples/nema-17-bracket.step new file mode 100644 index 0000000..c950780 --- /dev/null +++ b/docs/topology_selection/examples/nema-17-bracket.step @@ -0,0 +1,4030 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('Open CASCADE Model'),'2;1'); +FILE_NAME('nema-17-bracket','2025-04-01T21:12:35',('Author'),( + 'Open CASCADE'),'Open CASCADE STEP processor 7.8','build123d', + 'Unknown'); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }')); +ENDSEC; +DATA; +#1 = APPLICATION_PROTOCOL_DEFINITION('international standard', + 'automotive_design',2000,#2); +#2 = APPLICATION_CONTEXT( + 'core data for automotive mechanical design processes'); +#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10); +#4 = PRODUCT_DEFINITION_SHAPE('','',#5); +#5 = PRODUCT_DEFINITION('design','',#6,#9); +#6 = PRODUCT_DEFINITION_FORMATION('','',#7); +#7 = PRODUCT('nema-17-bracket','nema-17-bracket','',(#8)); +#8 = PRODUCT_CONTEXT('',#2,'mechanical'); +#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design'); +#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#3331); +#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14); +#12 = CARTESIAN_POINT('',(0.,0.,0.)); +#13 = DIRECTION('',(0.,0.,1.)); +#14 = DIRECTION('',(1.,0.,-0.)); +#15 = MANIFOLD_SOLID_BREP('',#16); +#16 = CLOSED_SHELL('',(#17,#348,#513,#567,#616,#739,#788,#815,#869,#923, + #977,#1031,#1085,#1907,#1956,#2625,#2649,#2676,#2683,#2730,#2757, + #2784,#2791,#2838,#2865,#2892,#2899,#2946,#2973,#3000,#3007,#3054, + #3081,#3108,#3115,#3162,#3189,#3216,#3223,#3270,#3297,#3324)); +#17 = ADVANCED_FACE('',(#18,#193,#224,#255,#286,#317),#32,.F.); +#18 = FACE_BOUND('',#19,.F.); +#19 = EDGE_LOOP('',(#20,#55,#83,#111,#139,#167)); +#20 = ORIENTED_EDGE('',*,*,#21,.F.); +#21 = EDGE_CURVE('',#22,#24,#26,.T.); +#22 = VERTEX_POINT('',#23); +#23 = CARTESIAN_POINT('',(-4.440892098501E-16,-20.5,3.)); +#24 = VERTEX_POINT('',#25); +#25 = CARTESIAN_POINT('',(0.,-20.5,49.)); +#26 = SURFACE_CURVE('',#27,(#31,#43),.PCURVE_S1.); +#27 = LINE('',#28,#29); +#28 = CARTESIAN_POINT('',(0.,-20.5,0.)); +#29 = VECTOR('',#30,1.); +#30 = DIRECTION('',(0.,0.,1.)); +#31 = PCURVE('',#32,#37); +#32 = PLANE('',#33); +#33 = AXIS2_PLACEMENT_3D('',#34,#35,#36); +#34 = CARTESIAN_POINT('',(0.,-20.5,0.)); +#35 = DIRECTION('',(1.,0.,0.)); +#36 = DIRECTION('',(0.,0.,1.)); +#37 = DEFINITIONAL_REPRESENTATION('',(#38),#42); +#38 = LINE('',#39,#40); +#39 = CARTESIAN_POINT('',(0.,0.)); +#40 = VECTOR('',#41,1.); +#41 = DIRECTION('',(1.,0.)); +#42 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#43 = PCURVE('',#44,#49); +#44 = PLANE('',#45); +#45 = AXIS2_PLACEMENT_3D('',#46,#47,#48); +#46 = CARTESIAN_POINT('',(0.,-20.5,0.)); +#47 = DIRECTION('',(0.,1.,0.)); +#48 = DIRECTION('',(0.,0.,1.)); +#49 = DEFINITIONAL_REPRESENTATION('',(#50),#54); +#50 = LINE('',#51,#52); +#51 = CARTESIAN_POINT('',(0.,0.)); +#52 = VECTOR('',#53,1.); +#53 = DIRECTION('',(1.,0.)); +#54 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#55 = ORIENTED_EDGE('',*,*,#56,.T.); +#56 = EDGE_CURVE('',#22,#57,#59,.T.); +#57 = VERTEX_POINT('',#58); +#58 = CARTESIAN_POINT('',(-4.440892098501E-16,20.5,3.)); +#59 = SURFACE_CURVE('',#60,(#64,#71),.PCURVE_S1.); +#60 = LINE('',#61,#62); +#61 = CARTESIAN_POINT('',(-4.440892098501E-16,-20.5,3.)); +#62 = VECTOR('',#63,1.); +#63 = DIRECTION('',(0.,1.,0.)); +#64 = PCURVE('',#32,#65); +#65 = DEFINITIONAL_REPRESENTATION('',(#66),#70); +#66 = LINE('',#67,#68); +#67 = CARTESIAN_POINT('',(3.,0.)); +#68 = VECTOR('',#69,1.); +#69 = DIRECTION('',(0.,-1.)); +#70 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#71 = PCURVE('',#72,#77); +#72 = CYLINDRICAL_SURFACE('',#73,3.); +#73 = AXIS2_PLACEMENT_3D('',#74,#75,#76); +#74 = CARTESIAN_POINT('',(3.,-20.5,3.)); +#75 = DIRECTION('',(0.,1.,0.)); +#76 = DIRECTION('',(-1.,0.,0.)); +#77 = DEFINITIONAL_REPRESENTATION('',(#78),#82); +#78 = LINE('',#79,#80); +#79 = CARTESIAN_POINT('',(-0.,0.)); +#80 = VECTOR('',#81,1.); +#81 = DIRECTION('',(-0.,1.)); +#82 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#83 = ORIENTED_EDGE('',*,*,#84,.F.); +#84 = EDGE_CURVE('',#85,#57,#87,.T.); +#85 = VERTEX_POINT('',#86); +#86 = CARTESIAN_POINT('',(0.,20.5,49.)); +#87 = SURFACE_CURVE('',#88,(#92,#99),.PCURVE_S1.); +#88 = LINE('',#89,#90); +#89 = CARTESIAN_POINT('',(0.,20.5,51.)); +#90 = VECTOR('',#91,1.); +#91 = DIRECTION('',(0.,0.,-1.)); +#92 = PCURVE('',#32,#93); +#93 = DEFINITIONAL_REPRESENTATION('',(#94),#98); +#94 = LINE('',#95,#96); +#95 = CARTESIAN_POINT('',(51.,-41.)); +#96 = VECTOR('',#97,1.); +#97 = DIRECTION('',(-1.,0.)); +#98 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#99 = PCURVE('',#100,#105); +#100 = PLANE('',#101); +#101 = AXIS2_PLACEMENT_3D('',#102,#103,#104); +#102 = CARTESIAN_POINT('',(0.,20.5,0.)); +#103 = DIRECTION('',(0.,1.,0.)); +#104 = DIRECTION('',(0.,0.,1.)); +#105 = DEFINITIONAL_REPRESENTATION('',(#106),#110); +#106 = LINE('',#107,#108); +#107 = CARTESIAN_POINT('',(51.,0.)); +#108 = VECTOR('',#109,1.); +#109 = DIRECTION('',(-1.,0.)); +#110 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#111 = ORIENTED_EDGE('',*,*,#112,.T.); +#112 = EDGE_CURVE('',#85,#113,#115,.T.); +#113 = VERTEX_POINT('',#114); +#114 = CARTESIAN_POINT('',(0.,18.5,51.)); +#115 = SURFACE_CURVE('',#116,(#120,#127),.PCURVE_S1.); +#116 = LINE('',#117,#118); +#117 = CARTESIAN_POINT('',(0.,22.,47.5)); +#118 = VECTOR('',#119,1.); +#119 = DIRECTION('',(0.,-0.707106781187,0.707106781187)); +#120 = PCURVE('',#32,#121); +#121 = DEFINITIONAL_REPRESENTATION('',(#122),#126); +#122 = LINE('',#123,#124); +#123 = CARTESIAN_POINT('',(47.5,-42.5)); +#124 = VECTOR('',#125,1.); +#125 = DIRECTION('',(0.707106781187,0.707106781187)); +#126 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#127 = PCURVE('',#128,#133); +#128 = PLANE('',#129); +#129 = AXIS2_PLACEMENT_3D('',#130,#131,#132); +#130 = CARTESIAN_POINT('',(0.,19.5,50.)); +#131 = DIRECTION('',(0.,0.707106781187,0.707106781187)); +#132 = DIRECTION('',(-1.,-0.,0.)); +#133 = DEFINITIONAL_REPRESENTATION('',(#134),#138); +#134 = LINE('',#135,#136); +#135 = CARTESIAN_POINT('',(-0.,-3.535533905933)); +#136 = VECTOR('',#137,1.); +#137 = DIRECTION('',(-0.,1.)); +#138 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#139 = ORIENTED_EDGE('',*,*,#140,.F.); +#140 = EDGE_CURVE('',#141,#113,#143,.T.); +#141 = VERTEX_POINT('',#142); +#142 = CARTESIAN_POINT('',(0.,-18.5,51.)); +#143 = SURFACE_CURVE('',#144,(#148,#155),.PCURVE_S1.); +#144 = LINE('',#145,#146); +#145 = CARTESIAN_POINT('',(0.,-20.5,51.)); +#146 = VECTOR('',#147,1.); +#147 = DIRECTION('',(0.,1.,0.)); +#148 = PCURVE('',#32,#149); +#149 = DEFINITIONAL_REPRESENTATION('',(#150),#154); +#150 = LINE('',#151,#152); +#151 = CARTESIAN_POINT('',(51.,0.)); +#152 = VECTOR('',#153,1.); +#153 = DIRECTION('',(0.,-1.)); +#154 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#155 = PCURVE('',#156,#161); +#156 = PLANE('',#157); +#157 = AXIS2_PLACEMENT_3D('',#158,#159,#160); +#158 = CARTESIAN_POINT('',(0.,-20.5,51.)); +#159 = DIRECTION('',(0.,0.,1.)); +#160 = DIRECTION('',(1.,0.,0.)); +#161 = DEFINITIONAL_REPRESENTATION('',(#162),#166); +#162 = LINE('',#163,#164); +#163 = CARTESIAN_POINT('',(0.,0.)); +#164 = VECTOR('',#165,1.); +#165 = DIRECTION('',(0.,1.)); +#166 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#167 = ORIENTED_EDGE('',*,*,#168,.F.); +#168 = EDGE_CURVE('',#24,#141,#169,.T.); +#169 = SURFACE_CURVE('',#170,(#174,#181),.PCURVE_S1.); +#170 = LINE('',#171,#172); +#171 = CARTESIAN_POINT('',(0.,-32.25,37.25)); +#172 = VECTOR('',#173,1.); +#173 = DIRECTION('',(-0.,0.707106781187,0.707106781187)); +#174 = PCURVE('',#32,#175); +#175 = DEFINITIONAL_REPRESENTATION('',(#176),#180); +#176 = LINE('',#177,#178); +#177 = CARTESIAN_POINT('',(37.25,11.75)); +#178 = VECTOR('',#179,1.); +#179 = DIRECTION('',(0.707106781187,-0.707106781187)); +#180 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#181 = PCURVE('',#182,#187); +#182 = PLANE('',#183); +#183 = AXIS2_PLACEMENT_3D('',#184,#185,#186); +#184 = CARTESIAN_POINT('',(0.,-19.5,50.)); +#185 = DIRECTION('',(0.,0.707106781187,-0.707106781187)); +#186 = DIRECTION('',(-1.,-0.,-0.)); +#187 = DEFINITIONAL_REPRESENTATION('',(#188),#192); +#188 = LINE('',#189,#190); +#189 = CARTESIAN_POINT('',(-0.,-18.03122292025)); +#190 = VECTOR('',#191,1.); +#191 = DIRECTION('',(-0.,1.)); +#192 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#193 = FACE_BOUND('',#194,.F.); +#194 = EDGE_LOOP('',(#195)); +#195 = ORIENTED_EDGE('',*,*,#196,.F.); +#196 = EDGE_CURVE('',#197,#197,#199,.T.); +#197 = VERTEX_POINT('',#198); +#198 = CARTESIAN_POINT('',(0.,17.15,14.5)); +#199 = SURFACE_CURVE('',#200,(#205,#212),.PCURVE_S1.); +#200 = CIRCLE('',#201,1.65); +#201 = AXIS2_PLACEMENT_3D('',#202,#203,#204); +#202 = CARTESIAN_POINT('',(0.,15.5,14.5)); +#203 = DIRECTION('',(1.,0.,0.)); +#204 = DIRECTION('',(0.,1.,0.)); +#205 = PCURVE('',#32,#206); +#206 = DEFINITIONAL_REPRESENTATION('',(#207),#211); +#207 = CIRCLE('',#208,1.65); +#208 = AXIS2_PLACEMENT_2D('',#209,#210); +#209 = CARTESIAN_POINT('',(14.5,-36.)); +#210 = DIRECTION('',(0.,-1.)); +#211 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#212 = PCURVE('',#213,#218); +#213 = CYLINDRICAL_SURFACE('',#214,1.65); +#214 = AXIS2_PLACEMENT_3D('',#215,#216,#217); +#215 = CARTESIAN_POINT('',(0.,15.5,14.5)); +#216 = DIRECTION('',(-1.,-0.,-0.)); +#217 = DIRECTION('',(0.,1.,0.)); +#218 = DEFINITIONAL_REPRESENTATION('',(#219),#223); +#219 = LINE('',#220,#221); +#220 = CARTESIAN_POINT('',(-0.,0.)); +#221 = VECTOR('',#222,1.); +#222 = DIRECTION('',(-1.,0.)); +#223 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#224 = FACE_BOUND('',#225,.F.); +#225 = EDGE_LOOP('',(#226)); +#226 = ORIENTED_EDGE('',*,*,#227,.F.); +#227 = EDGE_CURVE('',#228,#228,#230,.T.); +#228 = VERTEX_POINT('',#229); +#229 = CARTESIAN_POINT('',(0.,17.15,45.5)); +#230 = SURFACE_CURVE('',#231,(#236,#243),.PCURVE_S1.); +#231 = CIRCLE('',#232,1.65); +#232 = AXIS2_PLACEMENT_3D('',#233,#234,#235); +#233 = CARTESIAN_POINT('',(0.,15.5,45.5)); +#234 = DIRECTION('',(1.,0.,0.)); +#235 = DIRECTION('',(0.,1.,0.)); +#236 = PCURVE('',#32,#237); +#237 = DEFINITIONAL_REPRESENTATION('',(#238),#242); +#238 = CIRCLE('',#239,1.65); +#239 = AXIS2_PLACEMENT_2D('',#240,#241); +#240 = CARTESIAN_POINT('',(45.5,-36.)); +#241 = DIRECTION('',(0.,-1.)); +#242 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#243 = PCURVE('',#244,#249); +#244 = CYLINDRICAL_SURFACE('',#245,1.65); +#245 = AXIS2_PLACEMENT_3D('',#246,#247,#248); +#246 = CARTESIAN_POINT('',(0.,15.5,45.5)); +#247 = DIRECTION('',(-1.,-0.,-0.)); +#248 = DIRECTION('',(0.,1.,0.)); +#249 = DEFINITIONAL_REPRESENTATION('',(#250),#254); +#250 = LINE('',#251,#252); +#251 = CARTESIAN_POINT('',(-0.,0.)); +#252 = VECTOR('',#253,1.); +#253 = DIRECTION('',(-1.,0.)); +#254 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#255 = FACE_BOUND('',#256,.F.); +#256 = EDGE_LOOP('',(#257)); +#257 = ORIENTED_EDGE('',*,*,#258,.F.); +#258 = EDGE_CURVE('',#259,#259,#261,.T.); +#259 = VERTEX_POINT('',#260); +#260 = CARTESIAN_POINT('',(0.,-13.85,14.5)); +#261 = SURFACE_CURVE('',#262,(#267,#274),.PCURVE_S1.); +#262 = CIRCLE('',#263,1.65); +#263 = AXIS2_PLACEMENT_3D('',#264,#265,#266); +#264 = CARTESIAN_POINT('',(0.,-15.5,14.5)); +#265 = DIRECTION('',(1.,0.,0.)); +#266 = DIRECTION('',(0.,1.,0.)); +#267 = PCURVE('',#32,#268); +#268 = DEFINITIONAL_REPRESENTATION('',(#269),#273); +#269 = CIRCLE('',#270,1.65); +#270 = AXIS2_PLACEMENT_2D('',#271,#272); +#271 = CARTESIAN_POINT('',(14.5,-5.)); +#272 = DIRECTION('',(0.,-1.)); +#273 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#274 = PCURVE('',#275,#280); +#275 = CYLINDRICAL_SURFACE('',#276,1.65); +#276 = AXIS2_PLACEMENT_3D('',#277,#278,#279); +#277 = CARTESIAN_POINT('',(0.,-15.5,14.5)); +#278 = DIRECTION('',(-1.,-0.,-0.)); +#279 = DIRECTION('',(0.,1.,0.)); +#280 = DEFINITIONAL_REPRESENTATION('',(#281),#285); +#281 = LINE('',#282,#283); +#282 = CARTESIAN_POINT('',(-0.,0.)); +#283 = VECTOR('',#284,1.); +#284 = DIRECTION('',(-1.,0.)); +#285 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#286 = FACE_BOUND('',#287,.F.); +#287 = EDGE_LOOP('',(#288)); +#288 = ORIENTED_EDGE('',*,*,#289,.F.); +#289 = EDGE_CURVE('',#290,#290,#292,.T.); +#290 = VERTEX_POINT('',#291); +#291 = CARTESIAN_POINT('',(0.,16.,30.)); +#292 = SURFACE_CURVE('',#293,(#298,#305),.PCURVE_S1.); +#293 = CIRCLE('',#294,16.); +#294 = AXIS2_PLACEMENT_3D('',#295,#296,#297); +#295 = CARTESIAN_POINT('',(0.,0.,30.)); +#296 = DIRECTION('',(1.,0.,0.)); +#297 = DIRECTION('',(0.,1.,0.)); +#298 = PCURVE('',#32,#299); +#299 = DEFINITIONAL_REPRESENTATION('',(#300),#304); +#300 = CIRCLE('',#301,16.); +#301 = AXIS2_PLACEMENT_2D('',#302,#303); +#302 = CARTESIAN_POINT('',(30.,-20.5)); +#303 = DIRECTION('',(0.,-1.)); +#304 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#305 = PCURVE('',#306,#311); +#306 = CYLINDRICAL_SURFACE('',#307,16.); +#307 = AXIS2_PLACEMENT_3D('',#308,#309,#310); +#308 = CARTESIAN_POINT('',(0.,0.,30.)); +#309 = DIRECTION('',(-1.,-0.,-0.)); +#310 = DIRECTION('',(0.,1.,0.)); +#311 = DEFINITIONAL_REPRESENTATION('',(#312),#316); +#312 = LINE('',#313,#314); +#313 = CARTESIAN_POINT('',(-0.,0.)); +#314 = VECTOR('',#315,1.); +#315 = DIRECTION('',(-1.,0.)); +#316 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#317 = FACE_BOUND('',#318,.F.); +#318 = EDGE_LOOP('',(#319)); +#319 = ORIENTED_EDGE('',*,*,#320,.F.); +#320 = EDGE_CURVE('',#321,#321,#323,.T.); +#321 = VERTEX_POINT('',#322); +#322 = CARTESIAN_POINT('',(0.,-13.85,45.5)); +#323 = SURFACE_CURVE('',#324,(#329,#336),.PCURVE_S1.); +#324 = CIRCLE('',#325,1.65); +#325 = AXIS2_PLACEMENT_3D('',#326,#327,#328); +#326 = CARTESIAN_POINT('',(0.,-15.5,45.5)); +#327 = DIRECTION('',(1.,0.,0.)); +#328 = DIRECTION('',(0.,1.,0.)); +#329 = PCURVE('',#32,#330); +#330 = DEFINITIONAL_REPRESENTATION('',(#331),#335); +#331 = CIRCLE('',#332,1.65); +#332 = AXIS2_PLACEMENT_2D('',#333,#334); +#333 = CARTESIAN_POINT('',(45.5,-5.)); +#334 = DIRECTION('',(0.,-1.)); +#335 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#336 = PCURVE('',#337,#342); +#337 = CYLINDRICAL_SURFACE('',#338,1.65); +#338 = AXIS2_PLACEMENT_3D('',#339,#340,#341); +#339 = CARTESIAN_POINT('',(0.,-15.5,45.5)); +#340 = DIRECTION('',(-1.,-0.,-0.)); +#341 = DIRECTION('',(0.,1.,0.)); +#342 = DEFINITIONAL_REPRESENTATION('',(#343),#347); +#343 = LINE('',#344,#345); +#344 = CARTESIAN_POINT('',(-0.,0.)); +#345 = VECTOR('',#346,1.); +#346 = DIRECTION('',(-1.,0.)); +#347 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#348 = ADVANCED_FACE('',(#349),#44,.F.); +#349 = FACE_BOUND('',#350,.F.); +#350 = EDGE_LOOP('',(#351,#381,#407,#408,#431,#459,#487)); +#351 = ORIENTED_EDGE('',*,*,#352,.F.); +#352 = EDGE_CURVE('',#353,#355,#357,.T.); +#353 = VERTEX_POINT('',#354); +#354 = CARTESIAN_POINT('',(3.,-20.5,-4.440892098501E-16)); +#355 = VERTEX_POINT('',#356); +#356 = CARTESIAN_POINT('',(33.,-20.5,0.)); +#357 = SURFACE_CURVE('',#358,(#362,#369),.PCURVE_S1.); +#358 = LINE('',#359,#360); +#359 = CARTESIAN_POINT('',(0.,-20.5,0.)); +#360 = VECTOR('',#361,1.); +#361 = DIRECTION('',(1.,0.,0.)); +#362 = PCURVE('',#44,#363); +#363 = DEFINITIONAL_REPRESENTATION('',(#364),#368); +#364 = LINE('',#365,#366); +#365 = CARTESIAN_POINT('',(0.,0.)); +#366 = VECTOR('',#367,1.); +#367 = DIRECTION('',(0.,1.)); +#368 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#369 = PCURVE('',#370,#375); +#370 = PLANE('',#371); +#371 = AXIS2_PLACEMENT_3D('',#372,#373,#374); +#372 = CARTESIAN_POINT('',(0.,-20.5,0.)); +#373 = DIRECTION('',(0.,0.,1.)); +#374 = DIRECTION('',(1.,0.,0.)); +#375 = DEFINITIONAL_REPRESENTATION('',(#376),#380); +#376 = LINE('',#377,#378); +#377 = CARTESIAN_POINT('',(0.,0.)); +#378 = VECTOR('',#379,1.); +#379 = DIRECTION('',(1.,0.)); +#380 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#381 = ORIENTED_EDGE('',*,*,#382,.F.); +#382 = EDGE_CURVE('',#22,#353,#383,.T.); +#383 = SURFACE_CURVE('',#384,(#389,#400),.PCURVE_S1.); +#384 = CIRCLE('',#385,3.); +#385 = AXIS2_PLACEMENT_3D('',#386,#387,#388); +#386 = CARTESIAN_POINT('',(3.,-20.5,3.)); +#387 = DIRECTION('',(-0.,-1.,0.)); +#388 = DIRECTION('',(0.,-0.,1.)); +#389 = PCURVE('',#44,#390); +#390 = DEFINITIONAL_REPRESENTATION('',(#391),#399); +#391 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#392,#393,#394,#395,#396,#397 +,#398),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#392 = CARTESIAN_POINT('',(6.,3.)); +#393 = CARTESIAN_POINT('',(6.,-2.196152422707)); +#394 = CARTESIAN_POINT('',(1.5,0.401923788647)); +#395 = CARTESIAN_POINT('',(-3.,3.)); +#396 = CARTESIAN_POINT('',(1.5,5.598076211353)); +#397 = CARTESIAN_POINT('',(6.,8.196152422707)); +#398 = CARTESIAN_POINT('',(6.,3.)); +#399 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#400 = PCURVE('',#72,#401); +#401 = DEFINITIONAL_REPRESENTATION('',(#402),#406); +#402 = LINE('',#403,#404); +#403 = CARTESIAN_POINT('',(1.570796326795,-0.)); +#404 = VECTOR('',#405,1.); +#405 = DIRECTION('',(-1.,0.)); +#406 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#407 = ORIENTED_EDGE('',*,*,#21,.T.); +#408 = ORIENTED_EDGE('',*,*,#409,.T.); +#409 = EDGE_CURVE('',#24,#410,#412,.T.); +#410 = VERTEX_POINT('',#411); +#411 = CARTESIAN_POINT('',(3.,-20.5,49.)); +#412 = SURFACE_CURVE('',#413,(#417,#424),.PCURVE_S1.); +#413 = LINE('',#414,#415); +#414 = CARTESIAN_POINT('',(0.,-20.5,49.)); +#415 = VECTOR('',#416,1.); +#416 = DIRECTION('',(1.,0.,0.)); +#417 = PCURVE('',#44,#418); +#418 = DEFINITIONAL_REPRESENTATION('',(#419),#423); +#419 = LINE('',#420,#421); +#420 = CARTESIAN_POINT('',(49.,0.)); +#421 = VECTOR('',#422,1.); +#422 = DIRECTION('',(0.,1.)); +#423 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#424 = PCURVE('',#182,#425); +#425 = DEFINITIONAL_REPRESENTATION('',(#426),#430); +#426 = LINE('',#427,#428); +#427 = CARTESIAN_POINT('',(-0.,-1.414213562373)); +#428 = VECTOR('',#429,1.); +#429 = DIRECTION('',(-1.,0.)); +#430 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#431 = ORIENTED_EDGE('',*,*,#432,.F.); +#432 = EDGE_CURVE('',#433,#410,#435,.T.); +#433 = VERTEX_POINT('',#434); +#434 = CARTESIAN_POINT('',(3.,-20.5,3.)); +#435 = SURFACE_CURVE('',#436,(#440,#447),.PCURVE_S1.); +#436 = LINE('',#437,#438); +#437 = CARTESIAN_POINT('',(3.,-20.5,0.)); +#438 = VECTOR('',#439,1.); +#439 = DIRECTION('',(0.,0.,1.)); +#440 = PCURVE('',#44,#441); +#441 = DEFINITIONAL_REPRESENTATION('',(#442),#446); +#442 = LINE('',#443,#444); +#443 = CARTESIAN_POINT('',(0.,3.)); +#444 = VECTOR('',#445,1.); +#445 = DIRECTION('',(1.,0.)); +#446 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#447 = PCURVE('',#448,#453); +#448 = PLANE('',#449); +#449 = AXIS2_PLACEMENT_3D('',#450,#451,#452); +#450 = CARTESIAN_POINT('',(3.,-20.5,0.)); +#451 = DIRECTION('',(1.,0.,0.)); +#452 = DIRECTION('',(0.,0.,1.)); +#453 = DEFINITIONAL_REPRESENTATION('',(#454),#458); +#454 = LINE('',#455,#456); +#455 = CARTESIAN_POINT('',(0.,0.)); +#456 = VECTOR('',#457,1.); +#457 = DIRECTION('',(1.,0.)); +#458 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#459 = ORIENTED_EDGE('',*,*,#460,.T.); +#460 = EDGE_CURVE('',#433,#461,#463,.T.); +#461 = VERTEX_POINT('',#462); +#462 = CARTESIAN_POINT('',(33.,-20.5,3.)); +#463 = SURFACE_CURVE('',#464,(#468,#475),.PCURVE_S1.); +#464 = LINE('',#465,#466); +#465 = CARTESIAN_POINT('',(0.,-20.5,3.)); +#466 = VECTOR('',#467,1.); +#467 = DIRECTION('',(1.,0.,0.)); +#468 = PCURVE('',#44,#469); +#469 = DEFINITIONAL_REPRESENTATION('',(#470),#474); +#470 = LINE('',#471,#472); +#471 = CARTESIAN_POINT('',(3.,0.)); +#472 = VECTOR('',#473,1.); +#473 = DIRECTION('',(0.,1.)); +#474 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#475 = PCURVE('',#476,#481); +#476 = PLANE('',#477); +#477 = AXIS2_PLACEMENT_3D('',#478,#479,#480); +#478 = CARTESIAN_POINT('',(0.,-20.5,3.)); +#479 = DIRECTION('',(0.,0.,1.)); +#480 = DIRECTION('',(1.,0.,0.)); +#481 = DEFINITIONAL_REPRESENTATION('',(#482),#486); +#482 = LINE('',#483,#484); +#483 = CARTESIAN_POINT('',(0.,0.)); +#484 = VECTOR('',#485,1.); +#485 = DIRECTION('',(1.,0.)); +#486 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#487 = ORIENTED_EDGE('',*,*,#488,.F.); +#488 = EDGE_CURVE('',#355,#461,#489,.T.); +#489 = SURFACE_CURVE('',#490,(#494,#501),.PCURVE_S1.); +#490 = LINE('',#491,#492); +#491 = CARTESIAN_POINT('',(33.,-20.5,0.)); +#492 = VECTOR('',#493,1.); +#493 = DIRECTION('',(0.,0.,1.)); +#494 = PCURVE('',#44,#495); +#495 = DEFINITIONAL_REPRESENTATION('',(#496),#500); +#496 = LINE('',#497,#498); +#497 = CARTESIAN_POINT('',(0.,33.)); +#498 = VECTOR('',#499,1.); +#499 = DIRECTION('',(1.,0.)); +#500 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#501 = PCURVE('',#502,#507); +#502 = PLANE('',#503); +#503 = AXIS2_PLACEMENT_3D('',#504,#505,#506); +#504 = CARTESIAN_POINT('',(34.,-19.5,0.)); +#505 = DIRECTION('',(-0.707106781187,0.707106781187,0.)); +#506 = DIRECTION('',(0.,0.,1.)); +#507 = DEFINITIONAL_REPRESENTATION('',(#508),#512); +#508 = LINE('',#509,#510); +#509 = CARTESIAN_POINT('',(0.,-1.414213562373)); +#510 = VECTOR('',#511,1.); +#511 = DIRECTION('',(1.,0.)); +#512 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#513 = ADVANCED_FACE('',(#514),#72,.T.); +#514 = FACE_BOUND('',#515,.F.); +#515 = EDGE_LOOP('',(#516,#517,#540,#566)); +#516 = ORIENTED_EDGE('',*,*,#382,.T.); +#517 = ORIENTED_EDGE('',*,*,#518,.T.); +#518 = EDGE_CURVE('',#353,#519,#521,.T.); +#519 = VERTEX_POINT('',#520); +#520 = CARTESIAN_POINT('',(3.,20.5,-4.440892098501E-16)); +#521 = SURFACE_CURVE('',#522,(#526,#533),.PCURVE_S1.); +#522 = LINE('',#523,#524); +#523 = CARTESIAN_POINT('',(3.,-20.5,-4.440892098501E-16)); +#524 = VECTOR('',#525,1.); +#525 = DIRECTION('',(0.,1.,0.)); +#526 = PCURVE('',#72,#527); +#527 = DEFINITIONAL_REPRESENTATION('',(#528),#532); +#528 = LINE('',#529,#530); +#529 = CARTESIAN_POINT('',(-1.570796326795,0.)); +#530 = VECTOR('',#531,1.); +#531 = DIRECTION('',(-0.,1.)); +#532 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#533 = PCURVE('',#370,#534); +#534 = DEFINITIONAL_REPRESENTATION('',(#535),#539); +#535 = LINE('',#536,#537); +#536 = CARTESIAN_POINT('',(3.,0.)); +#537 = VECTOR('',#538,1.); +#538 = DIRECTION('',(0.,1.)); +#539 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#540 = ORIENTED_EDGE('',*,*,#541,.F.); +#541 = EDGE_CURVE('',#57,#519,#542,.T.); +#542 = SURFACE_CURVE('',#543,(#548,#555),.PCURVE_S1.); +#543 = CIRCLE('',#544,3.); +#544 = AXIS2_PLACEMENT_3D('',#545,#546,#547); +#545 = CARTESIAN_POINT('',(3.,20.5,3.)); +#546 = DIRECTION('',(-0.,-1.,0.)); +#547 = DIRECTION('',(0.,-0.,1.)); +#548 = PCURVE('',#72,#549); +#549 = DEFINITIONAL_REPRESENTATION('',(#550),#554); +#550 = LINE('',#551,#552); +#551 = CARTESIAN_POINT('',(1.570796326795,41.)); +#552 = VECTOR('',#553,1.); +#553 = DIRECTION('',(-1.,0.)); +#554 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#555 = PCURVE('',#100,#556); +#556 = DEFINITIONAL_REPRESENTATION('',(#557),#565); +#557 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#558,#559,#560,#561,#562,#563 +,#564),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#558 = CARTESIAN_POINT('',(6.,3.)); +#559 = CARTESIAN_POINT('',(6.,-2.196152422707)); +#560 = CARTESIAN_POINT('',(1.5,0.401923788647)); +#561 = CARTESIAN_POINT('',(-3.,3.)); +#562 = CARTESIAN_POINT('',(1.5,5.598076211353)); +#563 = CARTESIAN_POINT('',(6.,8.196152422707)); +#564 = CARTESIAN_POINT('',(6.,3.)); +#565 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#566 = ORIENTED_EDGE('',*,*,#56,.F.); +#567 = ADVANCED_FACE('',(#568),#182,.F.); +#568 = FACE_BOUND('',#569,.T.); +#569 = EDGE_LOOP('',(#570,#571,#572,#595)); +#570 = ORIENTED_EDGE('',*,*,#168,.F.); +#571 = ORIENTED_EDGE('',*,*,#409,.T.); +#572 = ORIENTED_EDGE('',*,*,#573,.T.); +#573 = EDGE_CURVE('',#410,#574,#576,.T.); +#574 = VERTEX_POINT('',#575); +#575 = CARTESIAN_POINT('',(3.,-18.5,51.)); +#576 = SURFACE_CURVE('',#577,(#581,#588),.PCURVE_S1.); +#577 = LINE('',#578,#579); +#578 = CARTESIAN_POINT('',(3.,-32.25,37.25)); +#579 = VECTOR('',#580,1.); +#580 = DIRECTION('',(-0.,0.707106781187,0.707106781187)); +#581 = PCURVE('',#182,#582); +#582 = DEFINITIONAL_REPRESENTATION('',(#583),#587); +#583 = LINE('',#584,#585); +#584 = CARTESIAN_POINT('',(-3.,-18.03122292025)); +#585 = VECTOR('',#586,1.); +#586 = DIRECTION('',(-0.,1.)); +#587 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#588 = PCURVE('',#448,#589); +#589 = DEFINITIONAL_REPRESENTATION('',(#590),#594); +#590 = LINE('',#591,#592); +#591 = CARTESIAN_POINT('',(37.25,11.75)); +#592 = VECTOR('',#593,1.); +#593 = DIRECTION('',(0.707106781187,-0.707106781187)); +#594 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#595 = ORIENTED_EDGE('',*,*,#596,.F.); +#596 = EDGE_CURVE('',#141,#574,#597,.T.); +#597 = SURFACE_CURVE('',#598,(#602,#609),.PCURVE_S1.); +#598 = LINE('',#599,#600); +#599 = CARTESIAN_POINT('',(0.,-18.5,51.)); +#600 = VECTOR('',#601,1.); +#601 = DIRECTION('',(1.,0.,0.)); +#602 = PCURVE('',#182,#603); +#603 = DEFINITIONAL_REPRESENTATION('',(#604),#608); +#604 = LINE('',#605,#606); +#605 = CARTESIAN_POINT('',(-0.,1.414213562373)); +#606 = VECTOR('',#607,1.); +#607 = DIRECTION('',(-1.,0.)); +#608 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#609 = PCURVE('',#156,#610); +#610 = DEFINITIONAL_REPRESENTATION('',(#611),#615); +#611 = LINE('',#612,#613); +#612 = CARTESIAN_POINT('',(0.,2.)); +#613 = VECTOR('',#614,1.); +#614 = DIRECTION('',(1.,0.)); +#615 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#616 = ADVANCED_FACE('',(#617),#100,.T.); +#617 = FACE_BOUND('',#618,.T.); +#618 = EDGE_LOOP('',(#619,#620,#643,#666,#689,#717,#738)); +#619 = ORIENTED_EDGE('',*,*,#84,.F.); +#620 = ORIENTED_EDGE('',*,*,#621,.T.); +#621 = EDGE_CURVE('',#85,#622,#624,.T.); +#622 = VERTEX_POINT('',#623); +#623 = CARTESIAN_POINT('',(3.,20.5,49.)); +#624 = SURFACE_CURVE('',#625,(#629,#636),.PCURVE_S1.); +#625 = LINE('',#626,#627); +#626 = CARTESIAN_POINT('',(0.,20.5,49.)); +#627 = VECTOR('',#628,1.); +#628 = DIRECTION('',(1.,0.,0.)); +#629 = PCURVE('',#100,#630); +#630 = DEFINITIONAL_REPRESENTATION('',(#631),#635); +#631 = LINE('',#632,#633); +#632 = CARTESIAN_POINT('',(49.,0.)); +#633 = VECTOR('',#634,1.); +#634 = DIRECTION('',(0.,1.)); +#635 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#636 = PCURVE('',#128,#637); +#637 = DEFINITIONAL_REPRESENTATION('',(#638),#642); +#638 = LINE('',#639,#640); +#639 = CARTESIAN_POINT('',(-0.,-1.414213562373)); +#640 = VECTOR('',#641,1.); +#641 = DIRECTION('',(-1.,0.)); +#642 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#643 = ORIENTED_EDGE('',*,*,#644,.F.); +#644 = EDGE_CURVE('',#645,#622,#647,.T.); +#645 = VERTEX_POINT('',#646); +#646 = CARTESIAN_POINT('',(3.,20.5,3.)); +#647 = SURFACE_CURVE('',#648,(#652,#659),.PCURVE_S1.); +#648 = LINE('',#649,#650); +#649 = CARTESIAN_POINT('',(3.,20.5,0.)); +#650 = VECTOR('',#651,1.); +#651 = DIRECTION('',(0.,0.,1.)); +#652 = PCURVE('',#100,#653); +#653 = DEFINITIONAL_REPRESENTATION('',(#654),#658); +#654 = LINE('',#655,#656); +#655 = CARTESIAN_POINT('',(0.,3.)); +#656 = VECTOR('',#657,1.); +#657 = DIRECTION('',(1.,0.)); +#658 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#659 = PCURVE('',#448,#660); +#660 = DEFINITIONAL_REPRESENTATION('',(#661),#665); +#661 = LINE('',#662,#663); +#662 = CARTESIAN_POINT('',(0.,-41.)); +#663 = VECTOR('',#664,1.); +#664 = DIRECTION('',(1.,0.)); +#665 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#666 = ORIENTED_EDGE('',*,*,#667,.T.); +#667 = EDGE_CURVE('',#645,#668,#670,.T.); +#668 = VERTEX_POINT('',#669); +#669 = CARTESIAN_POINT('',(33.,20.5,3.)); +#670 = SURFACE_CURVE('',#671,(#675,#682),.PCURVE_S1.); +#671 = LINE('',#672,#673); +#672 = CARTESIAN_POINT('',(0.,20.5,3.)); +#673 = VECTOR('',#674,1.); +#674 = DIRECTION('',(1.,0.,0.)); +#675 = PCURVE('',#100,#676); +#676 = DEFINITIONAL_REPRESENTATION('',(#677),#681); +#677 = LINE('',#678,#679); +#678 = CARTESIAN_POINT('',(3.,0.)); +#679 = VECTOR('',#680,1.); +#680 = DIRECTION('',(0.,1.)); +#681 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#682 = PCURVE('',#476,#683); +#683 = DEFINITIONAL_REPRESENTATION('',(#684),#688); +#684 = LINE('',#685,#686); +#685 = CARTESIAN_POINT('',(0.,41.)); +#686 = VECTOR('',#687,1.); +#687 = DIRECTION('',(1.,0.)); +#688 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#689 = ORIENTED_EDGE('',*,*,#690,.F.); +#690 = EDGE_CURVE('',#691,#668,#693,.T.); +#691 = VERTEX_POINT('',#692); +#692 = CARTESIAN_POINT('',(33.,20.5,0.)); +#693 = SURFACE_CURVE('',#694,(#698,#705),.PCURVE_S1.); +#694 = LINE('',#695,#696); +#695 = CARTESIAN_POINT('',(33.,20.5,0.)); +#696 = VECTOR('',#697,1.); +#697 = DIRECTION('',(0.,0.,1.)); +#698 = PCURVE('',#100,#699); +#699 = DEFINITIONAL_REPRESENTATION('',(#700),#704); +#700 = LINE('',#701,#702); +#701 = CARTESIAN_POINT('',(0.,33.)); +#702 = VECTOR('',#703,1.); +#703 = DIRECTION('',(1.,0.)); +#704 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#705 = PCURVE('',#706,#711); +#706 = PLANE('',#707); +#707 = AXIS2_PLACEMENT_3D('',#708,#709,#710); +#708 = CARTESIAN_POINT('',(34.,19.5,0.)); +#709 = DIRECTION('',(0.707106781187,0.707106781187,0.)); +#710 = DIRECTION('',(0.,-0.,1.)); +#711 = DEFINITIONAL_REPRESENTATION('',(#712),#716); +#712 = LINE('',#713,#714); +#713 = CARTESIAN_POINT('',(0.,-1.414213562373)); +#714 = VECTOR('',#715,1.); +#715 = DIRECTION('',(1.,0.)); +#716 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#717 = ORIENTED_EDGE('',*,*,#718,.T.); +#718 = EDGE_CURVE('',#691,#519,#719,.T.); +#719 = SURFACE_CURVE('',#720,(#724,#731),.PCURVE_S1.); +#720 = LINE('',#721,#722); +#721 = CARTESIAN_POINT('',(35.,20.5,0.)); +#722 = VECTOR('',#723,1.); +#723 = DIRECTION('',(-1.,0.,0.)); +#724 = PCURVE('',#100,#725); +#725 = DEFINITIONAL_REPRESENTATION('',(#726),#730); +#726 = LINE('',#727,#728); +#727 = CARTESIAN_POINT('',(0.,35.)); +#728 = VECTOR('',#729,1.); +#729 = DIRECTION('',(0.,-1.)); +#730 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#731 = PCURVE('',#370,#732); +#732 = DEFINITIONAL_REPRESENTATION('',(#733),#737); +#733 = LINE('',#734,#735); +#734 = CARTESIAN_POINT('',(35.,41.)); +#735 = VECTOR('',#736,1.); +#736 = DIRECTION('',(-1.,0.)); +#737 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#738 = ORIENTED_EDGE('',*,*,#541,.F.); +#739 = ADVANCED_FACE('',(#740),#156,.T.); +#740 = FACE_BOUND('',#741,.T.); +#741 = EDGE_LOOP('',(#742,#743,#744,#767)); +#742 = ORIENTED_EDGE('',*,*,#140,.F.); +#743 = ORIENTED_EDGE('',*,*,#596,.T.); +#744 = ORIENTED_EDGE('',*,*,#745,.T.); +#745 = EDGE_CURVE('',#574,#746,#748,.T.); +#746 = VERTEX_POINT('',#747); +#747 = CARTESIAN_POINT('',(3.,18.5,51.)); +#748 = SURFACE_CURVE('',#749,(#753,#760),.PCURVE_S1.); +#749 = LINE('',#750,#751); +#750 = CARTESIAN_POINT('',(3.,-20.5,51.)); +#751 = VECTOR('',#752,1.); +#752 = DIRECTION('',(0.,1.,0.)); +#753 = PCURVE('',#156,#754); +#754 = DEFINITIONAL_REPRESENTATION('',(#755),#759); +#755 = LINE('',#756,#757); +#756 = CARTESIAN_POINT('',(3.,0.)); +#757 = VECTOR('',#758,1.); +#758 = DIRECTION('',(0.,1.)); +#759 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#760 = PCURVE('',#448,#761); +#761 = DEFINITIONAL_REPRESENTATION('',(#762),#766); +#762 = LINE('',#763,#764); +#763 = CARTESIAN_POINT('',(51.,0.)); +#764 = VECTOR('',#765,1.); +#765 = DIRECTION('',(0.,-1.)); +#766 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#767 = ORIENTED_EDGE('',*,*,#768,.F.); +#768 = EDGE_CURVE('',#113,#746,#769,.T.); +#769 = SURFACE_CURVE('',#770,(#774,#781),.PCURVE_S1.); +#770 = LINE('',#771,#772); +#771 = CARTESIAN_POINT('',(0.,18.5,51.)); +#772 = VECTOR('',#773,1.); +#773 = DIRECTION('',(1.,0.,0.)); +#774 = PCURVE('',#156,#775); +#775 = DEFINITIONAL_REPRESENTATION('',(#776),#780); +#776 = LINE('',#777,#778); +#777 = CARTESIAN_POINT('',(0.,39.)); +#778 = VECTOR('',#779,1.); +#779 = DIRECTION('',(1.,0.)); +#780 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#781 = PCURVE('',#128,#782); +#782 = DEFINITIONAL_REPRESENTATION('',(#783),#787); +#783 = LINE('',#784,#785); +#784 = CARTESIAN_POINT('',(-0.,1.414213562373)); +#785 = VECTOR('',#786,1.); +#786 = DIRECTION('',(-1.,0.)); +#787 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#788 = ADVANCED_FACE('',(#789),#128,.T.); +#789 = FACE_BOUND('',#790,.F.); +#790 = EDGE_LOOP('',(#791,#792,#793,#814)); +#791 = ORIENTED_EDGE('',*,*,#112,.F.); +#792 = ORIENTED_EDGE('',*,*,#621,.T.); +#793 = ORIENTED_EDGE('',*,*,#794,.T.); +#794 = EDGE_CURVE('',#622,#746,#795,.T.); +#795 = SURFACE_CURVE('',#796,(#800,#807),.PCURVE_S1.); +#796 = LINE('',#797,#798); +#797 = CARTESIAN_POINT('',(3.,22.,47.5)); +#798 = VECTOR('',#799,1.); +#799 = DIRECTION('',(0.,-0.707106781187,0.707106781187)); +#800 = PCURVE('',#128,#801); +#801 = DEFINITIONAL_REPRESENTATION('',(#802),#806); +#802 = LINE('',#803,#804); +#803 = CARTESIAN_POINT('',(-3.,-3.535533905933)); +#804 = VECTOR('',#805,1.); +#805 = DIRECTION('',(-0.,1.)); +#806 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#807 = PCURVE('',#448,#808); +#808 = DEFINITIONAL_REPRESENTATION('',(#809),#813); +#809 = LINE('',#810,#811); +#810 = CARTESIAN_POINT('',(47.5,-42.5)); +#811 = VECTOR('',#812,1.); +#812 = DIRECTION('',(0.707106781187,0.707106781187)); +#813 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#814 = ORIENTED_EDGE('',*,*,#768,.F.); +#815 = ADVANCED_FACE('',(#816),#213,.F.); +#816 = FACE_BOUND('',#817,.T.); +#817 = EDGE_LOOP('',(#818,#841,#842,#843)); +#818 = ORIENTED_EDGE('',*,*,#819,.F.); +#819 = EDGE_CURVE('',#197,#820,#822,.T.); +#820 = VERTEX_POINT('',#821); +#821 = CARTESIAN_POINT('',(3.,17.15,14.5)); +#822 = SEAM_CURVE('',#823,(#827,#834),.PCURVE_S1.); +#823 = LINE('',#824,#825); +#824 = CARTESIAN_POINT('',(0.,17.15,14.5)); +#825 = VECTOR('',#826,1.); +#826 = DIRECTION('',(1.,0.,0.)); +#827 = PCURVE('',#213,#828); +#828 = DEFINITIONAL_REPRESENTATION('',(#829),#833); +#829 = LINE('',#830,#831); +#830 = CARTESIAN_POINT('',(-6.28318530718,0.)); +#831 = VECTOR('',#832,1.); +#832 = DIRECTION('',(-0.,-1.)); +#833 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#834 = PCURVE('',#213,#835); +#835 = DEFINITIONAL_REPRESENTATION('',(#836),#840); +#836 = LINE('',#837,#838); +#837 = CARTESIAN_POINT('',(-0.,0.)); +#838 = VECTOR('',#839,1.); +#839 = DIRECTION('',(-0.,-1.)); +#840 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#841 = ORIENTED_EDGE('',*,*,#196,.F.); +#842 = ORIENTED_EDGE('',*,*,#819,.T.); +#843 = ORIENTED_EDGE('',*,*,#844,.F.); +#844 = EDGE_CURVE('',#820,#820,#845,.T.); +#845 = SURFACE_CURVE('',#846,(#851,#858),.PCURVE_S1.); +#846 = CIRCLE('',#847,1.65); +#847 = AXIS2_PLACEMENT_3D('',#848,#849,#850); +#848 = CARTESIAN_POINT('',(3.,15.5,14.5)); +#849 = DIRECTION('',(-1.,0.,0.)); +#850 = DIRECTION('',(0.,1.,0.)); +#851 = PCURVE('',#213,#852); +#852 = DEFINITIONAL_REPRESENTATION('',(#853),#857); +#853 = LINE('',#854,#855); +#854 = CARTESIAN_POINT('',(-6.28318530718,-3.)); +#855 = VECTOR('',#856,1.); +#856 = DIRECTION('',(1.,-0.)); +#857 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#858 = PCURVE('',#448,#859); +#859 = DEFINITIONAL_REPRESENTATION('',(#860),#868); +#860 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#861,#862,#863,#864,#865,#866 +,#867),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#861 = CARTESIAN_POINT('',(14.5,-37.65)); +#862 = CARTESIAN_POINT('',(11.642116167511,-37.65)); +#863 = CARTESIAN_POINT('',(13.071058083756,-35.175)); +#864 = CARTESIAN_POINT('',(14.5,-32.7)); +#865 = CARTESIAN_POINT('',(15.928941916244,-35.175)); +#866 = CARTESIAN_POINT('',(17.357883832489,-37.65)); +#867 = CARTESIAN_POINT('',(14.5,-37.65)); +#868 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#869 = ADVANCED_FACE('',(#870),#244,.F.); +#870 = FACE_BOUND('',#871,.T.); +#871 = EDGE_LOOP('',(#872,#895,#896,#897)); +#872 = ORIENTED_EDGE('',*,*,#873,.F.); +#873 = EDGE_CURVE('',#228,#874,#876,.T.); +#874 = VERTEX_POINT('',#875); +#875 = CARTESIAN_POINT('',(3.,17.15,45.5)); +#876 = SEAM_CURVE('',#877,(#881,#888),.PCURVE_S1.); +#877 = LINE('',#878,#879); +#878 = CARTESIAN_POINT('',(0.,17.15,45.5)); +#879 = VECTOR('',#880,1.); +#880 = DIRECTION('',(1.,0.,0.)); +#881 = PCURVE('',#244,#882); +#882 = DEFINITIONAL_REPRESENTATION('',(#883),#887); +#883 = LINE('',#884,#885); +#884 = CARTESIAN_POINT('',(-6.28318530718,0.)); +#885 = VECTOR('',#886,1.); +#886 = DIRECTION('',(-0.,-1.)); +#887 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#888 = PCURVE('',#244,#889); +#889 = DEFINITIONAL_REPRESENTATION('',(#890),#894); +#890 = LINE('',#891,#892); +#891 = CARTESIAN_POINT('',(-0.,0.)); +#892 = VECTOR('',#893,1.); +#893 = DIRECTION('',(-0.,-1.)); +#894 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#895 = ORIENTED_EDGE('',*,*,#227,.F.); +#896 = ORIENTED_EDGE('',*,*,#873,.T.); +#897 = ORIENTED_EDGE('',*,*,#898,.F.); +#898 = EDGE_CURVE('',#874,#874,#899,.T.); +#899 = SURFACE_CURVE('',#900,(#905,#912),.PCURVE_S1.); +#900 = CIRCLE('',#901,1.65); +#901 = AXIS2_PLACEMENT_3D('',#902,#903,#904); +#902 = CARTESIAN_POINT('',(3.,15.5,45.5)); +#903 = DIRECTION('',(-1.,0.,0.)); +#904 = DIRECTION('',(0.,1.,0.)); +#905 = PCURVE('',#244,#906); +#906 = DEFINITIONAL_REPRESENTATION('',(#907),#911); +#907 = LINE('',#908,#909); +#908 = CARTESIAN_POINT('',(-6.28318530718,-3.)); +#909 = VECTOR('',#910,1.); +#910 = DIRECTION('',(1.,-0.)); +#911 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#912 = PCURVE('',#448,#913); +#913 = DEFINITIONAL_REPRESENTATION('',(#914),#922); +#914 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#915,#916,#917,#918,#919,#920 +,#921),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#915 = CARTESIAN_POINT('',(45.5,-37.65)); +#916 = CARTESIAN_POINT('',(42.642116167511,-37.65)); +#917 = CARTESIAN_POINT('',(44.071058083756,-35.175)); +#918 = CARTESIAN_POINT('',(45.5,-32.7)); +#919 = CARTESIAN_POINT('',(46.928941916244,-35.175)); +#920 = CARTESIAN_POINT('',(48.357883832489,-37.65)); +#921 = CARTESIAN_POINT('',(45.5,-37.65)); +#922 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#923 = ADVANCED_FACE('',(#924),#275,.F.); +#924 = FACE_BOUND('',#925,.T.); +#925 = EDGE_LOOP('',(#926,#949,#950,#951)); +#926 = ORIENTED_EDGE('',*,*,#927,.F.); +#927 = EDGE_CURVE('',#259,#928,#930,.T.); +#928 = VERTEX_POINT('',#929); +#929 = CARTESIAN_POINT('',(3.,-13.85,14.5)); +#930 = SEAM_CURVE('',#931,(#935,#942),.PCURVE_S1.); +#931 = LINE('',#932,#933); +#932 = CARTESIAN_POINT('',(0.,-13.85,14.5)); +#933 = VECTOR('',#934,1.); +#934 = DIRECTION('',(1.,0.,0.)); +#935 = PCURVE('',#275,#936); +#936 = DEFINITIONAL_REPRESENTATION('',(#937),#941); +#937 = LINE('',#938,#939); +#938 = CARTESIAN_POINT('',(-6.28318530718,0.)); +#939 = VECTOR('',#940,1.); +#940 = DIRECTION('',(-0.,-1.)); +#941 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#942 = PCURVE('',#275,#943); +#943 = DEFINITIONAL_REPRESENTATION('',(#944),#948); +#944 = LINE('',#945,#946); +#945 = CARTESIAN_POINT('',(-0.,0.)); +#946 = VECTOR('',#947,1.); +#947 = DIRECTION('',(-0.,-1.)); +#948 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#949 = ORIENTED_EDGE('',*,*,#258,.F.); +#950 = ORIENTED_EDGE('',*,*,#927,.T.); +#951 = ORIENTED_EDGE('',*,*,#952,.F.); +#952 = EDGE_CURVE('',#928,#928,#953,.T.); +#953 = SURFACE_CURVE('',#954,(#959,#966),.PCURVE_S1.); +#954 = CIRCLE('',#955,1.65); +#955 = AXIS2_PLACEMENT_3D('',#956,#957,#958); +#956 = CARTESIAN_POINT('',(3.,-15.5,14.5)); +#957 = DIRECTION('',(-1.,0.,0.)); +#958 = DIRECTION('',(0.,1.,0.)); +#959 = PCURVE('',#275,#960); +#960 = DEFINITIONAL_REPRESENTATION('',(#961),#965); +#961 = LINE('',#962,#963); +#962 = CARTESIAN_POINT('',(-6.28318530718,-3.)); +#963 = VECTOR('',#964,1.); +#964 = DIRECTION('',(1.,-0.)); +#965 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#966 = PCURVE('',#448,#967); +#967 = DEFINITIONAL_REPRESENTATION('',(#968),#976); +#968 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#969,#970,#971,#972,#973,#974 +,#975),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#969 = CARTESIAN_POINT('',(14.5,-6.65)); +#970 = CARTESIAN_POINT('',(11.642116167511,-6.65)); +#971 = CARTESIAN_POINT('',(13.071058083756,-4.175)); +#972 = CARTESIAN_POINT('',(14.5,-1.7)); +#973 = CARTESIAN_POINT('',(15.928941916244,-4.175)); +#974 = CARTESIAN_POINT('',(17.357883832489,-6.65)); +#975 = CARTESIAN_POINT('',(14.5,-6.65)); +#976 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#977 = ADVANCED_FACE('',(#978),#306,.F.); +#978 = FACE_BOUND('',#979,.T.); +#979 = EDGE_LOOP('',(#980,#1003,#1004,#1005)); +#980 = ORIENTED_EDGE('',*,*,#981,.F.); +#981 = EDGE_CURVE('',#290,#982,#984,.T.); +#982 = VERTEX_POINT('',#983); +#983 = CARTESIAN_POINT('',(3.,16.,30.)); +#984 = SEAM_CURVE('',#985,(#989,#996),.PCURVE_S1.); +#985 = LINE('',#986,#987); +#986 = CARTESIAN_POINT('',(0.,16.,30.)); +#987 = VECTOR('',#988,1.); +#988 = DIRECTION('',(1.,0.,0.)); +#989 = PCURVE('',#306,#990); +#990 = DEFINITIONAL_REPRESENTATION('',(#991),#995); +#991 = LINE('',#992,#993); +#992 = CARTESIAN_POINT('',(-6.28318530718,0.)); +#993 = VECTOR('',#994,1.); +#994 = DIRECTION('',(-0.,-1.)); +#995 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#996 = PCURVE('',#306,#997); +#997 = DEFINITIONAL_REPRESENTATION('',(#998),#1002); +#998 = LINE('',#999,#1000); +#999 = CARTESIAN_POINT('',(-0.,0.)); +#1000 = VECTOR('',#1001,1.); +#1001 = DIRECTION('',(-0.,-1.)); +#1002 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1003 = ORIENTED_EDGE('',*,*,#289,.F.); +#1004 = ORIENTED_EDGE('',*,*,#981,.T.); +#1005 = ORIENTED_EDGE('',*,*,#1006,.F.); +#1006 = EDGE_CURVE('',#982,#982,#1007,.T.); +#1007 = SURFACE_CURVE('',#1008,(#1013,#1020),.PCURVE_S1.); +#1008 = CIRCLE('',#1009,16.); +#1009 = AXIS2_PLACEMENT_3D('',#1010,#1011,#1012); +#1010 = CARTESIAN_POINT('',(3.,0.,30.)); +#1011 = DIRECTION('',(-1.,0.,0.)); +#1012 = DIRECTION('',(0.,1.,0.)); +#1013 = PCURVE('',#306,#1014); +#1014 = DEFINITIONAL_REPRESENTATION('',(#1015),#1019); +#1015 = LINE('',#1016,#1017); +#1016 = CARTESIAN_POINT('',(-6.28318530718,-3.)); +#1017 = VECTOR('',#1018,1.); +#1018 = DIRECTION('',(1.,-0.)); +#1019 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1020 = PCURVE('',#448,#1021); +#1021 = DEFINITIONAL_REPRESENTATION('',(#1022),#1030); +#1022 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1023,#1024,#1025,#1026, +#1027,#1028,#1029),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1023 = CARTESIAN_POINT('',(30.,-36.5)); +#1024 = CARTESIAN_POINT('',(2.287187078898,-36.5)); +#1025 = CARTESIAN_POINT('',(16.143593539449,-12.5)); +#1026 = CARTESIAN_POINT('',(30.,11.5)); +#1027 = CARTESIAN_POINT('',(43.856406460551,-12.5)); +#1028 = CARTESIAN_POINT('',(57.712812921102,-36.5)); +#1029 = CARTESIAN_POINT('',(30.,-36.5)); +#1030 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1031 = ADVANCED_FACE('',(#1032),#337,.F.); +#1032 = FACE_BOUND('',#1033,.T.); +#1033 = EDGE_LOOP('',(#1034,#1057,#1058,#1059)); +#1034 = ORIENTED_EDGE('',*,*,#1035,.F.); +#1035 = EDGE_CURVE('',#321,#1036,#1038,.T.); +#1036 = VERTEX_POINT('',#1037); +#1037 = CARTESIAN_POINT('',(3.,-13.85,45.5)); +#1038 = SEAM_CURVE('',#1039,(#1043,#1050),.PCURVE_S1.); +#1039 = LINE('',#1040,#1041); +#1040 = CARTESIAN_POINT('',(0.,-13.85,45.5)); +#1041 = VECTOR('',#1042,1.); +#1042 = DIRECTION('',(1.,0.,0.)); +#1043 = PCURVE('',#337,#1044); +#1044 = DEFINITIONAL_REPRESENTATION('',(#1045),#1049); +#1045 = LINE('',#1046,#1047); +#1046 = CARTESIAN_POINT('',(-6.28318530718,0.)); +#1047 = VECTOR('',#1048,1.); +#1048 = DIRECTION('',(-0.,-1.)); +#1049 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1050 = PCURVE('',#337,#1051); +#1051 = DEFINITIONAL_REPRESENTATION('',(#1052),#1056); +#1052 = LINE('',#1053,#1054); +#1053 = CARTESIAN_POINT('',(-0.,0.)); +#1054 = VECTOR('',#1055,1.); +#1055 = DIRECTION('',(-0.,-1.)); +#1056 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1057 = ORIENTED_EDGE('',*,*,#320,.F.); +#1058 = ORIENTED_EDGE('',*,*,#1035,.T.); +#1059 = ORIENTED_EDGE('',*,*,#1060,.F.); +#1060 = EDGE_CURVE('',#1036,#1036,#1061,.T.); +#1061 = SURFACE_CURVE('',#1062,(#1067,#1074),.PCURVE_S1.); +#1062 = CIRCLE('',#1063,1.65); +#1063 = AXIS2_PLACEMENT_3D('',#1064,#1065,#1066); +#1064 = CARTESIAN_POINT('',(3.,-15.5,45.5)); +#1065 = DIRECTION('',(-1.,0.,0.)); +#1066 = DIRECTION('',(0.,1.,0.)); +#1067 = PCURVE('',#337,#1068); +#1068 = DEFINITIONAL_REPRESENTATION('',(#1069),#1073); +#1069 = LINE('',#1070,#1071); +#1070 = CARTESIAN_POINT('',(-6.28318530718,-3.)); +#1071 = VECTOR('',#1072,1.); +#1072 = DIRECTION('',(1.,-0.)); +#1073 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1074 = PCURVE('',#448,#1075); +#1075 = DEFINITIONAL_REPRESENTATION('',(#1076),#1084); +#1076 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1077,#1078,#1079,#1080, +#1081,#1082,#1083),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1077 = CARTESIAN_POINT('',(45.5,-6.65)); +#1078 = CARTESIAN_POINT('',(42.642116167511,-6.65)); +#1079 = CARTESIAN_POINT('',(44.071058083756,-4.175)); +#1080 = CARTESIAN_POINT('',(45.5,-1.7)); +#1081 = CARTESIAN_POINT('',(46.928941916244,-4.175)); +#1082 = CARTESIAN_POINT('',(48.357883832489,-6.65)); +#1083 = CARTESIAN_POINT('',(45.5,-6.65)); +#1084 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1085 = ADVANCED_FACE('',(#1086,#1163,#1287,#1411,#1535,#1659,#1783), + #370,.F.); +#1086 = FACE_BOUND('',#1087,.F.); +#1087 = EDGE_LOOP('',(#1088,#1089,#1112,#1140,#1161,#1162)); +#1088 = ORIENTED_EDGE('',*,*,#352,.T.); +#1089 = ORIENTED_EDGE('',*,*,#1090,.T.); +#1090 = EDGE_CURVE('',#355,#1091,#1093,.T.); +#1091 = VERTEX_POINT('',#1092); +#1092 = CARTESIAN_POINT('',(35.,-18.5,0.)); +#1093 = SURFACE_CURVE('',#1094,(#1098,#1105),.PCURVE_S1.); +#1094 = LINE('',#1095,#1096); +#1095 = CARTESIAN_POINT('',(25.25,-28.25,0.)); +#1096 = VECTOR('',#1097,1.); +#1097 = DIRECTION('',(0.707106781187,0.707106781187,-0.)); +#1098 = PCURVE('',#370,#1099); +#1099 = DEFINITIONAL_REPRESENTATION('',(#1100),#1104); +#1100 = LINE('',#1101,#1102); +#1101 = CARTESIAN_POINT('',(25.25,-7.75)); +#1102 = VECTOR('',#1103,1.); +#1103 = DIRECTION('',(0.707106781187,0.707106781187)); +#1104 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1105 = PCURVE('',#502,#1106); +#1106 = DEFINITIONAL_REPRESENTATION('',(#1107),#1111); +#1107 = LINE('',#1108,#1109); +#1108 = CARTESIAN_POINT('',(0.,-12.37436867076)); +#1109 = VECTOR('',#1110,1.); +#1110 = DIRECTION('',(0.,1.)); +#1111 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1112 = ORIENTED_EDGE('',*,*,#1113,.T.); +#1113 = EDGE_CURVE('',#1091,#1114,#1116,.T.); +#1114 = VERTEX_POINT('',#1115); +#1115 = CARTESIAN_POINT('',(35.,18.5,0.)); +#1116 = SURFACE_CURVE('',#1117,(#1121,#1128),.PCURVE_S1.); +#1117 = LINE('',#1118,#1119); +#1118 = CARTESIAN_POINT('',(35.,-20.5,0.)); +#1119 = VECTOR('',#1120,1.); +#1120 = DIRECTION('',(0.,1.,0.)); +#1121 = PCURVE('',#370,#1122); +#1122 = DEFINITIONAL_REPRESENTATION('',(#1123),#1127); +#1123 = LINE('',#1124,#1125); +#1124 = CARTESIAN_POINT('',(35.,0.)); +#1125 = VECTOR('',#1126,1.); +#1126 = DIRECTION('',(0.,1.)); +#1127 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1128 = PCURVE('',#1129,#1134); +#1129 = PLANE('',#1130); +#1130 = AXIS2_PLACEMENT_3D('',#1131,#1132,#1133); +#1131 = CARTESIAN_POINT('',(35.,-20.5,0.)); +#1132 = DIRECTION('',(1.,0.,0.)); +#1133 = DIRECTION('',(0.,0.,1.)); +#1134 = DEFINITIONAL_REPRESENTATION('',(#1135),#1139); +#1135 = LINE('',#1136,#1137); +#1136 = CARTESIAN_POINT('',(0.,0.)); +#1137 = VECTOR('',#1138,1.); +#1138 = DIRECTION('',(0.,-1.)); +#1139 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1140 = ORIENTED_EDGE('',*,*,#1141,.F.); +#1141 = EDGE_CURVE('',#691,#1114,#1142,.T.); +#1142 = SURFACE_CURVE('',#1143,(#1147,#1154),.PCURVE_S1.); +#1143 = LINE('',#1144,#1145); +#1144 = CARTESIAN_POINT('',(35.5,18.,0.)); +#1145 = VECTOR('',#1146,1.); +#1146 = DIRECTION('',(0.707106781187,-0.707106781187,0.)); +#1147 = PCURVE('',#370,#1148); +#1148 = DEFINITIONAL_REPRESENTATION('',(#1149),#1153); +#1149 = LINE('',#1150,#1151); +#1150 = CARTESIAN_POINT('',(35.5,38.5)); +#1151 = VECTOR('',#1152,1.); +#1152 = DIRECTION('',(0.707106781187,-0.707106781187)); +#1153 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1154 = PCURVE('',#706,#1155); +#1155 = DEFINITIONAL_REPRESENTATION('',(#1156),#1160); +#1156 = LINE('',#1157,#1158); +#1157 = CARTESIAN_POINT('',(0.,2.12132034356)); +#1158 = VECTOR('',#1159,1.); +#1159 = DIRECTION('',(0.,1.)); +#1160 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1161 = ORIENTED_EDGE('',*,*,#718,.T.); +#1162 = ORIENTED_EDGE('',*,*,#518,.F.); +#1163 = FACE_BOUND('',#1164,.F.); +#1164 = EDGE_LOOP('',(#1165,#1195,#1228,#1256)); +#1165 = ORIENTED_EDGE('',*,*,#1166,.F.); +#1166 = EDGE_CURVE('',#1167,#1169,#1171,.T.); +#1167 = VERTEX_POINT('',#1168); +#1168 = CARTESIAN_POINT('',(7.75,-12.5,0.)); +#1169 = VERTEX_POINT('',#1170); +#1170 = CARTESIAN_POINT('',(7.75,-15.5,0.)); +#1171 = SURFACE_CURVE('',#1172,(#1176,#1183),.PCURVE_S1.); +#1172 = LINE('',#1173,#1174); +#1173 = CARTESIAN_POINT('',(7.75,-12.5,0.)); +#1174 = VECTOR('',#1175,1.); +#1175 = DIRECTION('',(0.,-1.,0.)); +#1176 = PCURVE('',#370,#1177); +#1177 = DEFINITIONAL_REPRESENTATION('',(#1178),#1182); +#1178 = LINE('',#1179,#1180); +#1179 = CARTESIAN_POINT('',(7.75,8.)); +#1180 = VECTOR('',#1181,1.); +#1181 = DIRECTION('',(0.,-1.)); +#1182 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1183 = PCURVE('',#1184,#1189); +#1184 = PLANE('',#1185); +#1185 = AXIS2_PLACEMENT_3D('',#1186,#1187,#1188); +#1186 = CARTESIAN_POINT('',(7.75,-12.5,0.)); +#1187 = DIRECTION('',(1.,0.,-0.)); +#1188 = DIRECTION('',(0.,-1.,0.)); +#1189 = DEFINITIONAL_REPRESENTATION('',(#1190),#1194); +#1190 = LINE('',#1191,#1192); +#1191 = CARTESIAN_POINT('',(0.,0.)); +#1192 = VECTOR('',#1193,1.); +#1193 = DIRECTION('',(1.,0.)); +#1194 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1195 = ORIENTED_EDGE('',*,*,#1196,.T.); +#1196 = EDGE_CURVE('',#1167,#1197,#1199,.T.); +#1197 = VERTEX_POINT('',#1198); +#1198 = CARTESIAN_POINT('',(12.25,-12.5,0.)); +#1199 = SURFACE_CURVE('',#1200,(#1205,#1216),.PCURVE_S1.); +#1200 = CIRCLE('',#1201,2.25); +#1201 = AXIS2_PLACEMENT_3D('',#1202,#1203,#1204); +#1202 = CARTESIAN_POINT('',(10.,-12.5,0.)); +#1203 = DIRECTION('',(-0.,-0.,-1.)); +#1204 = DIRECTION('',(0.,-1.,0.)); +#1205 = PCURVE('',#370,#1206); +#1206 = DEFINITIONAL_REPRESENTATION('',(#1207),#1215); +#1207 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1208,#1209,#1210,#1211, +#1212,#1213,#1214),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1208 = CARTESIAN_POINT('',(10.,5.75)); +#1209 = CARTESIAN_POINT('',(6.10288568297,5.75)); +#1210 = CARTESIAN_POINT('',(8.051442841485,9.125)); +#1211 = CARTESIAN_POINT('',(10.,12.5)); +#1212 = CARTESIAN_POINT('',(11.948557158515,9.125)); +#1213 = CARTESIAN_POINT('',(13.89711431703,5.75)); +#1214 = CARTESIAN_POINT('',(10.,5.75)); +#1215 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1216 = PCURVE('',#1217,#1222); +#1217 = CYLINDRICAL_SURFACE('',#1218,2.25); +#1218 = AXIS2_PLACEMENT_3D('',#1219,#1220,#1221); +#1219 = CARTESIAN_POINT('',(10.,-12.5,0.)); +#1220 = DIRECTION('',(-0.,-0.,-1.)); +#1221 = DIRECTION('',(0.,-1.,0.)); +#1222 = DEFINITIONAL_REPRESENTATION('',(#1223),#1227); +#1223 = LINE('',#1224,#1225); +#1224 = CARTESIAN_POINT('',(0.,0.)); +#1225 = VECTOR('',#1226,1.); +#1226 = DIRECTION('',(1.,0.)); +#1227 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1228 = ORIENTED_EDGE('',*,*,#1229,.F.); +#1229 = EDGE_CURVE('',#1230,#1197,#1232,.T.); +#1230 = VERTEX_POINT('',#1231); +#1231 = CARTESIAN_POINT('',(12.25,-15.5,0.)); +#1232 = SURFACE_CURVE('',#1233,(#1237,#1244),.PCURVE_S1.); +#1233 = LINE('',#1234,#1235); +#1234 = CARTESIAN_POINT('',(12.25,-15.5,0.)); +#1235 = VECTOR('',#1236,1.); +#1236 = DIRECTION('',(0.,1.,0.)); +#1237 = PCURVE('',#370,#1238); +#1238 = DEFINITIONAL_REPRESENTATION('',(#1239),#1243); +#1239 = LINE('',#1240,#1241); +#1240 = CARTESIAN_POINT('',(12.25,5.)); +#1241 = VECTOR('',#1242,1.); +#1242 = DIRECTION('',(0.,1.)); +#1243 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1244 = PCURVE('',#1245,#1250); +#1245 = PLANE('',#1246); +#1246 = AXIS2_PLACEMENT_3D('',#1247,#1248,#1249); +#1247 = CARTESIAN_POINT('',(12.25,-15.5,0.)); +#1248 = DIRECTION('',(-1.,0.,0.)); +#1249 = DIRECTION('',(0.,1.,0.)); +#1250 = DEFINITIONAL_REPRESENTATION('',(#1251),#1255); +#1251 = LINE('',#1252,#1253); +#1252 = CARTESIAN_POINT('',(0.,0.)); +#1253 = VECTOR('',#1254,1.); +#1254 = DIRECTION('',(1.,0.)); +#1255 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1256 = ORIENTED_EDGE('',*,*,#1257,.T.); +#1257 = EDGE_CURVE('',#1230,#1169,#1258,.T.); +#1258 = SURFACE_CURVE('',#1259,(#1264,#1275),.PCURVE_S1.); +#1259 = CIRCLE('',#1260,2.25); +#1260 = AXIS2_PLACEMENT_3D('',#1261,#1262,#1263); +#1261 = CARTESIAN_POINT('',(10.,-15.5,0.)); +#1262 = DIRECTION('',(0.,0.,-1.)); +#1263 = DIRECTION('',(0.,1.,0.)); +#1264 = PCURVE('',#370,#1265); +#1265 = DEFINITIONAL_REPRESENTATION('',(#1266),#1274); +#1266 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1267,#1268,#1269,#1270, +#1271,#1272,#1273),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1267 = CARTESIAN_POINT('',(10.,7.25)); +#1268 = CARTESIAN_POINT('',(13.89711431703,7.25)); +#1269 = CARTESIAN_POINT('',(11.948557158515,3.875)); +#1270 = CARTESIAN_POINT('',(10.,0.5)); +#1271 = CARTESIAN_POINT('',(8.051442841485,3.875)); +#1272 = CARTESIAN_POINT('',(6.10288568297,7.25)); +#1273 = CARTESIAN_POINT('',(10.,7.25)); +#1274 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1275 = PCURVE('',#1276,#1281); +#1276 = CYLINDRICAL_SURFACE('',#1277,2.25); +#1277 = AXIS2_PLACEMENT_3D('',#1278,#1279,#1280); +#1278 = CARTESIAN_POINT('',(10.,-15.5,0.)); +#1279 = DIRECTION('',(0.,0.,-1.)); +#1280 = DIRECTION('',(0.,1.,0.)); +#1281 = DEFINITIONAL_REPRESENTATION('',(#1282),#1286); +#1282 = LINE('',#1283,#1284); +#1283 = CARTESIAN_POINT('',(0.,0.)); +#1284 = VECTOR('',#1285,1.); +#1285 = DIRECTION('',(1.,0.)); +#1286 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1287 = FACE_BOUND('',#1288,.F.); +#1288 = EDGE_LOOP('',(#1289,#1319,#1352,#1380)); +#1289 = ORIENTED_EDGE('',*,*,#1290,.F.); +#1290 = EDGE_CURVE('',#1291,#1293,#1295,.T.); +#1291 = VERTEX_POINT('',#1292); +#1292 = CARTESIAN_POINT('',(21.5,-13.25,0.)); +#1293 = VERTEX_POINT('',#1294); +#1294 = CARTESIAN_POINT('',(18.5,-13.25,0.)); +#1295 = SURFACE_CURVE('',#1296,(#1300,#1307),.PCURVE_S1.); +#1296 = LINE('',#1297,#1298); +#1297 = CARTESIAN_POINT('',(21.5,-13.25,0.)); +#1298 = VECTOR('',#1299,1.); +#1299 = DIRECTION('',(-1.,0.,0.)); +#1300 = PCURVE('',#370,#1301); +#1301 = DEFINITIONAL_REPRESENTATION('',(#1302),#1306); +#1302 = LINE('',#1303,#1304); +#1303 = CARTESIAN_POINT('',(21.5,7.25)); +#1304 = VECTOR('',#1305,1.); +#1305 = DIRECTION('',(-1.,0.)); +#1306 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1307 = PCURVE('',#1308,#1313); +#1308 = PLANE('',#1309); +#1309 = AXIS2_PLACEMENT_3D('',#1310,#1311,#1312); +#1310 = CARTESIAN_POINT('',(21.5,-13.25,0.)); +#1311 = DIRECTION('',(0.,-1.,0.)); +#1312 = DIRECTION('',(-1.,0.,0.)); +#1313 = DEFINITIONAL_REPRESENTATION('',(#1314),#1318); +#1314 = LINE('',#1315,#1316); +#1315 = CARTESIAN_POINT('',(0.,-0.)); +#1316 = VECTOR('',#1317,1.); +#1317 = DIRECTION('',(1.,0.)); +#1318 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1319 = ORIENTED_EDGE('',*,*,#1320,.T.); +#1320 = EDGE_CURVE('',#1291,#1321,#1323,.T.); +#1321 = VERTEX_POINT('',#1322); +#1322 = CARTESIAN_POINT('',(21.5,-17.75,0.)); +#1323 = SURFACE_CURVE('',#1324,(#1329,#1340),.PCURVE_S1.); +#1324 = CIRCLE('',#1325,2.25); +#1325 = AXIS2_PLACEMENT_3D('',#1326,#1327,#1328); +#1326 = CARTESIAN_POINT('',(21.5,-15.5,0.)); +#1327 = DIRECTION('',(0.,0.,-1.)); +#1328 = DIRECTION('',(-1.,0.,0.)); +#1329 = PCURVE('',#370,#1330); +#1330 = DEFINITIONAL_REPRESENTATION('',(#1331),#1339); +#1331 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1332,#1333,#1334,#1335, +#1336,#1337,#1338),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1332 = CARTESIAN_POINT('',(19.25,5.)); +#1333 = CARTESIAN_POINT('',(19.25,8.89711431703)); +#1334 = CARTESIAN_POINT('',(22.625,6.948557158515)); +#1335 = CARTESIAN_POINT('',(26.,5.)); +#1336 = CARTESIAN_POINT('',(22.625,3.051442841485)); +#1337 = CARTESIAN_POINT('',(19.25,1.10288568297)); +#1338 = CARTESIAN_POINT('',(19.25,5.)); +#1339 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1340 = PCURVE('',#1341,#1346); +#1341 = CYLINDRICAL_SURFACE('',#1342,2.25); +#1342 = AXIS2_PLACEMENT_3D('',#1343,#1344,#1345); +#1343 = CARTESIAN_POINT('',(21.5,-15.5,0.)); +#1344 = DIRECTION('',(0.,0.,-1.)); +#1345 = DIRECTION('',(-1.,0.,0.)); +#1346 = DEFINITIONAL_REPRESENTATION('',(#1347),#1351); +#1347 = LINE('',#1348,#1349); +#1348 = CARTESIAN_POINT('',(0.,0.)); +#1349 = VECTOR('',#1350,1.); +#1350 = DIRECTION('',(1.,0.)); +#1351 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1352 = ORIENTED_EDGE('',*,*,#1353,.F.); +#1353 = EDGE_CURVE('',#1354,#1321,#1356,.T.); +#1354 = VERTEX_POINT('',#1355); +#1355 = CARTESIAN_POINT('',(18.5,-17.75,0.)); +#1356 = SURFACE_CURVE('',#1357,(#1361,#1368),.PCURVE_S1.); +#1357 = LINE('',#1358,#1359); +#1358 = CARTESIAN_POINT('',(18.5,-17.75,0.)); +#1359 = VECTOR('',#1360,1.); +#1360 = DIRECTION('',(1.,0.,0.)); +#1361 = PCURVE('',#370,#1362); +#1362 = DEFINITIONAL_REPRESENTATION('',(#1363),#1367); +#1363 = LINE('',#1364,#1365); +#1364 = CARTESIAN_POINT('',(18.5,2.75)); +#1365 = VECTOR('',#1366,1.); +#1366 = DIRECTION('',(1.,0.)); +#1367 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1368 = PCURVE('',#1369,#1374); +#1369 = PLANE('',#1370); +#1370 = AXIS2_PLACEMENT_3D('',#1371,#1372,#1373); +#1371 = CARTESIAN_POINT('',(18.5,-17.75,0.)); +#1372 = DIRECTION('',(0.,1.,0.)); +#1373 = DIRECTION('',(1.,0.,0.)); +#1374 = DEFINITIONAL_REPRESENTATION('',(#1375),#1379); +#1375 = LINE('',#1376,#1377); +#1376 = CARTESIAN_POINT('',(0.,0.)); +#1377 = VECTOR('',#1378,1.); +#1378 = DIRECTION('',(1.,0.)); +#1379 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1380 = ORIENTED_EDGE('',*,*,#1381,.T.); +#1381 = EDGE_CURVE('',#1354,#1293,#1382,.T.); +#1382 = SURFACE_CURVE('',#1383,(#1388,#1399),.PCURVE_S1.); +#1383 = CIRCLE('',#1384,2.25); +#1384 = AXIS2_PLACEMENT_3D('',#1385,#1386,#1387); +#1385 = CARTESIAN_POINT('',(18.5,-15.5,0.)); +#1386 = DIRECTION('',(0.,0.,-1.)); +#1387 = DIRECTION('',(1.,0.,0.)); +#1388 = PCURVE('',#370,#1389); +#1389 = DEFINITIONAL_REPRESENTATION('',(#1390),#1398); +#1390 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1391,#1392,#1393,#1394, +#1395,#1396,#1397),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1391 = CARTESIAN_POINT('',(20.75,5.)); +#1392 = CARTESIAN_POINT('',(20.75,1.10288568297)); +#1393 = CARTESIAN_POINT('',(17.375,3.051442841485)); +#1394 = CARTESIAN_POINT('',(14.,5.)); +#1395 = CARTESIAN_POINT('',(17.375,6.948557158515)); +#1396 = CARTESIAN_POINT('',(20.75,8.89711431703)); +#1397 = CARTESIAN_POINT('',(20.75,5.)); +#1398 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1399 = PCURVE('',#1400,#1405); +#1400 = CYLINDRICAL_SURFACE('',#1401,2.25); +#1401 = AXIS2_PLACEMENT_3D('',#1402,#1403,#1404); +#1402 = CARTESIAN_POINT('',(18.5,-15.5,0.)); +#1403 = DIRECTION('',(0.,0.,-1.)); +#1404 = DIRECTION('',(1.,0.,0.)); +#1405 = DEFINITIONAL_REPRESENTATION('',(#1406),#1410); +#1406 = LINE('',#1407,#1408); +#1407 = CARTESIAN_POINT('',(0.,0.)); +#1408 = VECTOR('',#1409,1.); +#1409 = DIRECTION('',(1.,0.)); +#1410 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1411 = FACE_BOUND('',#1412,.F.); +#1412 = EDGE_LOOP('',(#1413,#1443,#1476,#1504)); +#1413 = ORIENTED_EDGE('',*,*,#1414,.F.); +#1414 = EDGE_CURVE('',#1415,#1417,#1419,.T.); +#1415 = VERTEX_POINT('',#1416); +#1416 = CARTESIAN_POINT('',(27.75,-12.5,0.)); +#1417 = VERTEX_POINT('',#1418); +#1418 = CARTESIAN_POINT('',(27.75,-15.5,0.)); +#1419 = SURFACE_CURVE('',#1420,(#1424,#1431),.PCURVE_S1.); +#1420 = LINE('',#1421,#1422); +#1421 = CARTESIAN_POINT('',(27.75,-12.5,0.)); +#1422 = VECTOR('',#1423,1.); +#1423 = DIRECTION('',(0.,-1.,0.)); +#1424 = PCURVE('',#370,#1425); +#1425 = DEFINITIONAL_REPRESENTATION('',(#1426),#1430); +#1426 = LINE('',#1427,#1428); +#1427 = CARTESIAN_POINT('',(27.75,8.)); +#1428 = VECTOR('',#1429,1.); +#1429 = DIRECTION('',(0.,-1.)); +#1430 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1431 = PCURVE('',#1432,#1437); +#1432 = PLANE('',#1433); +#1433 = AXIS2_PLACEMENT_3D('',#1434,#1435,#1436); +#1434 = CARTESIAN_POINT('',(27.75,-12.5,0.)); +#1435 = DIRECTION('',(1.,0.,-0.)); +#1436 = DIRECTION('',(0.,-1.,0.)); +#1437 = DEFINITIONAL_REPRESENTATION('',(#1438),#1442); +#1438 = LINE('',#1439,#1440); +#1439 = CARTESIAN_POINT('',(0.,0.)); +#1440 = VECTOR('',#1441,1.); +#1441 = DIRECTION('',(1.,0.)); +#1442 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1443 = ORIENTED_EDGE('',*,*,#1444,.T.); +#1444 = EDGE_CURVE('',#1415,#1445,#1447,.T.); +#1445 = VERTEX_POINT('',#1446); +#1446 = CARTESIAN_POINT('',(32.25,-12.5,0.)); +#1447 = SURFACE_CURVE('',#1448,(#1453,#1464),.PCURVE_S1.); +#1448 = CIRCLE('',#1449,2.25); +#1449 = AXIS2_PLACEMENT_3D('',#1450,#1451,#1452); +#1450 = CARTESIAN_POINT('',(30.,-12.5,0.)); +#1451 = DIRECTION('',(-0.,-0.,-1.)); +#1452 = DIRECTION('',(0.,-1.,0.)); +#1453 = PCURVE('',#370,#1454); +#1454 = DEFINITIONAL_REPRESENTATION('',(#1455),#1463); +#1455 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1456,#1457,#1458,#1459, +#1460,#1461,#1462),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1456 = CARTESIAN_POINT('',(30.,5.75)); +#1457 = CARTESIAN_POINT('',(26.10288568297,5.75)); +#1458 = CARTESIAN_POINT('',(28.051442841485,9.125)); +#1459 = CARTESIAN_POINT('',(30.,12.5)); +#1460 = CARTESIAN_POINT('',(31.948557158515,9.125)); +#1461 = CARTESIAN_POINT('',(33.89711431703,5.75)); +#1462 = CARTESIAN_POINT('',(30.,5.75)); +#1463 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1464 = PCURVE('',#1465,#1470); +#1465 = CYLINDRICAL_SURFACE('',#1466,2.25); +#1466 = AXIS2_PLACEMENT_3D('',#1467,#1468,#1469); +#1467 = CARTESIAN_POINT('',(30.,-12.5,0.)); +#1468 = DIRECTION('',(-0.,-0.,-1.)); +#1469 = DIRECTION('',(0.,-1.,0.)); +#1470 = DEFINITIONAL_REPRESENTATION('',(#1471),#1475); +#1471 = LINE('',#1472,#1473); +#1472 = CARTESIAN_POINT('',(0.,0.)); +#1473 = VECTOR('',#1474,1.); +#1474 = DIRECTION('',(1.,0.)); +#1475 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1476 = ORIENTED_EDGE('',*,*,#1477,.F.); +#1477 = EDGE_CURVE('',#1478,#1445,#1480,.T.); +#1478 = VERTEX_POINT('',#1479); +#1479 = CARTESIAN_POINT('',(32.25,-15.5,0.)); +#1480 = SURFACE_CURVE('',#1481,(#1485,#1492),.PCURVE_S1.); +#1481 = LINE('',#1482,#1483); +#1482 = CARTESIAN_POINT('',(32.25,-15.5,0.)); +#1483 = VECTOR('',#1484,1.); +#1484 = DIRECTION('',(0.,1.,0.)); +#1485 = PCURVE('',#370,#1486); +#1486 = DEFINITIONAL_REPRESENTATION('',(#1487),#1491); +#1487 = LINE('',#1488,#1489); +#1488 = CARTESIAN_POINT('',(32.25,5.)); +#1489 = VECTOR('',#1490,1.); +#1490 = DIRECTION('',(0.,1.)); +#1491 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1492 = PCURVE('',#1493,#1498); +#1493 = PLANE('',#1494); +#1494 = AXIS2_PLACEMENT_3D('',#1495,#1496,#1497); +#1495 = CARTESIAN_POINT('',(32.25,-15.5,0.)); +#1496 = DIRECTION('',(-1.,0.,0.)); +#1497 = DIRECTION('',(0.,1.,0.)); +#1498 = DEFINITIONAL_REPRESENTATION('',(#1499),#1503); +#1499 = LINE('',#1500,#1501); +#1500 = CARTESIAN_POINT('',(0.,0.)); +#1501 = VECTOR('',#1502,1.); +#1502 = DIRECTION('',(1.,0.)); +#1503 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1504 = ORIENTED_EDGE('',*,*,#1505,.T.); +#1505 = EDGE_CURVE('',#1478,#1417,#1506,.T.); +#1506 = SURFACE_CURVE('',#1507,(#1512,#1523),.PCURVE_S1.); +#1507 = CIRCLE('',#1508,2.25); +#1508 = AXIS2_PLACEMENT_3D('',#1509,#1510,#1511); +#1509 = CARTESIAN_POINT('',(30.,-15.5,0.)); +#1510 = DIRECTION('',(0.,0.,-1.)); +#1511 = DIRECTION('',(0.,1.,0.)); +#1512 = PCURVE('',#370,#1513); +#1513 = DEFINITIONAL_REPRESENTATION('',(#1514),#1522); +#1514 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1515,#1516,#1517,#1518, +#1519,#1520,#1521),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1515 = CARTESIAN_POINT('',(30.,7.25)); +#1516 = CARTESIAN_POINT('',(33.89711431703,7.25)); +#1517 = CARTESIAN_POINT('',(31.948557158515,3.875)); +#1518 = CARTESIAN_POINT('',(30.,0.5)); +#1519 = CARTESIAN_POINT('',(28.051442841485,3.875)); +#1520 = CARTESIAN_POINT('',(26.10288568297,7.25)); +#1521 = CARTESIAN_POINT('',(30.,7.25)); +#1522 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1523 = PCURVE('',#1524,#1529); +#1524 = CYLINDRICAL_SURFACE('',#1525,2.25); +#1525 = AXIS2_PLACEMENT_3D('',#1526,#1527,#1528); +#1526 = CARTESIAN_POINT('',(30.,-15.5,0.)); +#1527 = DIRECTION('',(0.,0.,-1.)); +#1528 = DIRECTION('',(0.,1.,0.)); +#1529 = DEFINITIONAL_REPRESENTATION('',(#1530),#1534); +#1530 = LINE('',#1531,#1532); +#1531 = CARTESIAN_POINT('',(0.,0.)); +#1532 = VECTOR('',#1533,1.); +#1533 = DIRECTION('',(1.,0.)); +#1534 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1535 = FACE_BOUND('',#1536,.F.); +#1536 = EDGE_LOOP('',(#1537,#1567,#1600,#1628)); +#1537 = ORIENTED_EDGE('',*,*,#1538,.F.); +#1538 = EDGE_CURVE('',#1539,#1541,#1543,.T.); +#1539 = VERTEX_POINT('',#1540); +#1540 = CARTESIAN_POINT('',(7.75,15.5,0.)); +#1541 = VERTEX_POINT('',#1542); +#1542 = CARTESIAN_POINT('',(7.75,12.5,0.)); +#1543 = SURFACE_CURVE('',#1544,(#1548,#1555),.PCURVE_S1.); +#1544 = LINE('',#1545,#1546); +#1545 = CARTESIAN_POINT('',(7.75,15.5,0.)); +#1546 = VECTOR('',#1547,1.); +#1547 = DIRECTION('',(0.,-1.,0.)); +#1548 = PCURVE('',#370,#1549); +#1549 = DEFINITIONAL_REPRESENTATION('',(#1550),#1554); +#1550 = LINE('',#1551,#1552); +#1551 = CARTESIAN_POINT('',(7.75,36.)); +#1552 = VECTOR('',#1553,1.); +#1553 = DIRECTION('',(0.,-1.)); +#1554 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1555 = PCURVE('',#1556,#1561); +#1556 = PLANE('',#1557); +#1557 = AXIS2_PLACEMENT_3D('',#1558,#1559,#1560); +#1558 = CARTESIAN_POINT('',(7.75,15.5,0.)); +#1559 = DIRECTION('',(1.,0.,-0.)); +#1560 = DIRECTION('',(0.,-1.,0.)); +#1561 = DEFINITIONAL_REPRESENTATION('',(#1562),#1566); +#1562 = LINE('',#1563,#1564); +#1563 = CARTESIAN_POINT('',(0.,0.)); +#1564 = VECTOR('',#1565,1.); +#1565 = DIRECTION('',(1.,0.)); +#1566 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1567 = ORIENTED_EDGE('',*,*,#1568,.T.); +#1568 = EDGE_CURVE('',#1539,#1569,#1571,.T.); +#1569 = VERTEX_POINT('',#1570); +#1570 = CARTESIAN_POINT('',(12.25,15.5,0.)); +#1571 = SURFACE_CURVE('',#1572,(#1577,#1588),.PCURVE_S1.); +#1572 = CIRCLE('',#1573,2.25); +#1573 = AXIS2_PLACEMENT_3D('',#1574,#1575,#1576); +#1574 = CARTESIAN_POINT('',(10.,15.5,0.)); +#1575 = DIRECTION('',(-0.,-0.,-1.)); +#1576 = DIRECTION('',(0.,-1.,0.)); +#1577 = PCURVE('',#370,#1578); +#1578 = DEFINITIONAL_REPRESENTATION('',(#1579),#1587); +#1579 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1580,#1581,#1582,#1583, +#1584,#1585,#1586),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1580 = CARTESIAN_POINT('',(10.,33.75)); +#1581 = CARTESIAN_POINT('',(6.10288568297,33.75)); +#1582 = CARTESIAN_POINT('',(8.051442841485,37.125)); +#1583 = CARTESIAN_POINT('',(10.,40.5)); +#1584 = CARTESIAN_POINT('',(11.948557158515,37.125)); +#1585 = CARTESIAN_POINT('',(13.89711431703,33.75)); +#1586 = CARTESIAN_POINT('',(10.,33.75)); +#1587 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1588 = PCURVE('',#1589,#1594); +#1589 = CYLINDRICAL_SURFACE('',#1590,2.25); +#1590 = AXIS2_PLACEMENT_3D('',#1591,#1592,#1593); +#1591 = CARTESIAN_POINT('',(10.,15.5,0.)); +#1592 = DIRECTION('',(-0.,-0.,-1.)); +#1593 = DIRECTION('',(0.,-1.,0.)); +#1594 = DEFINITIONAL_REPRESENTATION('',(#1595),#1599); +#1595 = LINE('',#1596,#1597); +#1596 = CARTESIAN_POINT('',(0.,0.)); +#1597 = VECTOR('',#1598,1.); +#1598 = DIRECTION('',(1.,0.)); +#1599 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1600 = ORIENTED_EDGE('',*,*,#1601,.F.); +#1601 = EDGE_CURVE('',#1602,#1569,#1604,.T.); +#1602 = VERTEX_POINT('',#1603); +#1603 = CARTESIAN_POINT('',(12.25,12.5,0.)); +#1604 = SURFACE_CURVE('',#1605,(#1609,#1616),.PCURVE_S1.); +#1605 = LINE('',#1606,#1607); +#1606 = CARTESIAN_POINT('',(12.25,12.5,0.)); +#1607 = VECTOR('',#1608,1.); +#1608 = DIRECTION('',(0.,1.,0.)); +#1609 = PCURVE('',#370,#1610); +#1610 = DEFINITIONAL_REPRESENTATION('',(#1611),#1615); +#1611 = LINE('',#1612,#1613); +#1612 = CARTESIAN_POINT('',(12.25,33.)); +#1613 = VECTOR('',#1614,1.); +#1614 = DIRECTION('',(0.,1.)); +#1615 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1616 = PCURVE('',#1617,#1622); +#1617 = PLANE('',#1618); +#1618 = AXIS2_PLACEMENT_3D('',#1619,#1620,#1621); +#1619 = CARTESIAN_POINT('',(12.25,12.5,0.)); +#1620 = DIRECTION('',(-1.,0.,0.)); +#1621 = DIRECTION('',(0.,1.,0.)); +#1622 = DEFINITIONAL_REPRESENTATION('',(#1623),#1627); +#1623 = LINE('',#1624,#1625); +#1624 = CARTESIAN_POINT('',(0.,0.)); +#1625 = VECTOR('',#1626,1.); +#1626 = DIRECTION('',(1.,0.)); +#1627 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1628 = ORIENTED_EDGE('',*,*,#1629,.T.); +#1629 = EDGE_CURVE('',#1602,#1541,#1630,.T.); +#1630 = SURFACE_CURVE('',#1631,(#1636,#1647),.PCURVE_S1.); +#1631 = CIRCLE('',#1632,2.25); +#1632 = AXIS2_PLACEMENT_3D('',#1633,#1634,#1635); +#1633 = CARTESIAN_POINT('',(10.,12.5,0.)); +#1634 = DIRECTION('',(0.,0.,-1.)); +#1635 = DIRECTION('',(0.,1.,0.)); +#1636 = PCURVE('',#370,#1637); +#1637 = DEFINITIONAL_REPRESENTATION('',(#1638),#1646); +#1638 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1639,#1640,#1641,#1642, +#1643,#1644,#1645),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1639 = CARTESIAN_POINT('',(10.,35.25)); +#1640 = CARTESIAN_POINT('',(13.89711431703,35.25)); +#1641 = CARTESIAN_POINT('',(11.948557158515,31.875)); +#1642 = CARTESIAN_POINT('',(10.,28.5)); +#1643 = CARTESIAN_POINT('',(8.051442841485,31.875)); +#1644 = CARTESIAN_POINT('',(6.10288568297,35.25)); +#1645 = CARTESIAN_POINT('',(10.,35.25)); +#1646 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1647 = PCURVE('',#1648,#1653); +#1648 = CYLINDRICAL_SURFACE('',#1649,2.25); +#1649 = AXIS2_PLACEMENT_3D('',#1650,#1651,#1652); +#1650 = CARTESIAN_POINT('',(10.,12.5,0.)); +#1651 = DIRECTION('',(0.,0.,-1.)); +#1652 = DIRECTION('',(0.,1.,0.)); +#1653 = DEFINITIONAL_REPRESENTATION('',(#1654),#1658); +#1654 = LINE('',#1655,#1656); +#1655 = CARTESIAN_POINT('',(0.,0.)); +#1656 = VECTOR('',#1657,1.); +#1657 = DIRECTION('',(1.,0.)); +#1658 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1659 = FACE_BOUND('',#1660,.F.); +#1660 = EDGE_LOOP('',(#1661,#1691,#1724,#1752)); +#1661 = ORIENTED_EDGE('',*,*,#1662,.F.); +#1662 = EDGE_CURVE('',#1663,#1665,#1667,.T.); +#1663 = VERTEX_POINT('',#1664); +#1664 = CARTESIAN_POINT('',(21.5,17.75,0.)); +#1665 = VERTEX_POINT('',#1666); +#1666 = CARTESIAN_POINT('',(18.5,17.75,0.)); +#1667 = SURFACE_CURVE('',#1668,(#1672,#1679),.PCURVE_S1.); +#1668 = LINE('',#1669,#1670); +#1669 = CARTESIAN_POINT('',(21.5,17.75,0.)); +#1670 = VECTOR('',#1671,1.); +#1671 = DIRECTION('',(-1.,0.,0.)); +#1672 = PCURVE('',#370,#1673); +#1673 = DEFINITIONAL_REPRESENTATION('',(#1674),#1678); +#1674 = LINE('',#1675,#1676); +#1675 = CARTESIAN_POINT('',(21.5,38.25)); +#1676 = VECTOR('',#1677,1.); +#1677 = DIRECTION('',(-1.,0.)); +#1678 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1679 = PCURVE('',#1680,#1685); +#1680 = PLANE('',#1681); +#1681 = AXIS2_PLACEMENT_3D('',#1682,#1683,#1684); +#1682 = CARTESIAN_POINT('',(21.5,17.75,0.)); +#1683 = DIRECTION('',(0.,-1.,0.)); +#1684 = DIRECTION('',(-1.,0.,0.)); +#1685 = DEFINITIONAL_REPRESENTATION('',(#1686),#1690); +#1686 = LINE('',#1687,#1688); +#1687 = CARTESIAN_POINT('',(0.,-0.)); +#1688 = VECTOR('',#1689,1.); +#1689 = DIRECTION('',(1.,0.)); +#1690 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1691 = ORIENTED_EDGE('',*,*,#1692,.T.); +#1692 = EDGE_CURVE('',#1663,#1693,#1695,.T.); +#1693 = VERTEX_POINT('',#1694); +#1694 = CARTESIAN_POINT('',(21.5,13.25,0.)); +#1695 = SURFACE_CURVE('',#1696,(#1701,#1712),.PCURVE_S1.); +#1696 = CIRCLE('',#1697,2.25); +#1697 = AXIS2_PLACEMENT_3D('',#1698,#1699,#1700); +#1698 = CARTESIAN_POINT('',(21.5,15.5,0.)); +#1699 = DIRECTION('',(0.,0.,-1.)); +#1700 = DIRECTION('',(-1.,0.,0.)); +#1701 = PCURVE('',#370,#1702); +#1702 = DEFINITIONAL_REPRESENTATION('',(#1703),#1711); +#1703 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1704,#1705,#1706,#1707, +#1708,#1709,#1710),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1704 = CARTESIAN_POINT('',(19.25,36.)); +#1705 = CARTESIAN_POINT('',(19.25,39.89711431703)); +#1706 = CARTESIAN_POINT('',(22.625,37.948557158515)); +#1707 = CARTESIAN_POINT('',(26.,36.)); +#1708 = CARTESIAN_POINT('',(22.625,34.051442841485)); +#1709 = CARTESIAN_POINT('',(19.25,32.10288568297)); +#1710 = CARTESIAN_POINT('',(19.25,36.)); +#1711 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1712 = PCURVE('',#1713,#1718); +#1713 = CYLINDRICAL_SURFACE('',#1714,2.25); +#1714 = AXIS2_PLACEMENT_3D('',#1715,#1716,#1717); +#1715 = CARTESIAN_POINT('',(21.5,15.5,0.)); +#1716 = DIRECTION('',(0.,0.,-1.)); +#1717 = DIRECTION('',(-1.,0.,0.)); +#1718 = DEFINITIONAL_REPRESENTATION('',(#1719),#1723); +#1719 = LINE('',#1720,#1721); +#1720 = CARTESIAN_POINT('',(0.,0.)); +#1721 = VECTOR('',#1722,1.); +#1722 = DIRECTION('',(1.,0.)); +#1723 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1724 = ORIENTED_EDGE('',*,*,#1725,.F.); +#1725 = EDGE_CURVE('',#1726,#1693,#1728,.T.); +#1726 = VERTEX_POINT('',#1727); +#1727 = CARTESIAN_POINT('',(18.5,13.25,0.)); +#1728 = SURFACE_CURVE('',#1729,(#1733,#1740),.PCURVE_S1.); +#1729 = LINE('',#1730,#1731); +#1730 = CARTESIAN_POINT('',(18.5,13.25,0.)); +#1731 = VECTOR('',#1732,1.); +#1732 = DIRECTION('',(1.,0.,0.)); +#1733 = PCURVE('',#370,#1734); +#1734 = DEFINITIONAL_REPRESENTATION('',(#1735),#1739); +#1735 = LINE('',#1736,#1737); +#1736 = CARTESIAN_POINT('',(18.5,33.75)); +#1737 = VECTOR('',#1738,1.); +#1738 = DIRECTION('',(1.,0.)); +#1739 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1740 = PCURVE('',#1741,#1746); +#1741 = PLANE('',#1742); +#1742 = AXIS2_PLACEMENT_3D('',#1743,#1744,#1745); +#1743 = CARTESIAN_POINT('',(18.5,13.25,0.)); +#1744 = DIRECTION('',(0.,1.,0.)); +#1745 = DIRECTION('',(1.,0.,0.)); +#1746 = DEFINITIONAL_REPRESENTATION('',(#1747),#1751); +#1747 = LINE('',#1748,#1749); +#1748 = CARTESIAN_POINT('',(0.,0.)); +#1749 = VECTOR('',#1750,1.); +#1750 = DIRECTION('',(1.,0.)); +#1751 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1752 = ORIENTED_EDGE('',*,*,#1753,.T.); +#1753 = EDGE_CURVE('',#1726,#1665,#1754,.T.); +#1754 = SURFACE_CURVE('',#1755,(#1760,#1771),.PCURVE_S1.); +#1755 = CIRCLE('',#1756,2.25); +#1756 = AXIS2_PLACEMENT_3D('',#1757,#1758,#1759); +#1757 = CARTESIAN_POINT('',(18.5,15.5,0.)); +#1758 = DIRECTION('',(0.,0.,-1.)); +#1759 = DIRECTION('',(1.,0.,0.)); +#1760 = PCURVE('',#370,#1761); +#1761 = DEFINITIONAL_REPRESENTATION('',(#1762),#1770); +#1762 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1763,#1764,#1765,#1766, +#1767,#1768,#1769),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1763 = CARTESIAN_POINT('',(20.75,36.)); +#1764 = CARTESIAN_POINT('',(20.75,32.10288568297)); +#1765 = CARTESIAN_POINT('',(17.375,34.051442841485)); +#1766 = CARTESIAN_POINT('',(14.,36.)); +#1767 = CARTESIAN_POINT('',(17.375,37.948557158515)); +#1768 = CARTESIAN_POINT('',(20.75,39.89711431703)); +#1769 = CARTESIAN_POINT('',(20.75,36.)); +#1770 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1771 = PCURVE('',#1772,#1777); +#1772 = CYLINDRICAL_SURFACE('',#1773,2.25); +#1773 = AXIS2_PLACEMENT_3D('',#1774,#1775,#1776); +#1774 = CARTESIAN_POINT('',(18.5,15.5,0.)); +#1775 = DIRECTION('',(0.,0.,-1.)); +#1776 = DIRECTION('',(1.,0.,0.)); +#1777 = DEFINITIONAL_REPRESENTATION('',(#1778),#1782); +#1778 = LINE('',#1779,#1780); +#1779 = CARTESIAN_POINT('',(0.,0.)); +#1780 = VECTOR('',#1781,1.); +#1781 = DIRECTION('',(1.,0.)); +#1782 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1783 = FACE_BOUND('',#1784,.F.); +#1784 = EDGE_LOOP('',(#1785,#1815,#1848,#1876)); +#1785 = ORIENTED_EDGE('',*,*,#1786,.F.); +#1786 = EDGE_CURVE('',#1787,#1789,#1791,.T.); +#1787 = VERTEX_POINT('',#1788); +#1788 = CARTESIAN_POINT('',(27.75,15.5,0.)); +#1789 = VERTEX_POINT('',#1790); +#1790 = CARTESIAN_POINT('',(27.75,12.5,0.)); +#1791 = SURFACE_CURVE('',#1792,(#1796,#1803),.PCURVE_S1.); +#1792 = LINE('',#1793,#1794); +#1793 = CARTESIAN_POINT('',(27.75,15.5,0.)); +#1794 = VECTOR('',#1795,1.); +#1795 = DIRECTION('',(0.,-1.,0.)); +#1796 = PCURVE('',#370,#1797); +#1797 = DEFINITIONAL_REPRESENTATION('',(#1798),#1802); +#1798 = LINE('',#1799,#1800); +#1799 = CARTESIAN_POINT('',(27.75,36.)); +#1800 = VECTOR('',#1801,1.); +#1801 = DIRECTION('',(0.,-1.)); +#1802 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1803 = PCURVE('',#1804,#1809); +#1804 = PLANE('',#1805); +#1805 = AXIS2_PLACEMENT_3D('',#1806,#1807,#1808); +#1806 = CARTESIAN_POINT('',(27.75,15.5,0.)); +#1807 = DIRECTION('',(1.,0.,-0.)); +#1808 = DIRECTION('',(0.,-1.,0.)); +#1809 = DEFINITIONAL_REPRESENTATION('',(#1810),#1814); +#1810 = LINE('',#1811,#1812); +#1811 = CARTESIAN_POINT('',(0.,0.)); +#1812 = VECTOR('',#1813,1.); +#1813 = DIRECTION('',(1.,0.)); +#1814 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1815 = ORIENTED_EDGE('',*,*,#1816,.T.); +#1816 = EDGE_CURVE('',#1787,#1817,#1819,.T.); +#1817 = VERTEX_POINT('',#1818); +#1818 = CARTESIAN_POINT('',(32.25,15.5,0.)); +#1819 = SURFACE_CURVE('',#1820,(#1825,#1836),.PCURVE_S1.); +#1820 = CIRCLE('',#1821,2.25); +#1821 = AXIS2_PLACEMENT_3D('',#1822,#1823,#1824); +#1822 = CARTESIAN_POINT('',(30.,15.5,0.)); +#1823 = DIRECTION('',(-0.,-0.,-1.)); +#1824 = DIRECTION('',(0.,-1.,0.)); +#1825 = PCURVE('',#370,#1826); +#1826 = DEFINITIONAL_REPRESENTATION('',(#1827),#1835); +#1827 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1828,#1829,#1830,#1831, +#1832,#1833,#1834),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1828 = CARTESIAN_POINT('',(30.,33.75)); +#1829 = CARTESIAN_POINT('',(26.10288568297,33.75)); +#1830 = CARTESIAN_POINT('',(28.051442841485,37.125)); +#1831 = CARTESIAN_POINT('',(30.,40.5)); +#1832 = CARTESIAN_POINT('',(31.948557158515,37.125)); +#1833 = CARTESIAN_POINT('',(33.89711431703,33.75)); +#1834 = CARTESIAN_POINT('',(30.,33.75)); +#1835 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1836 = PCURVE('',#1837,#1842); +#1837 = CYLINDRICAL_SURFACE('',#1838,2.25); +#1838 = AXIS2_PLACEMENT_3D('',#1839,#1840,#1841); +#1839 = CARTESIAN_POINT('',(30.,15.5,0.)); +#1840 = DIRECTION('',(-0.,-0.,-1.)); +#1841 = DIRECTION('',(0.,-1.,0.)); +#1842 = DEFINITIONAL_REPRESENTATION('',(#1843),#1847); +#1843 = LINE('',#1844,#1845); +#1844 = CARTESIAN_POINT('',(0.,0.)); +#1845 = VECTOR('',#1846,1.); +#1846 = DIRECTION('',(1.,0.)); +#1847 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1848 = ORIENTED_EDGE('',*,*,#1849,.F.); +#1849 = EDGE_CURVE('',#1850,#1817,#1852,.T.); +#1850 = VERTEX_POINT('',#1851); +#1851 = CARTESIAN_POINT('',(32.25,12.5,0.)); +#1852 = SURFACE_CURVE('',#1853,(#1857,#1864),.PCURVE_S1.); +#1853 = LINE('',#1854,#1855); +#1854 = CARTESIAN_POINT('',(32.25,12.5,0.)); +#1855 = VECTOR('',#1856,1.); +#1856 = DIRECTION('',(0.,1.,0.)); +#1857 = PCURVE('',#370,#1858); +#1858 = DEFINITIONAL_REPRESENTATION('',(#1859),#1863); +#1859 = LINE('',#1860,#1861); +#1860 = CARTESIAN_POINT('',(32.25,33.)); +#1861 = VECTOR('',#1862,1.); +#1862 = DIRECTION('',(0.,1.)); +#1863 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1864 = PCURVE('',#1865,#1870); +#1865 = PLANE('',#1866); +#1866 = AXIS2_PLACEMENT_3D('',#1867,#1868,#1869); +#1867 = CARTESIAN_POINT('',(32.25,12.5,0.)); +#1868 = DIRECTION('',(-1.,0.,0.)); +#1869 = DIRECTION('',(0.,1.,0.)); +#1870 = DEFINITIONAL_REPRESENTATION('',(#1871),#1875); +#1871 = LINE('',#1872,#1873); +#1872 = CARTESIAN_POINT('',(0.,0.)); +#1873 = VECTOR('',#1874,1.); +#1874 = DIRECTION('',(1.,0.)); +#1875 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1876 = ORIENTED_EDGE('',*,*,#1877,.T.); +#1877 = EDGE_CURVE('',#1850,#1789,#1878,.T.); +#1878 = SURFACE_CURVE('',#1879,(#1884,#1895),.PCURVE_S1.); +#1879 = CIRCLE('',#1880,2.25); +#1880 = AXIS2_PLACEMENT_3D('',#1881,#1882,#1883); +#1881 = CARTESIAN_POINT('',(30.,12.5,0.)); +#1882 = DIRECTION('',(0.,0.,-1.)); +#1883 = DIRECTION('',(0.,1.,0.)); +#1884 = PCURVE('',#370,#1885); +#1885 = DEFINITIONAL_REPRESENTATION('',(#1886),#1894); +#1886 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#1887,#1888,#1889,#1890, +#1891,#1892,#1893),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#1887 = CARTESIAN_POINT('',(30.,35.25)); +#1888 = CARTESIAN_POINT('',(33.89711431703,35.25)); +#1889 = CARTESIAN_POINT('',(31.948557158515,31.875)); +#1890 = CARTESIAN_POINT('',(30.,28.5)); +#1891 = CARTESIAN_POINT('',(28.051442841485,31.875)); +#1892 = CARTESIAN_POINT('',(26.10288568297,35.25)); +#1893 = CARTESIAN_POINT('',(30.,35.25)); +#1894 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1895 = PCURVE('',#1896,#1901); +#1896 = CYLINDRICAL_SURFACE('',#1897,2.25); +#1897 = AXIS2_PLACEMENT_3D('',#1898,#1899,#1900); +#1898 = CARTESIAN_POINT('',(30.,12.5,0.)); +#1899 = DIRECTION('',(0.,0.,-1.)); +#1900 = DIRECTION('',(0.,1.,0.)); +#1901 = DEFINITIONAL_REPRESENTATION('',(#1902),#1906); +#1902 = LINE('',#1903,#1904); +#1903 = CARTESIAN_POINT('',(0.,0.)); +#1904 = VECTOR('',#1905,1.); +#1905 = DIRECTION('',(1.,0.)); +#1906 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1907 = ADVANCED_FACE('',(#1908),#502,.F.); +#1908 = FACE_BOUND('',#1909,.F.); +#1909 = EDGE_LOOP('',(#1910,#1911,#1912,#1935)); +#1910 = ORIENTED_EDGE('',*,*,#1090,.F.); +#1911 = ORIENTED_EDGE('',*,*,#488,.T.); +#1912 = ORIENTED_EDGE('',*,*,#1913,.T.); +#1913 = EDGE_CURVE('',#461,#1914,#1916,.T.); +#1914 = VERTEX_POINT('',#1915); +#1915 = CARTESIAN_POINT('',(35.,-18.5,3.)); +#1916 = SURFACE_CURVE('',#1917,(#1921,#1928),.PCURVE_S1.); +#1917 = LINE('',#1918,#1919); +#1918 = CARTESIAN_POINT('',(25.25,-28.25,3.)); +#1919 = VECTOR('',#1920,1.); +#1920 = DIRECTION('',(0.707106781187,0.707106781187,-0.)); +#1921 = PCURVE('',#502,#1922); +#1922 = DEFINITIONAL_REPRESENTATION('',(#1923),#1927); +#1923 = LINE('',#1924,#1925); +#1924 = CARTESIAN_POINT('',(3.,-12.37436867076)); +#1925 = VECTOR('',#1926,1.); +#1926 = DIRECTION('',(0.,1.)); +#1927 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1928 = PCURVE('',#476,#1929); +#1929 = DEFINITIONAL_REPRESENTATION('',(#1930),#1934); +#1930 = LINE('',#1931,#1932); +#1931 = CARTESIAN_POINT('',(25.25,-7.75)); +#1932 = VECTOR('',#1933,1.); +#1933 = DIRECTION('',(0.707106781187,0.707106781187)); +#1934 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1935 = ORIENTED_EDGE('',*,*,#1936,.F.); +#1936 = EDGE_CURVE('',#1091,#1914,#1937,.T.); +#1937 = SURFACE_CURVE('',#1938,(#1942,#1949),.PCURVE_S1.); +#1938 = LINE('',#1939,#1940); +#1939 = CARTESIAN_POINT('',(35.,-18.5,0.)); +#1940 = VECTOR('',#1941,1.); +#1941 = DIRECTION('',(0.,0.,1.)); +#1942 = PCURVE('',#502,#1943); +#1943 = DEFINITIONAL_REPRESENTATION('',(#1944),#1948); +#1944 = LINE('',#1945,#1946); +#1945 = CARTESIAN_POINT('',(0.,1.414213562373)); +#1946 = VECTOR('',#1947,1.); +#1947 = DIRECTION('',(1.,0.)); +#1948 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1949 = PCURVE('',#1129,#1950); +#1950 = DEFINITIONAL_REPRESENTATION('',(#1951),#1955); +#1951 = LINE('',#1952,#1953); +#1952 = CARTESIAN_POINT('',(0.,-2.)); +#1953 = VECTOR('',#1954,1.); +#1954 = DIRECTION('',(1.,0.)); +#1955 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1956 = ADVANCED_FACE('',(#1957,#2025,#2125,#2225,#2325,#2425,#2525), + #476,.T.); +#1957 = FACE_BOUND('',#1958,.T.); +#1958 = EDGE_LOOP('',(#1959,#1960,#1979,#1980,#1981,#2004)); +#1959 = ORIENTED_EDGE('',*,*,#667,.F.); +#1960 = ORIENTED_EDGE('',*,*,#1961,.F.); +#1961 = EDGE_CURVE('',#433,#645,#1962,.T.); +#1962 = SURFACE_CURVE('',#1963,(#1967,#1973),.PCURVE_S1.); +#1963 = LINE('',#1964,#1965); +#1964 = CARTESIAN_POINT('',(3.,-20.5,3.)); +#1965 = VECTOR('',#1966,1.); +#1966 = DIRECTION('',(0.,1.,0.)); +#1967 = PCURVE('',#476,#1968); +#1968 = DEFINITIONAL_REPRESENTATION('',(#1969),#1972); +#1969 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#1970,#1971),.UNSPECIFIED.,.F., + .F.,(2,2),(0.,41.),.PIECEWISE_BEZIER_KNOTS.); +#1970 = CARTESIAN_POINT('',(3.,0.)); +#1971 = CARTESIAN_POINT('',(3.,41.)); +#1972 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1973 = PCURVE('',#448,#1974); +#1974 = DEFINITIONAL_REPRESENTATION('',(#1975),#1978); +#1975 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#1976,#1977),.UNSPECIFIED.,.F., + .F.,(2,2),(0.,41.),.PIECEWISE_BEZIER_KNOTS.); +#1976 = CARTESIAN_POINT('',(3.,0.)); +#1977 = CARTESIAN_POINT('',(3.,-41.)); +#1978 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1979 = ORIENTED_EDGE('',*,*,#460,.T.); +#1980 = ORIENTED_EDGE('',*,*,#1913,.T.); +#1981 = ORIENTED_EDGE('',*,*,#1982,.T.); +#1982 = EDGE_CURVE('',#1914,#1983,#1985,.T.); +#1983 = VERTEX_POINT('',#1984); +#1984 = CARTESIAN_POINT('',(35.,18.5,3.)); +#1985 = SURFACE_CURVE('',#1986,(#1990,#1997),.PCURVE_S1.); +#1986 = LINE('',#1987,#1988); +#1987 = CARTESIAN_POINT('',(35.,-20.5,3.)); +#1988 = VECTOR('',#1989,1.); +#1989 = DIRECTION('',(0.,1.,0.)); +#1990 = PCURVE('',#476,#1991); +#1991 = DEFINITIONAL_REPRESENTATION('',(#1992),#1996); +#1992 = LINE('',#1993,#1994); +#1993 = CARTESIAN_POINT('',(35.,0.)); +#1994 = VECTOR('',#1995,1.); +#1995 = DIRECTION('',(0.,1.)); +#1996 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#1997 = PCURVE('',#1129,#1998); +#1998 = DEFINITIONAL_REPRESENTATION('',(#1999),#2003); +#1999 = LINE('',#2000,#2001); +#2000 = CARTESIAN_POINT('',(3.,0.)); +#2001 = VECTOR('',#2002,1.); +#2002 = DIRECTION('',(0.,-1.)); +#2003 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2004 = ORIENTED_EDGE('',*,*,#2005,.F.); +#2005 = EDGE_CURVE('',#668,#1983,#2006,.T.); +#2006 = SURFACE_CURVE('',#2007,(#2011,#2018),.PCURVE_S1.); +#2007 = LINE('',#2008,#2009); +#2008 = CARTESIAN_POINT('',(35.5,18.,3.)); +#2009 = VECTOR('',#2010,1.); +#2010 = DIRECTION('',(0.707106781187,-0.707106781187,0.)); +#2011 = PCURVE('',#476,#2012); +#2012 = DEFINITIONAL_REPRESENTATION('',(#2013),#2017); +#2013 = LINE('',#2014,#2015); +#2014 = CARTESIAN_POINT('',(35.5,38.5)); +#2015 = VECTOR('',#2016,1.); +#2016 = DIRECTION('',(0.707106781187,-0.707106781187)); +#2017 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2018 = PCURVE('',#706,#2019); +#2019 = DEFINITIONAL_REPRESENTATION('',(#2020),#2024); +#2020 = LINE('',#2021,#2022); +#2021 = CARTESIAN_POINT('',(3.,2.12132034356)); +#2022 = VECTOR('',#2023,1.); +#2023 = DIRECTION('',(0.,1.)); +#2024 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2025 = FACE_BOUND('',#2026,.T.); +#2026 = EDGE_LOOP('',(#2027,#2050,#2078,#2099)); +#2027 = ORIENTED_EDGE('',*,*,#2028,.F.); +#2028 = EDGE_CURVE('',#2029,#2031,#2033,.T.); +#2029 = VERTEX_POINT('',#2030); +#2030 = CARTESIAN_POINT('',(7.75,-12.5,3.)); +#2031 = VERTEX_POINT('',#2032); +#2032 = CARTESIAN_POINT('',(7.75,-15.5,3.)); +#2033 = SURFACE_CURVE('',#2034,(#2038,#2044),.PCURVE_S1.); +#2034 = LINE('',#2035,#2036); +#2035 = CARTESIAN_POINT('',(7.75,-16.5,3.)); +#2036 = VECTOR('',#2037,1.); +#2037 = DIRECTION('',(0.,-1.,0.)); +#2038 = PCURVE('',#476,#2039); +#2039 = DEFINITIONAL_REPRESENTATION('',(#2040),#2043); +#2040 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2041,#2042),.UNSPECIFIED.,.F., + .F.,(2,2),(-4.,-1.),.PIECEWISE_BEZIER_KNOTS.); +#2041 = CARTESIAN_POINT('',(7.75,8.)); +#2042 = CARTESIAN_POINT('',(7.75,5.)); +#2043 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2044 = PCURVE('',#1184,#2045); +#2045 = DEFINITIONAL_REPRESENTATION('',(#2046),#2049); +#2046 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2047,#2048),.UNSPECIFIED.,.F., + .F.,(2,2),(-4.,-1.),.PIECEWISE_BEZIER_KNOTS.); +#2047 = CARTESIAN_POINT('',(0.,-3.)); +#2048 = CARTESIAN_POINT('',(3.,-3.)); +#2049 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2050 = ORIENTED_EDGE('',*,*,#2051,.T.); +#2051 = EDGE_CURVE('',#2029,#2052,#2054,.T.); +#2052 = VERTEX_POINT('',#2053); +#2053 = CARTESIAN_POINT('',(12.25,-12.5,3.)); +#2054 = SURFACE_CURVE('',#2055,(#2060,#2071),.PCURVE_S1.); +#2055 = CIRCLE('',#2056,2.25); +#2056 = AXIS2_PLACEMENT_3D('',#2057,#2058,#2059); +#2057 = CARTESIAN_POINT('',(10.,-12.5,3.)); +#2058 = DIRECTION('',(-0.,-0.,-1.)); +#2059 = DIRECTION('',(0.,-1.,0.)); +#2060 = PCURVE('',#476,#2061); +#2061 = DEFINITIONAL_REPRESENTATION('',(#2062),#2070); +#2062 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2063,#2064,#2065,#2066, +#2067,#2068,#2069),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2063 = CARTESIAN_POINT('',(10.,5.75)); +#2064 = CARTESIAN_POINT('',(6.10288568297,5.75)); +#2065 = CARTESIAN_POINT('',(8.051442841485,9.125)); +#2066 = CARTESIAN_POINT('',(10.,12.5)); +#2067 = CARTESIAN_POINT('',(11.948557158515,9.125)); +#2068 = CARTESIAN_POINT('',(13.89711431703,5.75)); +#2069 = CARTESIAN_POINT('',(10.,5.75)); +#2070 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2071 = PCURVE('',#1217,#2072); +#2072 = DEFINITIONAL_REPRESENTATION('',(#2073),#2077); +#2073 = LINE('',#2074,#2075); +#2074 = CARTESIAN_POINT('',(0.,-3.)); +#2075 = VECTOR('',#2076,1.); +#2076 = DIRECTION('',(1.,0.)); +#2077 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2078 = ORIENTED_EDGE('',*,*,#2079,.F.); +#2079 = EDGE_CURVE('',#2080,#2052,#2082,.T.); +#2080 = VERTEX_POINT('',#2081); +#2081 = CARTESIAN_POINT('',(12.25,-15.5,3.)); +#2082 = SURFACE_CURVE('',#2083,(#2087,#2093),.PCURVE_S1.); +#2083 = LINE('',#2084,#2085); +#2084 = CARTESIAN_POINT('',(12.25,-18.,3.)); +#2085 = VECTOR('',#2086,1.); +#2086 = DIRECTION('',(0.,1.,-0.)); +#2087 = PCURVE('',#476,#2088); +#2088 = DEFINITIONAL_REPRESENTATION('',(#2089),#2092); +#2089 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2090,#2091),.UNSPECIFIED.,.F., + .F.,(2,2),(2.5,5.5),.PIECEWISE_BEZIER_KNOTS.); +#2090 = CARTESIAN_POINT('',(12.25,5.)); +#2091 = CARTESIAN_POINT('',(12.25,8.)); +#2092 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2093 = PCURVE('',#1245,#2094); +#2094 = DEFINITIONAL_REPRESENTATION('',(#2095),#2098); +#2095 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2096,#2097),.UNSPECIFIED.,.F., + .F.,(2,2),(2.5,5.5),.PIECEWISE_BEZIER_KNOTS.); +#2096 = CARTESIAN_POINT('',(0.,-3.)); +#2097 = CARTESIAN_POINT('',(3.,-3.)); +#2098 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2099 = ORIENTED_EDGE('',*,*,#2100,.T.); +#2100 = EDGE_CURVE('',#2080,#2031,#2101,.T.); +#2101 = SURFACE_CURVE('',#2102,(#2107,#2118),.PCURVE_S1.); +#2102 = CIRCLE('',#2103,2.25); +#2103 = AXIS2_PLACEMENT_3D('',#2104,#2105,#2106); +#2104 = CARTESIAN_POINT('',(10.,-15.5,3.)); +#2105 = DIRECTION('',(0.,0.,-1.)); +#2106 = DIRECTION('',(0.,1.,0.)); +#2107 = PCURVE('',#476,#2108); +#2108 = DEFINITIONAL_REPRESENTATION('',(#2109),#2117); +#2109 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2110,#2111,#2112,#2113, +#2114,#2115,#2116),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2110 = CARTESIAN_POINT('',(10.,7.25)); +#2111 = CARTESIAN_POINT('',(13.89711431703,7.25)); +#2112 = CARTESIAN_POINT('',(11.948557158515,3.875)); +#2113 = CARTESIAN_POINT('',(10.,0.5)); +#2114 = CARTESIAN_POINT('',(8.051442841485,3.875)); +#2115 = CARTESIAN_POINT('',(6.10288568297,7.25)); +#2116 = CARTESIAN_POINT('',(10.,7.25)); +#2117 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2118 = PCURVE('',#1276,#2119); +#2119 = DEFINITIONAL_REPRESENTATION('',(#2120),#2124); +#2120 = LINE('',#2121,#2122); +#2121 = CARTESIAN_POINT('',(0.,-3.)); +#2122 = VECTOR('',#2123,1.); +#2123 = DIRECTION('',(1.,0.)); +#2124 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2125 = FACE_BOUND('',#2126,.T.); +#2126 = EDGE_LOOP('',(#2127,#2150,#2178,#2199)); +#2127 = ORIENTED_EDGE('',*,*,#2128,.F.); +#2128 = EDGE_CURVE('',#2129,#2131,#2133,.T.); +#2129 = VERTEX_POINT('',#2130); +#2130 = CARTESIAN_POINT('',(21.5,-13.25,3.)); +#2131 = VERTEX_POINT('',#2132); +#2132 = CARTESIAN_POINT('',(18.5,-13.25,3.)); +#2133 = SURFACE_CURVE('',#2134,(#2138,#2144),.PCURVE_S1.); +#2134 = LINE('',#2135,#2136); +#2135 = CARTESIAN_POINT('',(10.75,-13.25,3.)); +#2136 = VECTOR('',#2137,1.); +#2137 = DIRECTION('',(-1.,0.,0.)); +#2138 = PCURVE('',#476,#2139); +#2139 = DEFINITIONAL_REPRESENTATION('',(#2140),#2143); +#2140 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2141,#2142),.UNSPECIFIED.,.F., + .F.,(2,2),(-10.75,-7.75),.PIECEWISE_BEZIER_KNOTS.); +#2141 = CARTESIAN_POINT('',(21.5,7.25)); +#2142 = CARTESIAN_POINT('',(18.5,7.25)); +#2143 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2144 = PCURVE('',#1308,#2145); +#2145 = DEFINITIONAL_REPRESENTATION('',(#2146),#2149); +#2146 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2147,#2148),.UNSPECIFIED.,.F., + .F.,(2,2),(-10.75,-7.75),.PIECEWISE_BEZIER_KNOTS.); +#2147 = CARTESIAN_POINT('',(0.,-3.)); +#2148 = CARTESIAN_POINT('',(3.,-3.)); +#2149 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2150 = ORIENTED_EDGE('',*,*,#2151,.T.); +#2151 = EDGE_CURVE('',#2129,#2152,#2154,.T.); +#2152 = VERTEX_POINT('',#2153); +#2153 = CARTESIAN_POINT('',(21.5,-17.75,3.)); +#2154 = SURFACE_CURVE('',#2155,(#2160,#2171),.PCURVE_S1.); +#2155 = CIRCLE('',#2156,2.25); +#2156 = AXIS2_PLACEMENT_3D('',#2157,#2158,#2159); +#2157 = CARTESIAN_POINT('',(21.5,-15.5,3.)); +#2158 = DIRECTION('',(0.,0.,-1.)); +#2159 = DIRECTION('',(-1.,0.,0.)); +#2160 = PCURVE('',#476,#2161); +#2161 = DEFINITIONAL_REPRESENTATION('',(#2162),#2170); +#2162 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2163,#2164,#2165,#2166, +#2167,#2168,#2169),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2163 = CARTESIAN_POINT('',(19.25,5.)); +#2164 = CARTESIAN_POINT('',(19.25,8.89711431703)); +#2165 = CARTESIAN_POINT('',(22.625,6.948557158515)); +#2166 = CARTESIAN_POINT('',(26.,5.)); +#2167 = CARTESIAN_POINT('',(22.625,3.051442841485)); +#2168 = CARTESIAN_POINT('',(19.25,1.10288568297)); +#2169 = CARTESIAN_POINT('',(19.25,5.)); +#2170 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2171 = PCURVE('',#1341,#2172); +#2172 = DEFINITIONAL_REPRESENTATION('',(#2173),#2177); +#2173 = LINE('',#2174,#2175); +#2174 = CARTESIAN_POINT('',(0.,-3.)); +#2175 = VECTOR('',#2176,1.); +#2176 = DIRECTION('',(1.,0.)); +#2177 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2178 = ORIENTED_EDGE('',*,*,#2179,.F.); +#2179 = EDGE_CURVE('',#2180,#2152,#2182,.T.); +#2180 = VERTEX_POINT('',#2181); +#2181 = CARTESIAN_POINT('',(18.5,-17.75,3.)); +#2182 = SURFACE_CURVE('',#2183,(#2187,#2193),.PCURVE_S1.); +#2183 = LINE('',#2184,#2185); +#2184 = CARTESIAN_POINT('',(9.25,-17.75,3.)); +#2185 = VECTOR('',#2186,1.); +#2186 = DIRECTION('',(1.,0.,0.)); +#2187 = PCURVE('',#476,#2188); +#2188 = DEFINITIONAL_REPRESENTATION('',(#2189),#2192); +#2189 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2190,#2191),.UNSPECIFIED.,.F., + .F.,(2,2),(9.25,12.25),.PIECEWISE_BEZIER_KNOTS.); +#2190 = CARTESIAN_POINT('',(18.5,2.75)); +#2191 = CARTESIAN_POINT('',(21.5,2.75)); +#2192 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2193 = PCURVE('',#1369,#2194); +#2194 = DEFINITIONAL_REPRESENTATION('',(#2195),#2198); +#2195 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2196,#2197),.UNSPECIFIED.,.F., + .F.,(2,2),(9.25,12.25),.PIECEWISE_BEZIER_KNOTS.); +#2196 = CARTESIAN_POINT('',(0.,-3.)); +#2197 = CARTESIAN_POINT('',(3.,-3.)); +#2198 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2199 = ORIENTED_EDGE('',*,*,#2200,.T.); +#2200 = EDGE_CURVE('',#2180,#2131,#2201,.T.); +#2201 = SURFACE_CURVE('',#2202,(#2207,#2218),.PCURVE_S1.); +#2202 = CIRCLE('',#2203,2.25); +#2203 = AXIS2_PLACEMENT_3D('',#2204,#2205,#2206); +#2204 = CARTESIAN_POINT('',(18.5,-15.5,3.)); +#2205 = DIRECTION('',(0.,0.,-1.)); +#2206 = DIRECTION('',(1.,0.,0.)); +#2207 = PCURVE('',#476,#2208); +#2208 = DEFINITIONAL_REPRESENTATION('',(#2209),#2217); +#2209 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2210,#2211,#2212,#2213, +#2214,#2215,#2216),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2210 = CARTESIAN_POINT('',(20.75,5.)); +#2211 = CARTESIAN_POINT('',(20.75,1.10288568297)); +#2212 = CARTESIAN_POINT('',(17.375,3.051442841485)); +#2213 = CARTESIAN_POINT('',(14.,5.)); +#2214 = CARTESIAN_POINT('',(17.375,6.948557158515)); +#2215 = CARTESIAN_POINT('',(20.75,8.89711431703)); +#2216 = CARTESIAN_POINT('',(20.75,5.)); +#2217 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2218 = PCURVE('',#1400,#2219); +#2219 = DEFINITIONAL_REPRESENTATION('',(#2220),#2224); +#2220 = LINE('',#2221,#2222); +#2221 = CARTESIAN_POINT('',(0.,-3.)); +#2222 = VECTOR('',#2223,1.); +#2223 = DIRECTION('',(1.,0.)); +#2224 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2225 = FACE_BOUND('',#2226,.T.); +#2226 = EDGE_LOOP('',(#2227,#2250,#2278,#2299)); +#2227 = ORIENTED_EDGE('',*,*,#2228,.F.); +#2228 = EDGE_CURVE('',#2229,#2231,#2233,.T.); +#2229 = VERTEX_POINT('',#2230); +#2230 = CARTESIAN_POINT('',(27.75,-12.5,3.)); +#2231 = VERTEX_POINT('',#2232); +#2232 = CARTESIAN_POINT('',(27.75,-15.5,3.)); +#2233 = SURFACE_CURVE('',#2234,(#2238,#2244),.PCURVE_S1.); +#2234 = LINE('',#2235,#2236); +#2235 = CARTESIAN_POINT('',(27.75,-16.5,3.)); +#2236 = VECTOR('',#2237,1.); +#2237 = DIRECTION('',(0.,-1.,0.)); +#2238 = PCURVE('',#476,#2239); +#2239 = DEFINITIONAL_REPRESENTATION('',(#2240),#2243); +#2240 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2241,#2242),.UNSPECIFIED.,.F., + .F.,(2,2),(-4.,-1.),.PIECEWISE_BEZIER_KNOTS.); +#2241 = CARTESIAN_POINT('',(27.75,8.)); +#2242 = CARTESIAN_POINT('',(27.75,5.)); +#2243 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2244 = PCURVE('',#1432,#2245); +#2245 = DEFINITIONAL_REPRESENTATION('',(#2246),#2249); +#2246 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2247,#2248),.UNSPECIFIED.,.F., + .F.,(2,2),(-4.,-1.),.PIECEWISE_BEZIER_KNOTS.); +#2247 = CARTESIAN_POINT('',(0.,-3.)); +#2248 = CARTESIAN_POINT('',(3.,-3.)); +#2249 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2250 = ORIENTED_EDGE('',*,*,#2251,.T.); +#2251 = EDGE_CURVE('',#2229,#2252,#2254,.T.); +#2252 = VERTEX_POINT('',#2253); +#2253 = CARTESIAN_POINT('',(32.25,-12.5,3.)); +#2254 = SURFACE_CURVE('',#2255,(#2260,#2271),.PCURVE_S1.); +#2255 = CIRCLE('',#2256,2.25); +#2256 = AXIS2_PLACEMENT_3D('',#2257,#2258,#2259); +#2257 = CARTESIAN_POINT('',(30.,-12.5,3.)); +#2258 = DIRECTION('',(-0.,-0.,-1.)); +#2259 = DIRECTION('',(0.,-1.,0.)); +#2260 = PCURVE('',#476,#2261); +#2261 = DEFINITIONAL_REPRESENTATION('',(#2262),#2270); +#2262 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2263,#2264,#2265,#2266, +#2267,#2268,#2269),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2263 = CARTESIAN_POINT('',(30.,5.75)); +#2264 = CARTESIAN_POINT('',(26.10288568297,5.75)); +#2265 = CARTESIAN_POINT('',(28.051442841485,9.125)); +#2266 = CARTESIAN_POINT('',(30.,12.5)); +#2267 = CARTESIAN_POINT('',(31.948557158515,9.125)); +#2268 = CARTESIAN_POINT('',(33.89711431703,5.75)); +#2269 = CARTESIAN_POINT('',(30.,5.75)); +#2270 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2271 = PCURVE('',#1465,#2272); +#2272 = DEFINITIONAL_REPRESENTATION('',(#2273),#2277); +#2273 = LINE('',#2274,#2275); +#2274 = CARTESIAN_POINT('',(0.,-3.)); +#2275 = VECTOR('',#2276,1.); +#2276 = DIRECTION('',(1.,0.)); +#2277 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2278 = ORIENTED_EDGE('',*,*,#2279,.F.); +#2279 = EDGE_CURVE('',#2280,#2252,#2282,.T.); +#2280 = VERTEX_POINT('',#2281); +#2281 = CARTESIAN_POINT('',(32.25,-15.5,3.)); +#2282 = SURFACE_CURVE('',#2283,(#2287,#2293),.PCURVE_S1.); +#2283 = LINE('',#2284,#2285); +#2284 = CARTESIAN_POINT('',(32.25,-18.,3.)); +#2285 = VECTOR('',#2286,1.); +#2286 = DIRECTION('',(0.,1.,-0.)); +#2287 = PCURVE('',#476,#2288); +#2288 = DEFINITIONAL_REPRESENTATION('',(#2289),#2292); +#2289 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2290,#2291),.UNSPECIFIED.,.F., + .F.,(2,2),(2.5,5.5),.PIECEWISE_BEZIER_KNOTS.); +#2290 = CARTESIAN_POINT('',(32.25,5.)); +#2291 = CARTESIAN_POINT('',(32.25,8.)); +#2292 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2293 = PCURVE('',#1493,#2294); +#2294 = DEFINITIONAL_REPRESENTATION('',(#2295),#2298); +#2295 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2296,#2297),.UNSPECIFIED.,.F., + .F.,(2,2),(2.5,5.5),.PIECEWISE_BEZIER_KNOTS.); +#2296 = CARTESIAN_POINT('',(0.,-3.)); +#2297 = CARTESIAN_POINT('',(3.,-3.)); +#2298 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2299 = ORIENTED_EDGE('',*,*,#2300,.T.); +#2300 = EDGE_CURVE('',#2280,#2231,#2301,.T.); +#2301 = SURFACE_CURVE('',#2302,(#2307,#2318),.PCURVE_S1.); +#2302 = CIRCLE('',#2303,2.25); +#2303 = AXIS2_PLACEMENT_3D('',#2304,#2305,#2306); +#2304 = CARTESIAN_POINT('',(30.,-15.5,3.)); +#2305 = DIRECTION('',(0.,0.,-1.)); +#2306 = DIRECTION('',(0.,1.,0.)); +#2307 = PCURVE('',#476,#2308); +#2308 = DEFINITIONAL_REPRESENTATION('',(#2309),#2317); +#2309 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2310,#2311,#2312,#2313, +#2314,#2315,#2316),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2310 = CARTESIAN_POINT('',(30.,7.25)); +#2311 = CARTESIAN_POINT('',(33.89711431703,7.25)); +#2312 = CARTESIAN_POINT('',(31.948557158515,3.875)); +#2313 = CARTESIAN_POINT('',(30.,0.5)); +#2314 = CARTESIAN_POINT('',(28.051442841485,3.875)); +#2315 = CARTESIAN_POINT('',(26.10288568297,7.25)); +#2316 = CARTESIAN_POINT('',(30.,7.25)); +#2317 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2318 = PCURVE('',#1524,#2319); +#2319 = DEFINITIONAL_REPRESENTATION('',(#2320),#2324); +#2320 = LINE('',#2321,#2322); +#2321 = CARTESIAN_POINT('',(0.,-3.)); +#2322 = VECTOR('',#2323,1.); +#2323 = DIRECTION('',(1.,0.)); +#2324 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2325 = FACE_BOUND('',#2326,.T.); +#2326 = EDGE_LOOP('',(#2327,#2350,#2378,#2399)); +#2327 = ORIENTED_EDGE('',*,*,#2328,.F.); +#2328 = EDGE_CURVE('',#2329,#2331,#2333,.T.); +#2329 = VERTEX_POINT('',#2330); +#2330 = CARTESIAN_POINT('',(7.75,15.5,3.)); +#2331 = VERTEX_POINT('',#2332); +#2332 = CARTESIAN_POINT('',(7.75,12.5,3.)); +#2333 = SURFACE_CURVE('',#2334,(#2338,#2344),.PCURVE_S1.); +#2334 = LINE('',#2335,#2336); +#2335 = CARTESIAN_POINT('',(7.75,-2.5,3.)); +#2336 = VECTOR('',#2337,1.); +#2337 = DIRECTION('',(0.,-1.,0.)); +#2338 = PCURVE('',#476,#2339); +#2339 = DEFINITIONAL_REPRESENTATION('',(#2340),#2343); +#2340 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2341,#2342),.UNSPECIFIED.,.F., + .F.,(2,2),(-18.,-15.),.PIECEWISE_BEZIER_KNOTS.); +#2341 = CARTESIAN_POINT('',(7.75,36.)); +#2342 = CARTESIAN_POINT('',(7.75,33.)); +#2343 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2344 = PCURVE('',#1556,#2345); +#2345 = DEFINITIONAL_REPRESENTATION('',(#2346),#2349); +#2346 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2347,#2348),.UNSPECIFIED.,.F., + .F.,(2,2),(-18.,-15.),.PIECEWISE_BEZIER_KNOTS.); +#2347 = CARTESIAN_POINT('',(0.,-3.)); +#2348 = CARTESIAN_POINT('',(3.,-3.)); +#2349 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2350 = ORIENTED_EDGE('',*,*,#2351,.T.); +#2351 = EDGE_CURVE('',#2329,#2352,#2354,.T.); +#2352 = VERTEX_POINT('',#2353); +#2353 = CARTESIAN_POINT('',(12.25,15.5,3.)); +#2354 = SURFACE_CURVE('',#2355,(#2360,#2371),.PCURVE_S1.); +#2355 = CIRCLE('',#2356,2.25); +#2356 = AXIS2_PLACEMENT_3D('',#2357,#2358,#2359); +#2357 = CARTESIAN_POINT('',(10.,15.5,3.)); +#2358 = DIRECTION('',(-0.,-0.,-1.)); +#2359 = DIRECTION('',(0.,-1.,0.)); +#2360 = PCURVE('',#476,#2361); +#2361 = DEFINITIONAL_REPRESENTATION('',(#2362),#2370); +#2362 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2363,#2364,#2365,#2366, +#2367,#2368,#2369),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2363 = CARTESIAN_POINT('',(10.,33.75)); +#2364 = CARTESIAN_POINT('',(6.10288568297,33.75)); +#2365 = CARTESIAN_POINT('',(8.051442841485,37.125)); +#2366 = CARTESIAN_POINT('',(10.,40.5)); +#2367 = CARTESIAN_POINT('',(11.948557158515,37.125)); +#2368 = CARTESIAN_POINT('',(13.89711431703,33.75)); +#2369 = CARTESIAN_POINT('',(10.,33.75)); +#2370 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2371 = PCURVE('',#1589,#2372); +#2372 = DEFINITIONAL_REPRESENTATION('',(#2373),#2377); +#2373 = LINE('',#2374,#2375); +#2374 = CARTESIAN_POINT('',(0.,-3.)); +#2375 = VECTOR('',#2376,1.); +#2376 = DIRECTION('',(1.,0.)); +#2377 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2378 = ORIENTED_EDGE('',*,*,#2379,.F.); +#2379 = EDGE_CURVE('',#2380,#2352,#2382,.T.); +#2380 = VERTEX_POINT('',#2381); +#2381 = CARTESIAN_POINT('',(12.25,12.5,3.)); +#2382 = SURFACE_CURVE('',#2383,(#2387,#2393),.PCURVE_S1.); +#2383 = LINE('',#2384,#2385); +#2384 = CARTESIAN_POINT('',(12.25,-4.,3.)); +#2385 = VECTOR('',#2386,1.); +#2386 = DIRECTION('',(0.,1.,-0.)); +#2387 = PCURVE('',#476,#2388); +#2388 = DEFINITIONAL_REPRESENTATION('',(#2389),#2392); +#2389 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2390,#2391),.UNSPECIFIED.,.F., + .F.,(2,2),(16.5,19.5),.PIECEWISE_BEZIER_KNOTS.); +#2390 = CARTESIAN_POINT('',(12.25,33.)); +#2391 = CARTESIAN_POINT('',(12.25,36.)); +#2392 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2393 = PCURVE('',#1617,#2394); +#2394 = DEFINITIONAL_REPRESENTATION('',(#2395),#2398); +#2395 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2396,#2397),.UNSPECIFIED.,.F., + .F.,(2,2),(16.5,19.5),.PIECEWISE_BEZIER_KNOTS.); +#2396 = CARTESIAN_POINT('',(0.,-3.)); +#2397 = CARTESIAN_POINT('',(3.,-3.)); +#2398 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2399 = ORIENTED_EDGE('',*,*,#2400,.T.); +#2400 = EDGE_CURVE('',#2380,#2331,#2401,.T.); +#2401 = SURFACE_CURVE('',#2402,(#2407,#2418),.PCURVE_S1.); +#2402 = CIRCLE('',#2403,2.25); +#2403 = AXIS2_PLACEMENT_3D('',#2404,#2405,#2406); +#2404 = CARTESIAN_POINT('',(10.,12.5,3.)); +#2405 = DIRECTION('',(0.,0.,-1.)); +#2406 = DIRECTION('',(0.,1.,0.)); +#2407 = PCURVE('',#476,#2408); +#2408 = DEFINITIONAL_REPRESENTATION('',(#2409),#2417); +#2409 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2410,#2411,#2412,#2413, +#2414,#2415,#2416),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2410 = CARTESIAN_POINT('',(10.,35.25)); +#2411 = CARTESIAN_POINT('',(13.89711431703,35.25)); +#2412 = CARTESIAN_POINT('',(11.948557158515,31.875)); +#2413 = CARTESIAN_POINT('',(10.,28.5)); +#2414 = CARTESIAN_POINT('',(8.051442841485,31.875)); +#2415 = CARTESIAN_POINT('',(6.10288568297,35.25)); +#2416 = CARTESIAN_POINT('',(10.,35.25)); +#2417 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2418 = PCURVE('',#1648,#2419); +#2419 = DEFINITIONAL_REPRESENTATION('',(#2420),#2424); +#2420 = LINE('',#2421,#2422); +#2421 = CARTESIAN_POINT('',(0.,-3.)); +#2422 = VECTOR('',#2423,1.); +#2423 = DIRECTION('',(1.,0.)); +#2424 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2425 = FACE_BOUND('',#2426,.T.); +#2426 = EDGE_LOOP('',(#2427,#2450,#2478,#2499)); +#2427 = ORIENTED_EDGE('',*,*,#2428,.F.); +#2428 = EDGE_CURVE('',#2429,#2431,#2433,.T.); +#2429 = VERTEX_POINT('',#2430); +#2430 = CARTESIAN_POINT('',(21.5,17.75,3.)); +#2431 = VERTEX_POINT('',#2432); +#2432 = CARTESIAN_POINT('',(18.5,17.75,3.)); +#2433 = SURFACE_CURVE('',#2434,(#2438,#2444),.PCURVE_S1.); +#2434 = LINE('',#2435,#2436); +#2435 = CARTESIAN_POINT('',(10.75,17.75,3.)); +#2436 = VECTOR('',#2437,1.); +#2437 = DIRECTION('',(-1.,0.,0.)); +#2438 = PCURVE('',#476,#2439); +#2439 = DEFINITIONAL_REPRESENTATION('',(#2440),#2443); +#2440 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2441,#2442),.UNSPECIFIED.,.F., + .F.,(2,2),(-10.75,-7.75),.PIECEWISE_BEZIER_KNOTS.); +#2441 = CARTESIAN_POINT('',(21.5,38.25)); +#2442 = CARTESIAN_POINT('',(18.5,38.25)); +#2443 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2444 = PCURVE('',#1680,#2445); +#2445 = DEFINITIONAL_REPRESENTATION('',(#2446),#2449); +#2446 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2447,#2448),.UNSPECIFIED.,.F., + .F.,(2,2),(-10.75,-7.75),.PIECEWISE_BEZIER_KNOTS.); +#2447 = CARTESIAN_POINT('',(0.,-3.)); +#2448 = CARTESIAN_POINT('',(3.,-3.)); +#2449 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2450 = ORIENTED_EDGE('',*,*,#2451,.T.); +#2451 = EDGE_CURVE('',#2429,#2452,#2454,.T.); +#2452 = VERTEX_POINT('',#2453); +#2453 = CARTESIAN_POINT('',(21.5,13.25,3.)); +#2454 = SURFACE_CURVE('',#2455,(#2460,#2471),.PCURVE_S1.); +#2455 = CIRCLE('',#2456,2.25); +#2456 = AXIS2_PLACEMENT_3D('',#2457,#2458,#2459); +#2457 = CARTESIAN_POINT('',(21.5,15.5,3.)); +#2458 = DIRECTION('',(0.,0.,-1.)); +#2459 = DIRECTION('',(-1.,0.,0.)); +#2460 = PCURVE('',#476,#2461); +#2461 = DEFINITIONAL_REPRESENTATION('',(#2462),#2470); +#2462 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2463,#2464,#2465,#2466, +#2467,#2468,#2469),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2463 = CARTESIAN_POINT('',(19.25,36.)); +#2464 = CARTESIAN_POINT('',(19.25,39.89711431703)); +#2465 = CARTESIAN_POINT('',(22.625,37.948557158515)); +#2466 = CARTESIAN_POINT('',(26.,36.)); +#2467 = CARTESIAN_POINT('',(22.625,34.051442841485)); +#2468 = CARTESIAN_POINT('',(19.25,32.10288568297)); +#2469 = CARTESIAN_POINT('',(19.25,36.)); +#2470 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2471 = PCURVE('',#1713,#2472); +#2472 = DEFINITIONAL_REPRESENTATION('',(#2473),#2477); +#2473 = LINE('',#2474,#2475); +#2474 = CARTESIAN_POINT('',(0.,-3.)); +#2475 = VECTOR('',#2476,1.); +#2476 = DIRECTION('',(1.,0.)); +#2477 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2478 = ORIENTED_EDGE('',*,*,#2479,.F.); +#2479 = EDGE_CURVE('',#2480,#2452,#2482,.T.); +#2480 = VERTEX_POINT('',#2481); +#2481 = CARTESIAN_POINT('',(18.5,13.25,3.)); +#2482 = SURFACE_CURVE('',#2483,(#2487,#2493),.PCURVE_S1.); +#2483 = LINE('',#2484,#2485); +#2484 = CARTESIAN_POINT('',(9.25,13.25,3.)); +#2485 = VECTOR('',#2486,1.); +#2486 = DIRECTION('',(1.,0.,0.)); +#2487 = PCURVE('',#476,#2488); +#2488 = DEFINITIONAL_REPRESENTATION('',(#2489),#2492); +#2489 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2490,#2491),.UNSPECIFIED.,.F., + .F.,(2,2),(9.25,12.25),.PIECEWISE_BEZIER_KNOTS.); +#2490 = CARTESIAN_POINT('',(18.5,33.75)); +#2491 = CARTESIAN_POINT('',(21.5,33.75)); +#2492 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2493 = PCURVE('',#1741,#2494); +#2494 = DEFINITIONAL_REPRESENTATION('',(#2495),#2498); +#2495 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2496,#2497),.UNSPECIFIED.,.F., + .F.,(2,2),(9.25,12.25),.PIECEWISE_BEZIER_KNOTS.); +#2496 = CARTESIAN_POINT('',(0.,-3.)); +#2497 = CARTESIAN_POINT('',(3.,-3.)); +#2498 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2499 = ORIENTED_EDGE('',*,*,#2500,.T.); +#2500 = EDGE_CURVE('',#2480,#2431,#2501,.T.); +#2501 = SURFACE_CURVE('',#2502,(#2507,#2518),.PCURVE_S1.); +#2502 = CIRCLE('',#2503,2.25); +#2503 = AXIS2_PLACEMENT_3D('',#2504,#2505,#2506); +#2504 = CARTESIAN_POINT('',(18.5,15.5,3.)); +#2505 = DIRECTION('',(0.,0.,-1.)); +#2506 = DIRECTION('',(1.,0.,0.)); +#2507 = PCURVE('',#476,#2508); +#2508 = DEFINITIONAL_REPRESENTATION('',(#2509),#2517); +#2509 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2510,#2511,#2512,#2513, +#2514,#2515,#2516),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2510 = CARTESIAN_POINT('',(20.75,36.)); +#2511 = CARTESIAN_POINT('',(20.75,32.10288568297)); +#2512 = CARTESIAN_POINT('',(17.375,34.051442841485)); +#2513 = CARTESIAN_POINT('',(14.,36.)); +#2514 = CARTESIAN_POINT('',(17.375,37.948557158515)); +#2515 = CARTESIAN_POINT('',(20.75,39.89711431703)); +#2516 = CARTESIAN_POINT('',(20.75,36.)); +#2517 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2518 = PCURVE('',#1772,#2519); +#2519 = DEFINITIONAL_REPRESENTATION('',(#2520),#2524); +#2520 = LINE('',#2521,#2522); +#2521 = CARTESIAN_POINT('',(0.,-3.)); +#2522 = VECTOR('',#2523,1.); +#2523 = DIRECTION('',(1.,0.)); +#2524 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2525 = FACE_BOUND('',#2526,.T.); +#2526 = EDGE_LOOP('',(#2527,#2550,#2578,#2599)); +#2527 = ORIENTED_EDGE('',*,*,#2528,.F.); +#2528 = EDGE_CURVE('',#2529,#2531,#2533,.T.); +#2529 = VERTEX_POINT('',#2530); +#2530 = CARTESIAN_POINT('',(27.75,15.5,3.)); +#2531 = VERTEX_POINT('',#2532); +#2532 = CARTESIAN_POINT('',(27.75,12.5,3.)); +#2533 = SURFACE_CURVE('',#2534,(#2538,#2544),.PCURVE_S1.); +#2534 = LINE('',#2535,#2536); +#2535 = CARTESIAN_POINT('',(27.75,-2.5,3.)); +#2536 = VECTOR('',#2537,1.); +#2537 = DIRECTION('',(0.,-1.,0.)); +#2538 = PCURVE('',#476,#2539); +#2539 = DEFINITIONAL_REPRESENTATION('',(#2540),#2543); +#2540 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2541,#2542),.UNSPECIFIED.,.F., + .F.,(2,2),(-18.,-15.),.PIECEWISE_BEZIER_KNOTS.); +#2541 = CARTESIAN_POINT('',(27.75,36.)); +#2542 = CARTESIAN_POINT('',(27.75,33.)); +#2543 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2544 = PCURVE('',#1804,#2545); +#2545 = DEFINITIONAL_REPRESENTATION('',(#2546),#2549); +#2546 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2547,#2548),.UNSPECIFIED.,.F., + .F.,(2,2),(-18.,-15.),.PIECEWISE_BEZIER_KNOTS.); +#2547 = CARTESIAN_POINT('',(0.,-3.)); +#2548 = CARTESIAN_POINT('',(3.,-3.)); +#2549 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2550 = ORIENTED_EDGE('',*,*,#2551,.T.); +#2551 = EDGE_CURVE('',#2529,#2552,#2554,.T.); +#2552 = VERTEX_POINT('',#2553); +#2553 = CARTESIAN_POINT('',(32.25,15.5,3.)); +#2554 = SURFACE_CURVE('',#2555,(#2560,#2571),.PCURVE_S1.); +#2555 = CIRCLE('',#2556,2.25); +#2556 = AXIS2_PLACEMENT_3D('',#2557,#2558,#2559); +#2557 = CARTESIAN_POINT('',(30.,15.5,3.)); +#2558 = DIRECTION('',(-0.,-0.,-1.)); +#2559 = DIRECTION('',(0.,-1.,0.)); +#2560 = PCURVE('',#476,#2561); +#2561 = DEFINITIONAL_REPRESENTATION('',(#2562),#2570); +#2562 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2563,#2564,#2565,#2566, +#2567,#2568,#2569),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2563 = CARTESIAN_POINT('',(30.,33.75)); +#2564 = CARTESIAN_POINT('',(26.10288568297,33.75)); +#2565 = CARTESIAN_POINT('',(28.051442841485,37.125)); +#2566 = CARTESIAN_POINT('',(30.,40.5)); +#2567 = CARTESIAN_POINT('',(31.948557158515,37.125)); +#2568 = CARTESIAN_POINT('',(33.89711431703,33.75)); +#2569 = CARTESIAN_POINT('',(30.,33.75)); +#2570 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2571 = PCURVE('',#1837,#2572); +#2572 = DEFINITIONAL_REPRESENTATION('',(#2573),#2577); +#2573 = LINE('',#2574,#2575); +#2574 = CARTESIAN_POINT('',(0.,-3.)); +#2575 = VECTOR('',#2576,1.); +#2576 = DIRECTION('',(1.,0.)); +#2577 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2578 = ORIENTED_EDGE('',*,*,#2579,.F.); +#2579 = EDGE_CURVE('',#2580,#2552,#2582,.T.); +#2580 = VERTEX_POINT('',#2581); +#2581 = CARTESIAN_POINT('',(32.25,12.5,3.)); +#2582 = SURFACE_CURVE('',#2583,(#2587,#2593),.PCURVE_S1.); +#2583 = LINE('',#2584,#2585); +#2584 = CARTESIAN_POINT('',(32.25,-4.,3.)); +#2585 = VECTOR('',#2586,1.); +#2586 = DIRECTION('',(0.,1.,-0.)); +#2587 = PCURVE('',#476,#2588); +#2588 = DEFINITIONAL_REPRESENTATION('',(#2589),#2592); +#2589 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2590,#2591),.UNSPECIFIED.,.F., + .F.,(2,2),(16.5,19.5),.PIECEWISE_BEZIER_KNOTS.); +#2590 = CARTESIAN_POINT('',(32.25,33.)); +#2591 = CARTESIAN_POINT('',(32.25,36.)); +#2592 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2593 = PCURVE('',#1865,#2594); +#2594 = DEFINITIONAL_REPRESENTATION('',(#2595),#2598); +#2595 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#2596,#2597),.UNSPECIFIED.,.F., + .F.,(2,2),(16.5,19.5),.PIECEWISE_BEZIER_KNOTS.); +#2596 = CARTESIAN_POINT('',(0.,-3.)); +#2597 = CARTESIAN_POINT('',(3.,-3.)); +#2598 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2599 = ORIENTED_EDGE('',*,*,#2600,.T.); +#2600 = EDGE_CURVE('',#2580,#2531,#2601,.T.); +#2601 = SURFACE_CURVE('',#2602,(#2607,#2618),.PCURVE_S1.); +#2602 = CIRCLE('',#2603,2.25); +#2603 = AXIS2_PLACEMENT_3D('',#2604,#2605,#2606); +#2604 = CARTESIAN_POINT('',(30.,12.5,3.)); +#2605 = DIRECTION('',(0.,0.,-1.)); +#2606 = DIRECTION('',(0.,1.,0.)); +#2607 = PCURVE('',#476,#2608); +#2608 = DEFINITIONAL_REPRESENTATION('',(#2609),#2617); +#2609 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#2610,#2611,#2612,#2613, +#2614,#2615,#2616),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2 + ,2,2,2,1),(-2.094395102393,0.,2.094395102393,4.188790204786, +6.28318530718,8.377580409573),.UNSPECIFIED.) CURVE() +GEOMETRIC_REPRESENTATION_ITEM() RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5, +1.,0.5,1.)) REPRESENTATION_ITEM('') ); +#2610 = CARTESIAN_POINT('',(30.,35.25)); +#2611 = CARTESIAN_POINT('',(33.89711431703,35.25)); +#2612 = CARTESIAN_POINT('',(31.948557158515,31.875)); +#2613 = CARTESIAN_POINT('',(30.,28.5)); +#2614 = CARTESIAN_POINT('',(28.051442841485,31.875)); +#2615 = CARTESIAN_POINT('',(26.10288568297,35.25)); +#2616 = CARTESIAN_POINT('',(30.,35.25)); +#2617 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2618 = PCURVE('',#1896,#2619); +#2619 = DEFINITIONAL_REPRESENTATION('',(#2620),#2624); +#2620 = LINE('',#2621,#2622); +#2621 = CARTESIAN_POINT('',(0.,-3.)); +#2622 = VECTOR('',#2623,1.); +#2623 = DIRECTION('',(1.,0.)); +#2624 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2625 = ADVANCED_FACE('',(#2626,#2634,#2637,#2640,#2643,#2646),#448,.T. + ); +#2626 = FACE_BOUND('',#2627,.T.); +#2627 = EDGE_LOOP('',(#2628,#2629,#2630,#2631,#2632,#2633)); +#2628 = ORIENTED_EDGE('',*,*,#432,.F.); +#2629 = ORIENTED_EDGE('',*,*,#1961,.T.); +#2630 = ORIENTED_EDGE('',*,*,#644,.T.); +#2631 = ORIENTED_EDGE('',*,*,#794,.T.); +#2632 = ORIENTED_EDGE('',*,*,#745,.F.); +#2633 = ORIENTED_EDGE('',*,*,#573,.F.); +#2634 = FACE_BOUND('',#2635,.T.); +#2635 = EDGE_LOOP('',(#2636)); +#2636 = ORIENTED_EDGE('',*,*,#844,.T.); +#2637 = FACE_BOUND('',#2638,.T.); +#2638 = EDGE_LOOP('',(#2639)); +#2639 = ORIENTED_EDGE('',*,*,#898,.T.); +#2640 = FACE_BOUND('',#2641,.T.); +#2641 = EDGE_LOOP('',(#2642)); +#2642 = ORIENTED_EDGE('',*,*,#952,.T.); +#2643 = FACE_BOUND('',#2644,.T.); +#2644 = EDGE_LOOP('',(#2645)); +#2645 = ORIENTED_EDGE('',*,*,#1006,.T.); +#2646 = FACE_BOUND('',#2647,.T.); +#2647 = EDGE_LOOP('',(#2648)); +#2648 = ORIENTED_EDGE('',*,*,#1060,.T.); +#2649 = ADVANCED_FACE('',(#2650),#706,.T.); +#2650 = FACE_BOUND('',#2651,.T.); +#2651 = EDGE_LOOP('',(#2652,#2653,#2654,#2655)); +#2652 = ORIENTED_EDGE('',*,*,#1141,.F.); +#2653 = ORIENTED_EDGE('',*,*,#690,.T.); +#2654 = ORIENTED_EDGE('',*,*,#2005,.T.); +#2655 = ORIENTED_EDGE('',*,*,#2656,.F.); +#2656 = EDGE_CURVE('',#1114,#1983,#2657,.T.); +#2657 = SURFACE_CURVE('',#2658,(#2662,#2669),.PCURVE_S1.); +#2658 = LINE('',#2659,#2660); +#2659 = CARTESIAN_POINT('',(35.,18.5,0.)); +#2660 = VECTOR('',#2661,1.); +#2661 = DIRECTION('',(0.,0.,1.)); +#2662 = PCURVE('',#706,#2663); +#2663 = DEFINITIONAL_REPRESENTATION('',(#2664),#2668); +#2664 = LINE('',#2665,#2666); +#2665 = CARTESIAN_POINT('',(0.,1.414213562373)); +#2666 = VECTOR('',#2667,1.); +#2667 = DIRECTION('',(1.,0.)); +#2668 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2669 = PCURVE('',#1129,#2670); +#2670 = DEFINITIONAL_REPRESENTATION('',(#2671),#2675); +#2671 = LINE('',#2672,#2673); +#2672 = CARTESIAN_POINT('',(0.,-39.)); +#2673 = VECTOR('',#2674,1.); +#2674 = DIRECTION('',(1.,0.)); +#2675 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2676 = ADVANCED_FACE('',(#2677),#1129,.T.); +#2677 = FACE_BOUND('',#2678,.T.); +#2678 = EDGE_LOOP('',(#2679,#2680,#2681,#2682)); +#2679 = ORIENTED_EDGE('',*,*,#1982,.F.); +#2680 = ORIENTED_EDGE('',*,*,#1936,.F.); +#2681 = ORIENTED_EDGE('',*,*,#1113,.T.); +#2682 = ORIENTED_EDGE('',*,*,#2656,.T.); +#2683 = ADVANCED_FACE('',(#2684),#1184,.T.); +#2684 = FACE_BOUND('',#2685,.T.); +#2685 = EDGE_LOOP('',(#2686,#2687,#2708,#2709)); +#2686 = ORIENTED_EDGE('',*,*,#1166,.F.); +#2687 = ORIENTED_EDGE('',*,*,#2688,.T.); +#2688 = EDGE_CURVE('',#1167,#2029,#2689,.T.); +#2689 = SURFACE_CURVE('',#2690,(#2694,#2701),.PCURVE_S1.); +#2690 = LINE('',#2691,#2692); +#2691 = CARTESIAN_POINT('',(7.75,-12.5,0.)); +#2692 = VECTOR('',#2693,1.); +#2693 = DIRECTION('',(0.,0.,1.)); +#2694 = PCURVE('',#1184,#2695); +#2695 = DEFINITIONAL_REPRESENTATION('',(#2696),#2700); +#2696 = LINE('',#2697,#2698); +#2697 = CARTESIAN_POINT('',(0.,0.)); +#2698 = VECTOR('',#2699,1.); +#2699 = DIRECTION('',(0.,-1.)); +#2700 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2701 = PCURVE('',#1217,#2702); +#2702 = DEFINITIONAL_REPRESENTATION('',(#2703),#2707); +#2703 = LINE('',#2704,#2705); +#2704 = CARTESIAN_POINT('',(1.570796326795,0.)); +#2705 = VECTOR('',#2706,1.); +#2706 = DIRECTION('',(0.,-1.)); +#2707 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2708 = ORIENTED_EDGE('',*,*,#2028,.T.); +#2709 = ORIENTED_EDGE('',*,*,#2710,.F.); +#2710 = EDGE_CURVE('',#1169,#2031,#2711,.T.); +#2711 = SURFACE_CURVE('',#2712,(#2716,#2723),.PCURVE_S1.); +#2712 = LINE('',#2713,#2714); +#2713 = CARTESIAN_POINT('',(7.75,-15.5,0.)); +#2714 = VECTOR('',#2715,1.); +#2715 = DIRECTION('',(0.,0.,1.)); +#2716 = PCURVE('',#1184,#2717); +#2717 = DEFINITIONAL_REPRESENTATION('',(#2718),#2722); +#2718 = LINE('',#2719,#2720); +#2719 = CARTESIAN_POINT('',(3.,0.)); +#2720 = VECTOR('',#2721,1.); +#2721 = DIRECTION('',(0.,-1.)); +#2722 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2723 = PCURVE('',#1276,#2724); +#2724 = DEFINITIONAL_REPRESENTATION('',(#2725),#2729); +#2725 = LINE('',#2726,#2727); +#2726 = CARTESIAN_POINT('',(4.712388980385,0.)); +#2727 = VECTOR('',#2728,1.); +#2728 = DIRECTION('',(0.,-1.)); +#2729 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2730 = ADVANCED_FACE('',(#2731),#1276,.F.); +#2731 = FACE_BOUND('',#2732,.F.); +#2732 = EDGE_LOOP('',(#2733,#2734,#2755,#2756)); +#2733 = ORIENTED_EDGE('',*,*,#1257,.F.); +#2734 = ORIENTED_EDGE('',*,*,#2735,.T.); +#2735 = EDGE_CURVE('',#1230,#2080,#2736,.T.); +#2736 = SURFACE_CURVE('',#2737,(#2741,#2748),.PCURVE_S1.); +#2737 = LINE('',#2738,#2739); +#2738 = CARTESIAN_POINT('',(12.25,-15.5,0.)); +#2739 = VECTOR('',#2740,1.); +#2740 = DIRECTION('',(0.,0.,1.)); +#2741 = PCURVE('',#1276,#2742); +#2742 = DEFINITIONAL_REPRESENTATION('',(#2743),#2747); +#2743 = LINE('',#2744,#2745); +#2744 = CARTESIAN_POINT('',(1.570796326795,0.)); +#2745 = VECTOR('',#2746,1.); +#2746 = DIRECTION('',(0.,-1.)); +#2747 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2748 = PCURVE('',#1245,#2749); +#2749 = DEFINITIONAL_REPRESENTATION('',(#2750),#2754); +#2750 = LINE('',#2751,#2752); +#2751 = CARTESIAN_POINT('',(0.,0.)); +#2752 = VECTOR('',#2753,1.); +#2753 = DIRECTION('',(0.,-1.)); +#2754 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2755 = ORIENTED_EDGE('',*,*,#2100,.T.); +#2756 = ORIENTED_EDGE('',*,*,#2710,.F.); +#2757 = ADVANCED_FACE('',(#2758),#1245,.T.); +#2758 = FACE_BOUND('',#2759,.T.); +#2759 = EDGE_LOOP('',(#2760,#2761,#2762,#2763)); +#2760 = ORIENTED_EDGE('',*,*,#1229,.F.); +#2761 = ORIENTED_EDGE('',*,*,#2735,.T.); +#2762 = ORIENTED_EDGE('',*,*,#2079,.T.); +#2763 = ORIENTED_EDGE('',*,*,#2764,.F.); +#2764 = EDGE_CURVE('',#1197,#2052,#2765,.T.); +#2765 = SURFACE_CURVE('',#2766,(#2770,#2777),.PCURVE_S1.); +#2766 = LINE('',#2767,#2768); +#2767 = CARTESIAN_POINT('',(12.25,-12.5,0.)); +#2768 = VECTOR('',#2769,1.); +#2769 = DIRECTION('',(0.,0.,1.)); +#2770 = PCURVE('',#1245,#2771); +#2771 = DEFINITIONAL_REPRESENTATION('',(#2772),#2776); +#2772 = LINE('',#2773,#2774); +#2773 = CARTESIAN_POINT('',(3.,0.)); +#2774 = VECTOR('',#2775,1.); +#2775 = DIRECTION('',(0.,-1.)); +#2776 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2777 = PCURVE('',#1217,#2778); +#2778 = DEFINITIONAL_REPRESENTATION('',(#2779),#2783); +#2779 = LINE('',#2780,#2781); +#2780 = CARTESIAN_POINT('',(4.712388980385,0.)); +#2781 = VECTOR('',#2782,1.); +#2782 = DIRECTION('',(0.,-1.)); +#2783 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2784 = ADVANCED_FACE('',(#2785),#1217,.F.); +#2785 = FACE_BOUND('',#2786,.F.); +#2786 = EDGE_LOOP('',(#2787,#2788,#2789,#2790)); +#2787 = ORIENTED_EDGE('',*,*,#1196,.F.); +#2788 = ORIENTED_EDGE('',*,*,#2688,.T.); +#2789 = ORIENTED_EDGE('',*,*,#2051,.T.); +#2790 = ORIENTED_EDGE('',*,*,#2764,.F.); +#2791 = ADVANCED_FACE('',(#2792),#1308,.T.); +#2792 = FACE_BOUND('',#2793,.T.); +#2793 = EDGE_LOOP('',(#2794,#2795,#2816,#2817)); +#2794 = ORIENTED_EDGE('',*,*,#1290,.F.); +#2795 = ORIENTED_EDGE('',*,*,#2796,.T.); +#2796 = EDGE_CURVE('',#1291,#2129,#2797,.T.); +#2797 = SURFACE_CURVE('',#2798,(#2802,#2809),.PCURVE_S1.); +#2798 = LINE('',#2799,#2800); +#2799 = CARTESIAN_POINT('',(21.5,-13.25,0.)); +#2800 = VECTOR('',#2801,1.); +#2801 = DIRECTION('',(0.,0.,1.)); +#2802 = PCURVE('',#1308,#2803); +#2803 = DEFINITIONAL_REPRESENTATION('',(#2804),#2808); +#2804 = LINE('',#2805,#2806); +#2805 = CARTESIAN_POINT('',(0.,-0.)); +#2806 = VECTOR('',#2807,1.); +#2807 = DIRECTION('',(0.,-1.)); +#2808 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2809 = PCURVE('',#1341,#2810); +#2810 = DEFINITIONAL_REPRESENTATION('',(#2811),#2815); +#2811 = LINE('',#2812,#2813); +#2812 = CARTESIAN_POINT('',(1.570796326795,0.)); +#2813 = VECTOR('',#2814,1.); +#2814 = DIRECTION('',(0.,-1.)); +#2815 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2816 = ORIENTED_EDGE('',*,*,#2128,.T.); +#2817 = ORIENTED_EDGE('',*,*,#2818,.F.); +#2818 = EDGE_CURVE('',#1293,#2131,#2819,.T.); +#2819 = SURFACE_CURVE('',#2820,(#2824,#2831),.PCURVE_S1.); +#2820 = LINE('',#2821,#2822); +#2821 = CARTESIAN_POINT('',(18.5,-13.25,0.)); +#2822 = VECTOR('',#2823,1.); +#2823 = DIRECTION('',(0.,0.,1.)); +#2824 = PCURVE('',#1308,#2825); +#2825 = DEFINITIONAL_REPRESENTATION('',(#2826),#2830); +#2826 = LINE('',#2827,#2828); +#2827 = CARTESIAN_POINT('',(3.,0.)); +#2828 = VECTOR('',#2829,1.); +#2829 = DIRECTION('',(0.,-1.)); +#2830 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2831 = PCURVE('',#1400,#2832); +#2832 = DEFINITIONAL_REPRESENTATION('',(#2833),#2837); +#2833 = LINE('',#2834,#2835); +#2834 = CARTESIAN_POINT('',(4.712388980385,0.)); +#2835 = VECTOR('',#2836,1.); +#2836 = DIRECTION('',(0.,-1.)); +#2837 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2838 = ADVANCED_FACE('',(#2839),#1400,.F.); +#2839 = FACE_BOUND('',#2840,.F.); +#2840 = EDGE_LOOP('',(#2841,#2842,#2863,#2864)); +#2841 = ORIENTED_EDGE('',*,*,#1381,.F.); +#2842 = ORIENTED_EDGE('',*,*,#2843,.T.); +#2843 = EDGE_CURVE('',#1354,#2180,#2844,.T.); +#2844 = SURFACE_CURVE('',#2845,(#2849,#2856),.PCURVE_S1.); +#2845 = LINE('',#2846,#2847); +#2846 = CARTESIAN_POINT('',(18.5,-17.75,0.)); +#2847 = VECTOR('',#2848,1.); +#2848 = DIRECTION('',(0.,0.,1.)); +#2849 = PCURVE('',#1400,#2850); +#2850 = DEFINITIONAL_REPRESENTATION('',(#2851),#2855); +#2851 = LINE('',#2852,#2853); +#2852 = CARTESIAN_POINT('',(1.570796326795,0.)); +#2853 = VECTOR('',#2854,1.); +#2854 = DIRECTION('',(0.,-1.)); +#2855 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2856 = PCURVE('',#1369,#2857); +#2857 = DEFINITIONAL_REPRESENTATION('',(#2858),#2862); +#2858 = LINE('',#2859,#2860); +#2859 = CARTESIAN_POINT('',(0.,0.)); +#2860 = VECTOR('',#2861,1.); +#2861 = DIRECTION('',(0.,-1.)); +#2862 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2863 = ORIENTED_EDGE('',*,*,#2200,.T.); +#2864 = ORIENTED_EDGE('',*,*,#2818,.F.); +#2865 = ADVANCED_FACE('',(#2866),#1369,.T.); +#2866 = FACE_BOUND('',#2867,.T.); +#2867 = EDGE_LOOP('',(#2868,#2869,#2870,#2871)); +#2868 = ORIENTED_EDGE('',*,*,#1353,.F.); +#2869 = ORIENTED_EDGE('',*,*,#2843,.T.); +#2870 = ORIENTED_EDGE('',*,*,#2179,.T.); +#2871 = ORIENTED_EDGE('',*,*,#2872,.F.); +#2872 = EDGE_CURVE('',#1321,#2152,#2873,.T.); +#2873 = SURFACE_CURVE('',#2874,(#2878,#2885),.PCURVE_S1.); +#2874 = LINE('',#2875,#2876); +#2875 = CARTESIAN_POINT('',(21.5,-17.75,0.)); +#2876 = VECTOR('',#2877,1.); +#2877 = DIRECTION('',(0.,0.,1.)); +#2878 = PCURVE('',#1369,#2879); +#2879 = DEFINITIONAL_REPRESENTATION('',(#2880),#2884); +#2880 = LINE('',#2881,#2882); +#2881 = CARTESIAN_POINT('',(3.,0.)); +#2882 = VECTOR('',#2883,1.); +#2883 = DIRECTION('',(0.,-1.)); +#2884 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2885 = PCURVE('',#1341,#2886); +#2886 = DEFINITIONAL_REPRESENTATION('',(#2887),#2891); +#2887 = LINE('',#2888,#2889); +#2888 = CARTESIAN_POINT('',(4.712388980385,0.)); +#2889 = VECTOR('',#2890,1.); +#2890 = DIRECTION('',(0.,-1.)); +#2891 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2892 = ADVANCED_FACE('',(#2893),#1341,.F.); +#2893 = FACE_BOUND('',#2894,.F.); +#2894 = EDGE_LOOP('',(#2895,#2896,#2897,#2898)); +#2895 = ORIENTED_EDGE('',*,*,#1320,.F.); +#2896 = ORIENTED_EDGE('',*,*,#2796,.T.); +#2897 = ORIENTED_EDGE('',*,*,#2151,.T.); +#2898 = ORIENTED_EDGE('',*,*,#2872,.F.); +#2899 = ADVANCED_FACE('',(#2900),#1432,.T.); +#2900 = FACE_BOUND('',#2901,.T.); +#2901 = EDGE_LOOP('',(#2902,#2903,#2924,#2925)); +#2902 = ORIENTED_EDGE('',*,*,#1414,.F.); +#2903 = ORIENTED_EDGE('',*,*,#2904,.T.); +#2904 = EDGE_CURVE('',#1415,#2229,#2905,.T.); +#2905 = SURFACE_CURVE('',#2906,(#2910,#2917),.PCURVE_S1.); +#2906 = LINE('',#2907,#2908); +#2907 = CARTESIAN_POINT('',(27.75,-12.5,0.)); +#2908 = VECTOR('',#2909,1.); +#2909 = DIRECTION('',(0.,0.,1.)); +#2910 = PCURVE('',#1432,#2911); +#2911 = DEFINITIONAL_REPRESENTATION('',(#2912),#2916); +#2912 = LINE('',#2913,#2914); +#2913 = CARTESIAN_POINT('',(0.,0.)); +#2914 = VECTOR('',#2915,1.); +#2915 = DIRECTION('',(0.,-1.)); +#2916 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2917 = PCURVE('',#1465,#2918); +#2918 = DEFINITIONAL_REPRESENTATION('',(#2919),#2923); +#2919 = LINE('',#2920,#2921); +#2920 = CARTESIAN_POINT('',(1.570796326795,0.)); +#2921 = VECTOR('',#2922,1.); +#2922 = DIRECTION('',(0.,-1.)); +#2923 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2924 = ORIENTED_EDGE('',*,*,#2228,.T.); +#2925 = ORIENTED_EDGE('',*,*,#2926,.F.); +#2926 = EDGE_CURVE('',#1417,#2231,#2927,.T.); +#2927 = SURFACE_CURVE('',#2928,(#2932,#2939),.PCURVE_S1.); +#2928 = LINE('',#2929,#2930); +#2929 = CARTESIAN_POINT('',(27.75,-15.5,0.)); +#2930 = VECTOR('',#2931,1.); +#2931 = DIRECTION('',(0.,0.,1.)); +#2932 = PCURVE('',#1432,#2933); +#2933 = DEFINITIONAL_REPRESENTATION('',(#2934),#2938); +#2934 = LINE('',#2935,#2936); +#2935 = CARTESIAN_POINT('',(3.,0.)); +#2936 = VECTOR('',#2937,1.); +#2937 = DIRECTION('',(0.,-1.)); +#2938 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2939 = PCURVE('',#1524,#2940); +#2940 = DEFINITIONAL_REPRESENTATION('',(#2941),#2945); +#2941 = LINE('',#2942,#2943); +#2942 = CARTESIAN_POINT('',(4.712388980385,0.)); +#2943 = VECTOR('',#2944,1.); +#2944 = DIRECTION('',(0.,-1.)); +#2945 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2946 = ADVANCED_FACE('',(#2947),#1524,.F.); +#2947 = FACE_BOUND('',#2948,.F.); +#2948 = EDGE_LOOP('',(#2949,#2950,#2971,#2972)); +#2949 = ORIENTED_EDGE('',*,*,#1505,.F.); +#2950 = ORIENTED_EDGE('',*,*,#2951,.T.); +#2951 = EDGE_CURVE('',#1478,#2280,#2952,.T.); +#2952 = SURFACE_CURVE('',#2953,(#2957,#2964),.PCURVE_S1.); +#2953 = LINE('',#2954,#2955); +#2954 = CARTESIAN_POINT('',(32.25,-15.5,0.)); +#2955 = VECTOR('',#2956,1.); +#2956 = DIRECTION('',(0.,0.,1.)); +#2957 = PCURVE('',#1524,#2958); +#2958 = DEFINITIONAL_REPRESENTATION('',(#2959),#2963); +#2959 = LINE('',#2960,#2961); +#2960 = CARTESIAN_POINT('',(1.570796326795,0.)); +#2961 = VECTOR('',#2962,1.); +#2962 = DIRECTION('',(0.,-1.)); +#2963 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2964 = PCURVE('',#1493,#2965); +#2965 = DEFINITIONAL_REPRESENTATION('',(#2966),#2970); +#2966 = LINE('',#2967,#2968); +#2967 = CARTESIAN_POINT('',(0.,0.)); +#2968 = VECTOR('',#2969,1.); +#2969 = DIRECTION('',(0.,-1.)); +#2970 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2971 = ORIENTED_EDGE('',*,*,#2300,.T.); +#2972 = ORIENTED_EDGE('',*,*,#2926,.F.); +#2973 = ADVANCED_FACE('',(#2974),#1493,.T.); +#2974 = FACE_BOUND('',#2975,.T.); +#2975 = EDGE_LOOP('',(#2976,#2977,#2978,#2979)); +#2976 = ORIENTED_EDGE('',*,*,#1477,.F.); +#2977 = ORIENTED_EDGE('',*,*,#2951,.T.); +#2978 = ORIENTED_EDGE('',*,*,#2279,.T.); +#2979 = ORIENTED_EDGE('',*,*,#2980,.F.); +#2980 = EDGE_CURVE('',#1445,#2252,#2981,.T.); +#2981 = SURFACE_CURVE('',#2982,(#2986,#2993),.PCURVE_S1.); +#2982 = LINE('',#2983,#2984); +#2983 = CARTESIAN_POINT('',(32.25,-12.5,0.)); +#2984 = VECTOR('',#2985,1.); +#2985 = DIRECTION('',(0.,0.,1.)); +#2986 = PCURVE('',#1493,#2987); +#2987 = DEFINITIONAL_REPRESENTATION('',(#2988),#2992); +#2988 = LINE('',#2989,#2990); +#2989 = CARTESIAN_POINT('',(3.,0.)); +#2990 = VECTOR('',#2991,1.); +#2991 = DIRECTION('',(0.,-1.)); +#2992 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#2993 = PCURVE('',#1465,#2994); +#2994 = DEFINITIONAL_REPRESENTATION('',(#2995),#2999); +#2995 = LINE('',#2996,#2997); +#2996 = CARTESIAN_POINT('',(4.712388980385,0.)); +#2997 = VECTOR('',#2998,1.); +#2998 = DIRECTION('',(0.,-1.)); +#2999 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3000 = ADVANCED_FACE('',(#3001),#1465,.F.); +#3001 = FACE_BOUND('',#3002,.F.); +#3002 = EDGE_LOOP('',(#3003,#3004,#3005,#3006)); +#3003 = ORIENTED_EDGE('',*,*,#1444,.F.); +#3004 = ORIENTED_EDGE('',*,*,#2904,.T.); +#3005 = ORIENTED_EDGE('',*,*,#2251,.T.); +#3006 = ORIENTED_EDGE('',*,*,#2980,.F.); +#3007 = ADVANCED_FACE('',(#3008),#1556,.T.); +#3008 = FACE_BOUND('',#3009,.T.); +#3009 = EDGE_LOOP('',(#3010,#3011,#3032,#3033)); +#3010 = ORIENTED_EDGE('',*,*,#1538,.F.); +#3011 = ORIENTED_EDGE('',*,*,#3012,.T.); +#3012 = EDGE_CURVE('',#1539,#2329,#3013,.T.); +#3013 = SURFACE_CURVE('',#3014,(#3018,#3025),.PCURVE_S1.); +#3014 = LINE('',#3015,#3016); +#3015 = CARTESIAN_POINT('',(7.75,15.5,0.)); +#3016 = VECTOR('',#3017,1.); +#3017 = DIRECTION('',(0.,0.,1.)); +#3018 = PCURVE('',#1556,#3019); +#3019 = DEFINITIONAL_REPRESENTATION('',(#3020),#3024); +#3020 = LINE('',#3021,#3022); +#3021 = CARTESIAN_POINT('',(0.,0.)); +#3022 = VECTOR('',#3023,1.); +#3023 = DIRECTION('',(0.,-1.)); +#3024 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3025 = PCURVE('',#1589,#3026); +#3026 = DEFINITIONAL_REPRESENTATION('',(#3027),#3031); +#3027 = LINE('',#3028,#3029); +#3028 = CARTESIAN_POINT('',(1.570796326795,0.)); +#3029 = VECTOR('',#3030,1.); +#3030 = DIRECTION('',(0.,-1.)); +#3031 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3032 = ORIENTED_EDGE('',*,*,#2328,.T.); +#3033 = ORIENTED_EDGE('',*,*,#3034,.F.); +#3034 = EDGE_CURVE('',#1541,#2331,#3035,.T.); +#3035 = SURFACE_CURVE('',#3036,(#3040,#3047),.PCURVE_S1.); +#3036 = LINE('',#3037,#3038); +#3037 = CARTESIAN_POINT('',(7.75,12.5,0.)); +#3038 = VECTOR('',#3039,1.); +#3039 = DIRECTION('',(0.,0.,1.)); +#3040 = PCURVE('',#1556,#3041); +#3041 = DEFINITIONAL_REPRESENTATION('',(#3042),#3046); +#3042 = LINE('',#3043,#3044); +#3043 = CARTESIAN_POINT('',(3.,0.)); +#3044 = VECTOR('',#3045,1.); +#3045 = DIRECTION('',(0.,-1.)); +#3046 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3047 = PCURVE('',#1648,#3048); +#3048 = DEFINITIONAL_REPRESENTATION('',(#3049),#3053); +#3049 = LINE('',#3050,#3051); +#3050 = CARTESIAN_POINT('',(4.712388980385,0.)); +#3051 = VECTOR('',#3052,1.); +#3052 = DIRECTION('',(0.,-1.)); +#3053 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3054 = ADVANCED_FACE('',(#3055),#1648,.F.); +#3055 = FACE_BOUND('',#3056,.F.); +#3056 = EDGE_LOOP('',(#3057,#3058,#3079,#3080)); +#3057 = ORIENTED_EDGE('',*,*,#1629,.F.); +#3058 = ORIENTED_EDGE('',*,*,#3059,.T.); +#3059 = EDGE_CURVE('',#1602,#2380,#3060,.T.); +#3060 = SURFACE_CURVE('',#3061,(#3065,#3072),.PCURVE_S1.); +#3061 = LINE('',#3062,#3063); +#3062 = CARTESIAN_POINT('',(12.25,12.5,0.)); +#3063 = VECTOR('',#3064,1.); +#3064 = DIRECTION('',(0.,0.,1.)); +#3065 = PCURVE('',#1648,#3066); +#3066 = DEFINITIONAL_REPRESENTATION('',(#3067),#3071); +#3067 = LINE('',#3068,#3069); +#3068 = CARTESIAN_POINT('',(1.570796326795,0.)); +#3069 = VECTOR('',#3070,1.); +#3070 = DIRECTION('',(0.,-1.)); +#3071 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3072 = PCURVE('',#1617,#3073); +#3073 = DEFINITIONAL_REPRESENTATION('',(#3074),#3078); +#3074 = LINE('',#3075,#3076); +#3075 = CARTESIAN_POINT('',(0.,0.)); +#3076 = VECTOR('',#3077,1.); +#3077 = DIRECTION('',(0.,-1.)); +#3078 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3079 = ORIENTED_EDGE('',*,*,#2400,.T.); +#3080 = ORIENTED_EDGE('',*,*,#3034,.F.); +#3081 = ADVANCED_FACE('',(#3082),#1617,.T.); +#3082 = FACE_BOUND('',#3083,.T.); +#3083 = EDGE_LOOP('',(#3084,#3085,#3086,#3087)); +#3084 = ORIENTED_EDGE('',*,*,#1601,.F.); +#3085 = ORIENTED_EDGE('',*,*,#3059,.T.); +#3086 = ORIENTED_EDGE('',*,*,#2379,.T.); +#3087 = ORIENTED_EDGE('',*,*,#3088,.F.); +#3088 = EDGE_CURVE('',#1569,#2352,#3089,.T.); +#3089 = SURFACE_CURVE('',#3090,(#3094,#3101),.PCURVE_S1.); +#3090 = LINE('',#3091,#3092); +#3091 = CARTESIAN_POINT('',(12.25,15.5,0.)); +#3092 = VECTOR('',#3093,1.); +#3093 = DIRECTION('',(0.,0.,1.)); +#3094 = PCURVE('',#1617,#3095); +#3095 = DEFINITIONAL_REPRESENTATION('',(#3096),#3100); +#3096 = LINE('',#3097,#3098); +#3097 = CARTESIAN_POINT('',(3.,0.)); +#3098 = VECTOR('',#3099,1.); +#3099 = DIRECTION('',(0.,-1.)); +#3100 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3101 = PCURVE('',#1589,#3102); +#3102 = DEFINITIONAL_REPRESENTATION('',(#3103),#3107); +#3103 = LINE('',#3104,#3105); +#3104 = CARTESIAN_POINT('',(4.712388980385,0.)); +#3105 = VECTOR('',#3106,1.); +#3106 = DIRECTION('',(0.,-1.)); +#3107 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3108 = ADVANCED_FACE('',(#3109),#1589,.F.); +#3109 = FACE_BOUND('',#3110,.F.); +#3110 = EDGE_LOOP('',(#3111,#3112,#3113,#3114)); +#3111 = ORIENTED_EDGE('',*,*,#1568,.F.); +#3112 = ORIENTED_EDGE('',*,*,#3012,.T.); +#3113 = ORIENTED_EDGE('',*,*,#2351,.T.); +#3114 = ORIENTED_EDGE('',*,*,#3088,.F.); +#3115 = ADVANCED_FACE('',(#3116),#1680,.T.); +#3116 = FACE_BOUND('',#3117,.T.); +#3117 = EDGE_LOOP('',(#3118,#3119,#3140,#3141)); +#3118 = ORIENTED_EDGE('',*,*,#1662,.F.); +#3119 = ORIENTED_EDGE('',*,*,#3120,.T.); +#3120 = EDGE_CURVE('',#1663,#2429,#3121,.T.); +#3121 = SURFACE_CURVE('',#3122,(#3126,#3133),.PCURVE_S1.); +#3122 = LINE('',#3123,#3124); +#3123 = CARTESIAN_POINT('',(21.5,17.75,0.)); +#3124 = VECTOR('',#3125,1.); +#3125 = DIRECTION('',(0.,0.,1.)); +#3126 = PCURVE('',#1680,#3127); +#3127 = DEFINITIONAL_REPRESENTATION('',(#3128),#3132); +#3128 = LINE('',#3129,#3130); +#3129 = CARTESIAN_POINT('',(0.,-0.)); +#3130 = VECTOR('',#3131,1.); +#3131 = DIRECTION('',(0.,-1.)); +#3132 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3133 = PCURVE('',#1713,#3134); +#3134 = DEFINITIONAL_REPRESENTATION('',(#3135),#3139); +#3135 = LINE('',#3136,#3137); +#3136 = CARTESIAN_POINT('',(1.570796326795,0.)); +#3137 = VECTOR('',#3138,1.); +#3138 = DIRECTION('',(0.,-1.)); +#3139 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3140 = ORIENTED_EDGE('',*,*,#2428,.T.); +#3141 = ORIENTED_EDGE('',*,*,#3142,.F.); +#3142 = EDGE_CURVE('',#1665,#2431,#3143,.T.); +#3143 = SURFACE_CURVE('',#3144,(#3148,#3155),.PCURVE_S1.); +#3144 = LINE('',#3145,#3146); +#3145 = CARTESIAN_POINT('',(18.5,17.75,0.)); +#3146 = VECTOR('',#3147,1.); +#3147 = DIRECTION('',(0.,0.,1.)); +#3148 = PCURVE('',#1680,#3149); +#3149 = DEFINITIONAL_REPRESENTATION('',(#3150),#3154); +#3150 = LINE('',#3151,#3152); +#3151 = CARTESIAN_POINT('',(3.,0.)); +#3152 = VECTOR('',#3153,1.); +#3153 = DIRECTION('',(0.,-1.)); +#3154 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3155 = PCURVE('',#1772,#3156); +#3156 = DEFINITIONAL_REPRESENTATION('',(#3157),#3161); +#3157 = LINE('',#3158,#3159); +#3158 = CARTESIAN_POINT('',(4.712388980385,0.)); +#3159 = VECTOR('',#3160,1.); +#3160 = DIRECTION('',(0.,-1.)); +#3161 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3162 = ADVANCED_FACE('',(#3163),#1772,.F.); +#3163 = FACE_BOUND('',#3164,.F.); +#3164 = EDGE_LOOP('',(#3165,#3166,#3187,#3188)); +#3165 = ORIENTED_EDGE('',*,*,#1753,.F.); +#3166 = ORIENTED_EDGE('',*,*,#3167,.T.); +#3167 = EDGE_CURVE('',#1726,#2480,#3168,.T.); +#3168 = SURFACE_CURVE('',#3169,(#3173,#3180),.PCURVE_S1.); +#3169 = LINE('',#3170,#3171); +#3170 = CARTESIAN_POINT('',(18.5,13.25,0.)); +#3171 = VECTOR('',#3172,1.); +#3172 = DIRECTION('',(0.,0.,1.)); +#3173 = PCURVE('',#1772,#3174); +#3174 = DEFINITIONAL_REPRESENTATION('',(#3175),#3179); +#3175 = LINE('',#3176,#3177); +#3176 = CARTESIAN_POINT('',(1.570796326795,0.)); +#3177 = VECTOR('',#3178,1.); +#3178 = DIRECTION('',(0.,-1.)); +#3179 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3180 = PCURVE('',#1741,#3181); +#3181 = DEFINITIONAL_REPRESENTATION('',(#3182),#3186); +#3182 = LINE('',#3183,#3184); +#3183 = CARTESIAN_POINT('',(0.,0.)); +#3184 = VECTOR('',#3185,1.); +#3185 = DIRECTION('',(0.,-1.)); +#3186 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3187 = ORIENTED_EDGE('',*,*,#2500,.T.); +#3188 = ORIENTED_EDGE('',*,*,#3142,.F.); +#3189 = ADVANCED_FACE('',(#3190),#1741,.T.); +#3190 = FACE_BOUND('',#3191,.T.); +#3191 = EDGE_LOOP('',(#3192,#3193,#3194,#3195)); +#3192 = ORIENTED_EDGE('',*,*,#1725,.F.); +#3193 = ORIENTED_EDGE('',*,*,#3167,.T.); +#3194 = ORIENTED_EDGE('',*,*,#2479,.T.); +#3195 = ORIENTED_EDGE('',*,*,#3196,.F.); +#3196 = EDGE_CURVE('',#1693,#2452,#3197,.T.); +#3197 = SURFACE_CURVE('',#3198,(#3202,#3209),.PCURVE_S1.); +#3198 = LINE('',#3199,#3200); +#3199 = CARTESIAN_POINT('',(21.5,13.25,0.)); +#3200 = VECTOR('',#3201,1.); +#3201 = DIRECTION('',(0.,0.,1.)); +#3202 = PCURVE('',#1741,#3203); +#3203 = DEFINITIONAL_REPRESENTATION('',(#3204),#3208); +#3204 = LINE('',#3205,#3206); +#3205 = CARTESIAN_POINT('',(3.,0.)); +#3206 = VECTOR('',#3207,1.); +#3207 = DIRECTION('',(0.,-1.)); +#3208 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3209 = PCURVE('',#1713,#3210); +#3210 = DEFINITIONAL_REPRESENTATION('',(#3211),#3215); +#3211 = LINE('',#3212,#3213); +#3212 = CARTESIAN_POINT('',(4.712388980385,0.)); +#3213 = VECTOR('',#3214,1.); +#3214 = DIRECTION('',(0.,-1.)); +#3215 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3216 = ADVANCED_FACE('',(#3217),#1713,.F.); +#3217 = FACE_BOUND('',#3218,.F.); +#3218 = EDGE_LOOP('',(#3219,#3220,#3221,#3222)); +#3219 = ORIENTED_EDGE('',*,*,#1692,.F.); +#3220 = ORIENTED_EDGE('',*,*,#3120,.T.); +#3221 = ORIENTED_EDGE('',*,*,#2451,.T.); +#3222 = ORIENTED_EDGE('',*,*,#3196,.F.); +#3223 = ADVANCED_FACE('',(#3224),#1804,.T.); +#3224 = FACE_BOUND('',#3225,.T.); +#3225 = EDGE_LOOP('',(#3226,#3227,#3248,#3249)); +#3226 = ORIENTED_EDGE('',*,*,#1786,.F.); +#3227 = ORIENTED_EDGE('',*,*,#3228,.T.); +#3228 = EDGE_CURVE('',#1787,#2529,#3229,.T.); +#3229 = SURFACE_CURVE('',#3230,(#3234,#3241),.PCURVE_S1.); +#3230 = LINE('',#3231,#3232); +#3231 = CARTESIAN_POINT('',(27.75,15.5,0.)); +#3232 = VECTOR('',#3233,1.); +#3233 = DIRECTION('',(0.,0.,1.)); +#3234 = PCURVE('',#1804,#3235); +#3235 = DEFINITIONAL_REPRESENTATION('',(#3236),#3240); +#3236 = LINE('',#3237,#3238); +#3237 = CARTESIAN_POINT('',(0.,0.)); +#3238 = VECTOR('',#3239,1.); +#3239 = DIRECTION('',(0.,-1.)); +#3240 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3241 = PCURVE('',#1837,#3242); +#3242 = DEFINITIONAL_REPRESENTATION('',(#3243),#3247); +#3243 = LINE('',#3244,#3245); +#3244 = CARTESIAN_POINT('',(1.570796326795,0.)); +#3245 = VECTOR('',#3246,1.); +#3246 = DIRECTION('',(0.,-1.)); +#3247 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3248 = ORIENTED_EDGE('',*,*,#2528,.T.); +#3249 = ORIENTED_EDGE('',*,*,#3250,.F.); +#3250 = EDGE_CURVE('',#1789,#2531,#3251,.T.); +#3251 = SURFACE_CURVE('',#3252,(#3256,#3263),.PCURVE_S1.); +#3252 = LINE('',#3253,#3254); +#3253 = CARTESIAN_POINT('',(27.75,12.5,0.)); +#3254 = VECTOR('',#3255,1.); +#3255 = DIRECTION('',(0.,0.,1.)); +#3256 = PCURVE('',#1804,#3257); +#3257 = DEFINITIONAL_REPRESENTATION('',(#3258),#3262); +#3258 = LINE('',#3259,#3260); +#3259 = CARTESIAN_POINT('',(3.,0.)); +#3260 = VECTOR('',#3261,1.); +#3261 = DIRECTION('',(0.,-1.)); +#3262 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3263 = PCURVE('',#1896,#3264); +#3264 = DEFINITIONAL_REPRESENTATION('',(#3265),#3269); +#3265 = LINE('',#3266,#3267); +#3266 = CARTESIAN_POINT('',(4.712388980385,0.)); +#3267 = VECTOR('',#3268,1.); +#3268 = DIRECTION('',(0.,-1.)); +#3269 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3270 = ADVANCED_FACE('',(#3271),#1896,.F.); +#3271 = FACE_BOUND('',#3272,.F.); +#3272 = EDGE_LOOP('',(#3273,#3274,#3295,#3296)); +#3273 = ORIENTED_EDGE('',*,*,#1877,.F.); +#3274 = ORIENTED_EDGE('',*,*,#3275,.T.); +#3275 = EDGE_CURVE('',#1850,#2580,#3276,.T.); +#3276 = SURFACE_CURVE('',#3277,(#3281,#3288),.PCURVE_S1.); +#3277 = LINE('',#3278,#3279); +#3278 = CARTESIAN_POINT('',(32.25,12.5,0.)); +#3279 = VECTOR('',#3280,1.); +#3280 = DIRECTION('',(0.,0.,1.)); +#3281 = PCURVE('',#1896,#3282); +#3282 = DEFINITIONAL_REPRESENTATION('',(#3283),#3287); +#3283 = LINE('',#3284,#3285); +#3284 = CARTESIAN_POINT('',(1.570796326795,0.)); +#3285 = VECTOR('',#3286,1.); +#3286 = DIRECTION('',(0.,-1.)); +#3287 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3288 = PCURVE('',#1865,#3289); +#3289 = DEFINITIONAL_REPRESENTATION('',(#3290),#3294); +#3290 = LINE('',#3291,#3292); +#3291 = CARTESIAN_POINT('',(0.,0.)); +#3292 = VECTOR('',#3293,1.); +#3293 = DIRECTION('',(0.,-1.)); +#3294 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3295 = ORIENTED_EDGE('',*,*,#2600,.T.); +#3296 = ORIENTED_EDGE('',*,*,#3250,.F.); +#3297 = ADVANCED_FACE('',(#3298),#1865,.T.); +#3298 = FACE_BOUND('',#3299,.T.); +#3299 = EDGE_LOOP('',(#3300,#3301,#3302,#3303)); +#3300 = ORIENTED_EDGE('',*,*,#1849,.F.); +#3301 = ORIENTED_EDGE('',*,*,#3275,.T.); +#3302 = ORIENTED_EDGE('',*,*,#2579,.T.); +#3303 = ORIENTED_EDGE('',*,*,#3304,.F.); +#3304 = EDGE_CURVE('',#1817,#2552,#3305,.T.); +#3305 = SURFACE_CURVE('',#3306,(#3310,#3317),.PCURVE_S1.); +#3306 = LINE('',#3307,#3308); +#3307 = CARTESIAN_POINT('',(32.25,15.5,0.)); +#3308 = VECTOR('',#3309,1.); +#3309 = DIRECTION('',(0.,0.,1.)); +#3310 = PCURVE('',#1865,#3311); +#3311 = DEFINITIONAL_REPRESENTATION('',(#3312),#3316); +#3312 = LINE('',#3313,#3314); +#3313 = CARTESIAN_POINT('',(3.,0.)); +#3314 = VECTOR('',#3315,1.); +#3315 = DIRECTION('',(0.,-1.)); +#3316 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3317 = PCURVE('',#1837,#3318); +#3318 = DEFINITIONAL_REPRESENTATION('',(#3319),#3323); +#3319 = LINE('',#3320,#3321); +#3320 = CARTESIAN_POINT('',(4.712388980385,0.)); +#3321 = VECTOR('',#3322,1.); +#3322 = DIRECTION('',(0.,-1.)); +#3323 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#3324 = ADVANCED_FACE('',(#3325),#1837,.F.); +#3325 = FACE_BOUND('',#3326,.F.); +#3326 = EDGE_LOOP('',(#3327,#3328,#3329,#3330)); +#3327 = ORIENTED_EDGE('',*,*,#1816,.F.); +#3328 = ORIENTED_EDGE('',*,*,#3228,.T.); +#3329 = ORIENTED_EDGE('',*,*,#2551,.T.); +#3330 = ORIENTED_EDGE('',*,*,#3304,.F.); +#3331 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) +GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3335)) +GLOBAL_UNIT_ASSIGNED_CONTEXT((#3332,#3333,#3334)) REPRESENTATION_CONTEXT +('Context #1','3D Context with UNIT and UNCERTAINTY') ); +#3332 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) ); +#3333 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) ); +#3334 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() ); +#3335 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-07),#3332, + 'distance_accuracy_value','confusion accuracy'); +#3336 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7)); +#3337 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',( + #3338),#3331); +#3338 = STYLED_ITEM('color',(#3339),#15); +#3339 = PRESENTATION_STYLE_ASSIGNMENT((#3340)); +#3340 = SURFACE_STYLE_USAGE(.BOTH.,#3341); +#3341 = SURFACE_SIDE_STYLE('',(#3342)); +#3342 = SURFACE_STYLE_FILL_AREA(#3343); +#3343 = FILL_AREA_STYLE('',(#3344)); +#3344 = FILL_AREA_STYLE_COLOUR('',#3345); +#3345 = DRAUGHTING_PRE_DEFINED_COLOUR('black'); +ENDSEC; +END-ISO-10303-21; diff --git a/docs/tutorial_joints.py b/docs/tutorial_joints.py index e6d2fd3..ba1b149 100644 --- a/docs/tutorial_joints.py +++ b/docs/tutorial_joints.py @@ -159,7 +159,7 @@ class Hinge(Compound): for hole, hole_location in enumerate(hole_locations): CylindricalJoint( label="hole" + str(hole), - axis=hole_location.to_axis(), + axis=Axis(hole_location), linear_range=(-2 * CM, 2 * CM), angular_range=(0, 360), ) diff --git a/docs/tutorials.rst b/docs/tutorials.rst index 7ea3407..5ae7c96 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -16,3 +16,4 @@ as later tutorials build on the concepts introduced in earlier ones. examples_1.rst tttt.rst tutorial_surface_modeling.rst + tech_drawing_tutorial.rst diff --git a/examples/bicycle_tire.py b/examples/bicycle_tire.py new file mode 100644 index 0000000..dc84a7f --- /dev/null +++ b/examples/bicycle_tire.py @@ -0,0 +1,109 @@ +""" +A bicycle tire with tread. + +name: bicycle_tire.py +by: Gumyr +date: May 20, 2025 + +desc: + + This example demonstrates how to model a realistic bicycle tire with a + patterned tread using build123d. The key concept showcased here is the + use of wrap_faces to project 2D planar geometry onto a curved 3D + surface. + + The tire cross-section is defined using a series of Bezier curves and + revolved to form the main tire body. A 2D tread pattern is created as a + sketch on a plane and then wrapped onto the non-planar revolved surface + using wrap_faces, following a path on the surface. The wrapped faces are + then thickened into 3D solid nubs and copied around the tire using + rotational placement. + + This technique is particularly useful for applying surface detail—such + as grooves, logos, or textures—to curved or freeform geometries in a CAD + model. + + Highlights: + - Complex profile creation using multiple Bezier segments. + - Surface wrapping of planar sketches using wrap_faces. + - Solidification of surface features via thicken. + - Circular duplication of solids using rotational transforms. + +license: + + Copyright 2025 Gumyr + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +""" + +# [Code] +import copy +from build123d import * +from ocp_vscode import show + +wheel_diameter = 740 * MM + +with BuildSketch() as tire_profile: + with BuildLine() as build_profile: + l00 = Bezier((0.0, 0.0), (7.05, 0.0), (12.18, 1.54), (15.13, 4.54)) + l01 = Bezier(l00 @ 1, (15.81, 5.22), (15.98, 5.44), (16.5, 6.23)) + l02 = Bezier(l01 @ 1, (18.45, 9.19), (19.61, 13.84), (19.94, 20.06)) + l03 = Bezier(l02 @ 1, (20.1, 23.24), (19.93, 27.48), (19.56, 29.45)) + l04 = Bezier(l03 @ 1, (19.13, 31.69), (18.23, 33.67), (16.91, 35.32)) + l05 = Bezier(l04 @ 1, (16.26, 36.12), (15.57, 36.77), (14.48, 37.58)) + l06 = Bezier(l05 @ 1, (12.77, 38.85), (11.51, 40.28), (10.76, 41.78)) + l07 = Bezier(l06 @ 1, (10.07, 43.16), (10.15, 43.81), (11.03, 43.98)) + l08 = Bezier(l07 @ 1, (11.82, 44.13), (12.15, 44.55), (12.08, 45.33)) + l09 = Bezier(l08 @ 1, (12.01, 46.07), (11.84, 46.43), (11.43, 46.69)) + l10 = Bezier(l09 @ 1, (10.98, 46.97), (10.07, 46.7), (9.47, 46.1)) + l11 = Bezier(l10 @ 1, (9.03, 45.65), (8.88, 45.31), (8.84, 44.65)) + l12 = Bezier(l11 @ 1, (8.78, 43.6), (9.11, 42.26), (9.72, 41.0)) + l13 = Bezier(l12 @ 1, (10.43, 39.54), (11.52, 38.2), (12.78, 37.22)) + l14 = Bezier(l13 @ 1, (15.36, 35.23), (16.58, 33.76), (17.45, 31.62)) + l15 = Bezier(l14 @ 1, (17.91, 30.49), (18.22, 29.27), (18.4, 27.8)) + l16 = Bezier(l15 @ 1, (18.53, 26.78), (18.52, 23.69), (18.37, 22.61)) + l17 = Bezier(l16 @ 1, (17.8, 18.23), (16.15, 14.7), (13.39, 11.94)) + l18 = Bezier(l17 @ 1, (11.89, 10.45), (10.19, 9.31), (8.09, 8.41)) + l19 = Bezier(l18 @ 1, (3.32, 6.35), (0.0, 6.64)) + mirror(about=Plane.YZ) + make_face() + +tire = revolve(Pos(Y=-wheel_diameter / 2) * tire_profile.face(), Axis.X) + +with BuildSketch() as tread_pattern: + with Locations((1, 1)): + Trapezoid(15, 12, 60, 120, align=Align.MIN) + with Locations((1, 8)): + with GridLocations(0, 5, 1, 2): + Rectangle(50, 2, mode=Mode.SUBTRACT) + +# Define the surface and path that the tread pattern will be wrapped onto +half_road_surface = Face.revolve(Pos(Y=-wheel_diameter / 2) * l00, 360, Axis.X) +tread_path = half_road_surface.edges().sort_by(Axis.X)[0] + +# Wrap the planar tread pattern onto the tire's outside surface +tread_faces = half_road_surface.wrap_faces(tread_pattern.faces(), tread_path) + +# Mirror the faces to the other half of the tire +tread_faces.extend([mirror(t, Plane.YZ) for t in tread_faces]) + +# Thicken the tread to become solid nubs +# tread_prime = [Solid.thicken(f, 3 * MM) for f in tread_faces] +tread_prime = [thicken(f, 3 * MM) for f in tread_faces] + +# Copy the nubs around the whole tire +tread = [Rot(X=r) * copy.copy(t) for t in tread_prime for r in range(0, 360, 2)] + +show(tire, tread) +# [End] diff --git a/examples/cast_bearing_unit.py b/examples/cast_bearing_unit.py new file mode 100644 index 0000000..51f40a8 --- /dev/null +++ b/examples/cast_bearing_unit.py @@ -0,0 +1,73 @@ +""" +An oval flanged bearing unit with tapered sides created with the draft operation. + +name: cast_bearing_unit.py +by: Gumyr +date: May 25, 2025 + +desc: + + This example demonstrates the creation of a castable flanged bearing housing + using the `draft` operation to add appropriate draft angles for mold release. + + ### Highlights: + + - **Component Integration**: The design incorporates a press-fit bore for a + `SingleRowAngularContactBallBearing` and mounting holes for + `SocketHeadCapScrew` fasteners. + - **Draft Angle Application**: Vertical side faces are identified and modified + with a 4-degree draft angle using the `draft()` function. This simulates the + taper needed for cast parts to be removed cleanly from a mold. + - **Filleting**: All edges are filleted to reflect casting-friendly geometry and + improve aesthetics. + - **Parametric Design**: Dimensions such as bolt spacing, bearing size, and + housing depth are parameterized for reuse and adaptation to other sizes. + + The result is a realistic, fabrication-aware model that can be used for + documentation, simulation, or manufacturing workflows. The final assembly + includes the housing, inserted bearing, and positioned screws, rendered with + appropriate coloring for clarity. + +license: + + Copyright 2025 Gumyr + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +""" + +# [Code] +from build123d import * +from ocp_vscode import show + +A, A1, Db2, H, J = 26, 11, 57, 98.5, 76.5 +with BuildPart() as oval_flanged_bearing_unit: + with BuildSketch() as plan: + housing = Circle(Db2 / 2) + with GridLocations(J, 0, 2, 1) as bolt_centers: + Circle((H - J) / 2) + make_hull() + extrude(amount=A1) + extrude(housing, amount=A) + drafted_faces = oval_flanged_bearing_unit.faces().filter_by(Axis.Z, reverse=True) + draft(drafted_faces, Plane.XY, 4) + fillet(oval_flanged_bearing_unit.edges(), 1) + with Locations(oval_flanged_bearing_unit.faces().sort_by(Axis.Z)[-1]): + CounterBoreHole(14 / 2, 47 / 2, 14) + with Locations(*bolt_centers): + Hole(5) + +oval_flanged_bearing_unit.part.color = Color(0x4C6377) + +show(oval_flanged_bearing_unit) +# [End] diff --git a/examples/fast_grid_holes.py b/examples/fast_grid_holes.py new file mode 100644 index 0000000..1d6ca82 --- /dev/null +++ b/examples/fast_grid_holes.py @@ -0,0 +1,65 @@ +""" +A fast way to make many holes. + +name: fast_grid_holes.py +by: Gumyr +date: May 31, 2025 + +desc: + + This example demonstrates an efficient approach to creating a large number of holes + (625 in this case) in a planar part using build123d. + + Instead of modeling and subtracting 3D solids for each hole—which is computationally + expensive—this method constructs a 2D Face from an outer perimeter wire and a list of + hole wires. The entire face is then extruded in a single operation to form the final + 3D object. This approach significantly reduces modeling time and complexity. + + The hexagonal hole pattern is generated using HexLocations, and each location is + populated with a hexagonal wire. These wires are passed directly to the Face constructor + as holes. On a typical Linux laptop, this script completes in approximately 1.02 seconds, + compared to substantially longer runtimes for boolean subtraction of individual holes in 3D. + +license: + + Copyright 2025 Gumyr + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +""" + +# [Code] +import timeit +from build123d import * +from ocp_vscode import show + +start_time = timeit.default_timer() + +# Calculate the locations of 625 holes +major_r = 10 +hole_locs = HexLocations(major_r, 25, 25) + +# Create wires for both the perimeter and all the holes +face_perimeter = Rectangle(500, 600).wire() +hex_hole = RegularPolygon(major_r - 1, 6, major_radius=True).wire() +holes = hole_locs * hex_hole + +# Create a new Face from the perimeter and hole wires +grid_pattern = Face(face_perimeter, holes) + +# Extrude to a 3D part +grid = extrude(grid_pattern, 1) + +print(f"Time: {timeit.default_timer() - start_time:0.3f}s") +show(grid) +# [End] diff --git a/examples/joints.py b/examples/joints.py index f143af5..c51fb50 100644 --- a/examples/joints.py +++ b/examples/joints.py @@ -1,6 +1,7 @@ """ Experimental Joint development file """ + from build123d import * from ocp_vscode import * @@ -72,9 +73,9 @@ swing_arm_hinge_edge: Edge = ( .sort_by(Axis.X)[-2:] .sort_by(Axis.Y)[0] ) -swing_arm_hinge_axis = swing_arm_hinge_edge.to_axis() +swing_arm_hinge_axis = Axis(swing_arm_hinge_edge) base_corner_edge = base.edges().sort_by(Axis((0, 0, 0), (1, 1, 0)))[-1] -base_hinge_axis = base_corner_edge.to_axis() +base_hinge_axis = Axis(base_corner_edge) j3 = RevoluteJoint("hinge", base, axis=base_hinge_axis, angular_range=(0, 180)) j4 = RigidJoint("corner", hinge_arm, swing_arm_hinge_axis.location) base.joints["hinge"].connect_to(hinge_arm.joints["corner"], angle=90) @@ -86,7 +87,7 @@ slider_arm = JointBox(4, 1, 2, 0.2) s1 = LinearJoint( "slide", base, - axis=Edge.make_mid_way(*base_top_edges, 0.67).to_axis(), + axis=Axis(Edge.make_mid_way(*base_top_edges, 0.67)), linear_range=(0, base_top_edges[0].length), ) s2 = RigidJoint("slide", slider_arm, Location(Vector(0, 0, 0))) @@ -111,7 +112,7 @@ j5.connect_to(j6, position=-1, angle=90) j7 = LinearJoint( "slot", base, - axis=Edge.make_mid_way(*base_top_edges, 0.33).to_axis(), + axis=Axis(Edge.make_mid_way(*base_top_edges, 0.33)), linear_range=(0, base_top_edges[0].length), ) pin_arm = JointBox(2, 1, 2) diff --git a/examples/joints_algebra.py b/examples/joints_algebra.py index c0da394..1484329 100644 --- a/examples/joints_algebra.py +++ b/examples/joints_algebra.py @@ -62,9 +62,9 @@ swing_arm_hinge_edge = ( .sort_by(Axis.X)[-2:] .sort_by(Axis.Y)[0] ) -swing_arm_hinge_axis = swing_arm_hinge_edge.to_axis() +swing_arm_hinge_axis = Axis(swing_arm_hinge_edge) base_corner_edge = base.edges().sort_by(Axis((0, 0, 0), (1, 1, 0)))[-1] -base_hinge_axis = base_corner_edge.to_axis() +base_hinge_axis = Axis(base_corner_edge) j3 = RevoluteJoint("hinge", base, axis=base_hinge_axis, angular_range=(0, 180)) j4 = RigidJoint("corner", hinge_arm, swing_arm_hinge_axis.location) base.joints["hinge"].connect_to(hinge_arm.joints["corner"], angle=90) @@ -77,7 +77,7 @@ slider_arm = JointBox(4, 1, 2, 0.2) s1 = LinearJoint( "slide", base, - axis=Edge.make_mid_way(*base_top_edges, 0.67).to_axis(), + axis=Axis(Edge.make_mid_way(*base_top_edges, 0.67)), linear_range=(0, base_top_edges[0].length), ) s2 = RigidJoint("slide", slider_arm, Location(Vector(0, 0, 0))) @@ -102,7 +102,7 @@ j5.connect_to(j6, position=-1, angle=90) j7 = LinearJoint( "slot", base, - axis=Edge.make_mid_way(*base_top_edges, 0.33).to_axis(), + axis=Axis(Edge.make_mid_way(*base_top_edges, 0.33)), linear_range=(0, base_top_edges[0].length), ) pin_arm = JointBox(2, 1, 2) diff --git a/examples/lego.py b/examples/lego.py index 88cb1a4..0cba2b3 100644 --- a/examples/lego.py +++ b/examples/lego.py @@ -75,7 +75,7 @@ with BuildPart() as lego: exporter = ExportSVG(scale=6) exporter.add_shape(plan.sketch) exporter.write("assets/lego_step6.svg") - # Substract a rectangle leaving ribs on the block walls + # Subtract a rectangle leaving ribs on the block walls Rectangle( block_length - 2 * (wall_thickness + ridge_depth), block_width - 2 * (wall_thickness + ridge_depth), diff --git a/examples/lego_algebra.py b/examples/lego_algebra.py index 9df8132..54abec2 100644 --- a/examples/lego_algebra.py +++ b/examples/lego_algebra.py @@ -34,7 +34,7 @@ plan += locs * Rectangle(width=block_length, height=ridge_width) locs = GridLocations(lego_unit_size, 0, pip_count, 1) plan += locs * Rectangle(width=ridge_width, height=block_width) -# Substract a rectangle leaving ribs on the block walls +# Subtract a rectangle leaving ribs on the block walls plan -= Rectangle( block_length - 2 * (wall_thickness + ridge_depth), block_width - 2 * (wall_thickness + ridge_depth), diff --git a/src/build123d/__init__.py b/src/build123d/__init__.py index 9ca383a..209c248 100644 --- a/src/build123d/__init__.py +++ b/src/build123d/__init__.py @@ -163,6 +163,7 @@ __all__ = [ "LinearJoint", "CylindricalJoint", "BallJoint", + "DraftAngleError", # Exporter classes "Export2D", "ExportDXF", @@ -197,6 +198,7 @@ __all__ = [ "add", "bounding_box", "chamfer", + "draft", "extrude", "fillet", "full_round", diff --git a/src/build123d/build_common.py b/src/build123d/build_common.py index 947885d..e5edde3 100644 --- a/src/build123d/build_common.py +++ b/src/build123d/build_common.py @@ -50,7 +50,7 @@ import functools from abc import ABC, abstractmethod from itertools import product from math import sqrt, cos, pi -from typing import Any, cast, overload, Protocol, Type, TypeVar +from typing import Any, cast, overload, Protocol, Type, TypeVar, Generic from collections.abc import Callable, Iterable from typing_extensions import Self @@ -155,6 +155,7 @@ operations_apply_to = { "add": ["BuildPart", "BuildSketch", "BuildLine"], "bounding_box": ["BuildPart", "BuildSketch", "BuildLine"], "chamfer": ["BuildPart", "BuildSketch", "BuildLine"], + "draft": ["BuildPart"], "extrude": ["BuildPart"], "fillet": ["BuildPart", "BuildSketch", "BuildLine"], "full_round": ["BuildSketch"], @@ -177,8 +178,11 @@ operations_apply_to = { B = TypeVar("B", bound="Builder") """Builder type hint""" +ShapeT = TypeVar("ShapeT", bound=Shape) +"""Builder's are generic shape creators""" -class Builder(ABC): + +class Builder(ABC, Generic[ShapeT]): """Builder Base class for the build123d Builders. @@ -230,7 +234,7 @@ class Builder(ABC): @property @abstractmethod - def _obj(self) -> Shape: + def _obj(self) -> Shape | None: """Object to pass to parent""" raise NotImplementedError # pragma: no cover @@ -247,6 +251,8 @@ class Builder(ABC): @property def new_edges(self) -> ShapeList[Edge]: """Edges that changed during last operation""" + if self._obj is None: + return ShapeList() before_list = [] if self.obj_before is None else [self.obj_before] return new_edges(*(before_list + self.to_combine), combined=self._obj) @@ -534,7 +540,8 @@ class Builder(ABC): """ vertex_list: list[Vertex] = [] if select == Select.ALL: - for obj_edge in self._obj.edges(): + obj_edges = [] if self._obj is None else self._obj.edges() + for obj_edge in obj_edges: vertex_list.extend(obj_edge.vertices()) elif select == Select.LAST: vertex_list = self.lasts[Vertex] @@ -578,7 +585,7 @@ class Builder(ABC): ShapeList[Edge]: Edges extracted """ if select == Select.ALL: - edge_list = self._obj.edges() + edge_list = ShapeList() if self._obj is None else self._obj.edges() elif select == Select.LAST: edge_list = self.lasts[Edge] elif select == Select.NEW: @@ -621,7 +628,7 @@ class Builder(ABC): ShapeList[Wire]: Wires extracted """ if select == Select.ALL: - wire_list = self._obj.wires() + wire_list = ShapeList() if self._obj is None else self._obj.wires() elif select == Select.LAST: wire_list = Wire.combine(self.lasts[Edge]) elif select == Select.NEW: @@ -664,7 +671,7 @@ class Builder(ABC): ShapeList[Face]: Faces extracted """ if select == Select.ALL: - face_list = self._obj.faces() + face_list = ShapeList() if self._obj is None else self._obj.faces() elif select == Select.LAST: face_list = self.lasts[Face] elif select == Select.NEW: @@ -707,7 +714,7 @@ class Builder(ABC): ShapeList[Solid]: Solids extracted """ if select == Select.ALL: - solid_list = self._obj.solids() + solid_list = ShapeList() if self._obj is None else self._obj.solids() elif select == Select.LAST: solid_list = self.lasts[Solid] elif select == Select.NEW: @@ -744,17 +751,18 @@ class Builder(ABC): ) -> ShapeList: """Extract Shapes""" obj_type = self._shape if obj_type is None else obj_type + if self._obj is None: + return ShapeList() + if obj_type == Vertex: - result = self._obj.vertices() - elif obj_type == Edge: - result = self._obj.edges() - elif obj_type == Face: - result = self._obj.faces() - elif obj_type == Solid: - result = self._obj.solids() - else: - result = None - return result + return self._obj.vertices() + if obj_type == Edge: + return self._obj.edges() + if obj_type == Face: + return self._obj.faces() + if obj_type == Solid: + return self._obj.solids() + return ShapeList() def validate_inputs( self, validating_class, objects: Shape | Iterable[Shape] | None = None @@ -1110,7 +1118,7 @@ class Locations(LocationList): elif isinstance(point, Vector): local_locations.append(Location(point)) elif isinstance(point, Vertex): - local_locations.append(Location(Vector(point.to_tuple()))) + local_locations.append(Location(Vector(point))) elif isinstance(point, tuple): local_locations.append(Location(Vector(point))) elif isinstance(point, Plane): @@ -1377,8 +1385,8 @@ def __gen_context_component_getter( @functools.wraps(func) def getter(select: Select = Select.ALL) -> T2: # Retrieve the current Builder context based on the method name - context = Builder._get_context(func.__name__) - if not context: + context: Builder | None = Builder._get_context(func.__name__) + if context is None: raise RuntimeError( f"{func.__name__}() requires a Builder context to be in scope" ) diff --git a/src/build123d/build_line.py b/src/build123d/build_line.py index 79c8252..52e9e74 100644 --- a/src/build123d/build_line.py +++ b/src/build123d/build_line.py @@ -36,7 +36,7 @@ from build123d.geometry import Location, Plane from build123d.topology import Curve, Edge, Face -class BuildLine(Builder): +class BuildLine(Builder[Curve]): """BuildLine The BuildLine class is a subclass of Builder for building lines (objects @@ -89,7 +89,15 @@ class BuildLine(Builder): """Set the current line""" self._line = value - _obj = line # Alias _obj to line + @property + def _obj(self) -> Curve | None: + """Alias _obj to line""" + return self._line + + @_obj.setter + def _obj(self, value: Curve) -> None: + """Set the current line""" + self._line = value def __exit__(self, exception_type, exception_value, traceback): """Upon exiting restore context and send object to parent""" diff --git a/src/build123d/build_part.py b/src/build123d/build_part.py index 3120f44..d37bdae 100644 --- a/src/build123d/build_part.py +++ b/src/build123d/build_part.py @@ -37,7 +37,7 @@ from build123d.geometry import Location, Plane from build123d.topology import Edge, Face, Joint, Part, Solid, Wire -class BuildPart(Builder): +class BuildPart(Builder[Part]): """BuildPart The BuildPart class is another subclass of Builder for building parts @@ -80,7 +80,15 @@ class BuildPart(Builder): """Set the current part""" self._part = value - _obj = part # Alias _obj to part + @property + def _obj(self) -> Part | None: + """Alias _obj to part""" + return self._part + + @_obj.setter + def _obj(self, value: Part) -> None: + """Set the current part""" + self._part = value @property def pending_edges_as_wire(self) -> Wire: diff --git a/src/build123d/build_sketch.py b/src/build123d/build_sketch.py index 7509000..496ef4e 100644 --- a/src/build123d/build_sketch.py +++ b/src/build123d/build_sketch.py @@ -36,7 +36,7 @@ from build123d.geometry import Location, Plane from build123d.topology import Compound, Edge, Face, ShapeList, Sketch, Wire -class BuildSketch(Builder): +class BuildSketch(Builder[Sketch]): """BuildSketch The BuildSketch class is a subclass of Builder for building planar 2D @@ -83,7 +83,15 @@ class BuildSketch(Builder): """Set the builder's object""" self._sketch_local = value - _obj = sketch_local # Alias _obj to sketch_local + @property + def _obj(self) -> Sketch | None: + """Alias _obj to sketch""" + return self._sketch_local + + @_obj.setter + def _obj(self, value: Sketch) -> None: + """Set the current sketch""" + self._sketch_local = value @property def sketch(self): diff --git a/src/build123d/drafting.py b/src/build123d/drafting.py index 176e6e6..ea93fd4 100644 --- a/src/build123d/drafting.py +++ b/src/build123d/drafting.py @@ -277,10 +277,7 @@ class Draft: if isinstance(path, (Edge, Wire)): processed_path = path elif isinstance(path, Iterable): - pnts = [ - Vector(p.to_tuple()) if isinstance(p, Vertex) else Vector(p) - for p in path - ] + pnts = [Vector(p) for p in path] if len(pnts) == 2: processed_path = Edge.make_line(*pnts) else: @@ -458,7 +455,7 @@ class DimensionLine(BaseSketchObject): else: self_intersection_area = self_intersection.area d_line += placed_label - bbox_size = d_line.bounding_box().size + bbox_size = d_line.bounding_box().diagonal # Minimize size while avoiding intersections if sketch is None: @@ -472,7 +469,7 @@ class DimensionLine(BaseSketchObject): else: common_area = line_intersection.area common_area += self_intersection_area - score = (d_line.area - 10 * common_area) / bbox_size.X + score = (d_line.area - 10 * common_area) / bbox_size d_lines[d_line] = score # Sort by score to find the best option diff --git a/src/build123d/exporters3d.py b/src/build123d/exporters3d.py index 7f2b53b..749e331 100644 --- a/src/build123d/exporters3d.py +++ b/src/build123d/exporters3d.py @@ -29,10 +29,10 @@ license: # pylint has trouble with the OCP imports # pylint: disable=no-name-in-module, import-error -from io import BytesIO +from datetime import datetime import warnings +from io import BytesIO from os import PathLike, fsdecode, fspath -from typing import Union import OCP.TopAbs as ta from anytree import PreOrderIter @@ -47,7 +47,11 @@ from OCP.RWGltf import RWGltf_CafWriter from OCP.STEPCAFControl import STEPCAFControl_Controller, STEPCAFControl_Writer from OCP.STEPControl import STEPControl_Controller, STEPControl_StepModelType from OCP.StlAPI import StlAPI_Writer -from OCP.TCollection import TCollection_AsciiString, TCollection_ExtendedString, TCollection_HAsciiString +from OCP.TCollection import ( + TCollection_AsciiString, + TCollection_ExtendedString, + TCollection_HAsciiString, +) from OCP.TColStd import TColStd_IndexedDataMapOfStringString from OCP.TDataStd import TDataStd_Name from OCP.TDF import TDF_Label @@ -262,6 +266,8 @@ def export_step( unit: Unit = Unit.MM, write_pcurves: bool = True, precision_mode: PrecisionMode = PrecisionMode.AVERAGE, + *, # Too many positional arguments + timestamp: str | datetime | None = None, ) -> bool: """export_step @@ -302,6 +308,11 @@ def export_step( header = APIHeaderSection_MakeHeader(writer.Writer().Model()) if to_export.label: header.SetName(TCollection_HAsciiString(to_export.label)) + if timestamp is not None: + if isinstance(timestamp, datetime): + header.SetTimeStamp(TCollection_HAsciiString(timestamp.isoformat())) + else: + header.SetTimeStamp(TCollection_HAsciiString(timestamp)) # consider using e.g. the non *Value versions instead # header.SetAuthorValue(1, TCollection_HAsciiString("Volker")); # header.SetOrganizationValue(1, TCollection_HAsciiString("myCompanyName")); diff --git a/src/build123d/geometry.py b/src/build123d/geometry.py index 041ec61..0404ca4 100644 --- a/src/build123d/geometry.py +++ b/src/build123d/geometry.py @@ -38,15 +38,12 @@ import copy as copy_module import itertools import json import logging -import numpy as np import warnings +from collections.abc import Callable, Iterable, Sequence +from math import degrees, isclose, log10, pi, radians +from typing import TYPE_CHECKING, Any, TypeAlias, overload -from collections.abc import Iterable, Sequence -from math import degrees, log10, pi, radians, isclose -from typing import Any, overload, TypeAlias, TYPE_CHECKING - -import OCP.TopAbs as TopAbs_ShapeEnum - +import numpy as np from OCP.Bnd import Bnd_Box, Bnd_OBB from OCP.BRep import BRep_Tool from OCP.BRepBndLib import BRepBndLib @@ -54,7 +51,7 @@ from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Transform from OCP.BRepGProp import BRepGProp, BRepGProp_Face # used for mass calculation from OCP.BRepTools import BRepTools from OCP.Geom import Geom_BoundedSurface, Geom_Line, Geom_Plane -from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf, GeomAPI_IntCS, GeomAPI_IntSS +from OCP.GeomAPI import GeomAPI_IntCS, GeomAPI_IntSS, GeomAPI_ProjectPointOnSurf from OCP.gp import ( gp_Ax1, gp_Ax2, @@ -74,10 +71,11 @@ from OCP.gp import ( # properties used to store mass calculation result from OCP.GProp import GProp_GProps from OCP.Quantity import Quantity_Color, Quantity_ColorRGBA +from OCP.TopAbs import TopAbs_ShapeEnum from OCP.TopLoc import TopLoc_Location from OCP.TopoDS import TopoDS, TopoDS_Edge, TopoDS_Face, TopoDS_Shape, TopoDS_Vertex -from build123d.build_enums import Align, Align2DType, Align3DType, Intrinsic, Extrinsic +from build123d.build_enums import Align, Align2DType, Align3DType, Extrinsic, Intrinsic if TYPE_CHECKING: # pragma: no cover from .topology import Edge, Face, Shape, Vertex @@ -276,6 +274,12 @@ class Vector: def to_tuple(self) -> tuple[float, float, float]: """Return tuple equivalent""" + warnings.warn( + "to_tuple is deprecated and will be removed in a future version. " + "Use 'tuple(Vector)' instead.", + DeprecationWarning, + stacklevel=2, + ) return (self.X, self.Y, self.Z) @property @@ -497,8 +501,8 @@ class Vector: return_value = Vector(gp_Vec(pnt_t.XYZ())) else: # to gp_Dir for transformation of "direction vectors" (no translation or scaling) - dir = self.to_dir() - dir_t = dir.Transformed(affine_transform.wrapped.Trsf()) + gp_dir = self.to_dir() + dir_t = gp_dir.Transformed(affine_transform.wrapped.Trsf()) return_value = Vector(gp_Vec(dir_t.XYZ())) return return_value @@ -533,6 +537,7 @@ class Vector: """Find intersection of plane and vector""" def intersect(self, *args, **kwargs): + """Find intersection of geometric objects and vector""" axis, plane, vector, location, shape = _parse_intersect_args(*args, **kwargs) if axis is not None: @@ -550,6 +555,8 @@ class Vector: if shape is not None: return shape.intersect(self) + return None + VectorLike: TypeAlias = ( Vector | tuple[float, float] | tuple[float, float, float] | Sequence[float] @@ -592,6 +599,7 @@ class Axis(metaclass=AxisMeta): origin (VectorLike): start point direction (VectorLike): direction edge (Edge): origin & direction defined by start of edge + location (Location): location to convert to axis Attributes: position (Vector): the global position of the axis origin @@ -602,90 +610,103 @@ class Axis(metaclass=AxisMeta): _dim = 1 @overload - def __init__(self, gp_ax1: gp_Ax1): # pragma: no cover + def __init__(self, gp_ax1: gp_Ax1): """Axis: point and direction""" @overload - def __init__(self, origin: VectorLike, direction: VectorLike): # pragma: no cover + def __init__(self, location: Location): + """Axis from location""" + + @overload + def __init__(self, origin: VectorLike, direction: VectorLike): """Axis: point and direction""" @overload - def __init__(self, edge: Edge): # pragma: no cover + def __init__(self, edge: Edge): """Axis: start of Edge""" - def __init__(self, *args, **kwargs): + def __init__( + self, *args, **kwargs + ): # pylint: disable=too-many-branches, too-many-locals gp_ax1 = kwargs.pop("gp_ax1", None) origin = kwargs.pop("origin", None) direction = kwargs.pop("direction", None) edge = kwargs.pop("edge", None) + location = kwargs.pop("location", None) # Handle unexpected kwargs if kwargs: raise ValueError(f"Unexpected argument(s): {', '.join(kwargs.keys())}") + # Handle positional arguments if len(args) == 1: - if isinstance(args[0], gp_Ax1): - gp_ax1 = args[0] - elif ( - hasattr(args[0], "wrapped") - and args[0].wrapped is not None - and isinstance(args[0].wrapped, TopoDS_Edge) - ): - edge = args[0] + arg = args[0] + if isinstance(arg, gp_Ax1): + gp_ax1 = arg + elif isinstance(arg, Location): + location = arg + elif hasattr(arg, "wrapped") and isinstance(arg.wrapped, TopoDS_Edge): + edge = arg + elif isinstance(arg, (Vector, tuple)): + origin = arg else: - origin = args[0] + raise ValueError(f"Unrecognized single argument: {arg}") elif len(args) == 2: origin, direction = args + # Handle edge-based construction if edge is not None: - if ( - hasattr(edge, "wrapped") - and edge.wrapped is not None - and isinstance(edge.wrapped, TopoDS_Edge) - ): - # Extract the start point and tangent - topods_edge: TopoDS_Edge = edge.wrapped # type: ignore[annotation-unchecked] - curve = BRep_Tool.Curve_s(topods_edge, float(), float()) - param_min, param_max = BRep_Tool.Range_s(topods_edge) - origin_pnt = gp_Pnt() - tangent_vec = gp_Vec() - curve.D1(param_min, origin_pnt, tangent_vec) - origin = Vector(origin_pnt) - direction = Vector(gp_Dir(tangent_vec)) - else: - raise ValueError(f"Invalid argument {edge}") + if not (hasattr(edge, "wrapped") and isinstance(edge.wrapped, TopoDS_Edge)): + raise ValueError(f"Invalid edge argument: {edge}") - if gp_ax1 is not None: - if not isinstance(gp_ax1, gp_Ax1): - raise ValueError(f"Invalid Axis parameter {gp_ax1}") - self.wrapped: gp_Ax1 = gp_ax1 # type: ignore[annotation-unchecked] - else: + topods_edge: TopoDS_Edge = edge.wrapped # type: ignore[annotation-unchecked] + curve = BRep_Tool.Curve_s(topods_edge, float(), float()) + param_min, _ = BRep_Tool.Range_s(topods_edge) + origin_pnt = gp_Pnt() + tangent_vec = gp_Vec() + curve.D1(param_min, origin_pnt, tangent_vec) + origin = Vector(origin_pnt) + direction = Vector(gp_Dir(tangent_vec)) + + # Convert location to axis + if location is not None: + gp_ax1 = Axis.Z.located(location).wrapped + + # Construct self.wrapped from gp_ax1 or origin/direction + if gp_ax1 is None: try: origin_vector = Vector(origin) direction_vector = Vector(direction) - except TypeError as exc: + gp_ax1 = gp_Ax1( + origin_vector.to_pnt(), + gp_Dir(*tuple(direction_vector.normalized())), + ) + except Exception as exc: raise ValueError("Invalid Axis parameters") from exc + elif not isinstance(gp_ax1, gp_Ax1): + raise ValueError(f"Invalid Axis parameter: {gp_ax1}") - self.wrapped = gp_Ax1( - origin_vector.to_pnt(), - gp_Dir(*tuple(direction_vector.normalized())), - ) + self.wrapped: gp_Ax1 = gp_ax1 # type: ignore[annotation-unchecked] @property def position(self): + """The position or origin of the Axis""" return Vector(self.wrapped.Location()) @position.setter def position(self, position: VectorLike): + """Set the position or origin of the Axis""" self.wrapped.SetLocation(Vector(position).to_pnt()) @property def direction(self): + """The normalized direction of the Axis""" return Vector(self.wrapped.Direction()) @direction.setter def direction(self, direction: VectorLike): + """Set the direction of the Axis""" self.wrapped.SetDirection(Vector(direction).to_dir()) @property @@ -713,11 +734,13 @@ class Axis(metaclass=AxisMeta): def __repr__(self) -> str: """Display self""" - return f"({self.position.to_tuple()},{self.direction.to_tuple()})" + return f"({tuple(self.position)},{tuple(self.direction)})" def __str__(self) -> str: """Display self""" - return f"{type(self).__name__}: ({self.position.to_tuple()},{self.direction.to_tuple()})" + return ( + f"{type(self).__name__}: ({tuple(self.position)},{tuple(self.direction)})" + ) def __eq__(self, other: object) -> bool: if not isinstance(other, Axis): @@ -735,6 +758,12 @@ class Axis(metaclass=AxisMeta): def to_plane(self) -> Plane: """Return self as Plane""" + warnings.warn( + "to_tuple is deprecated and will be removed in a future version. " + "Use 'Plane(Axis)' instead.", + DeprecationWarning, + stacklevel=2, + ) return Plane(origin=self.position, z_dir=self.direction) def is_coaxial( @@ -891,6 +920,7 @@ class Axis(metaclass=AxisMeta): """Find intersection of plane and axis""" def intersect(self, *args, **kwargs): + """Find intersection of geometric object and axis""" axis, plane, vector, location, shape = _parse_intersect_args(*args, **kwargs) if axis is not None: @@ -909,7 +939,7 @@ class Axis(metaclass=AxisMeta): # Solve the system of equations to find the intersection system_of_equations = np.array([d1, -d2, np.cross(d1, d2)]).T origin_diff = p2 - p1 - t1, t2, _ = np.linalg.lstsq(system_of_equations, origin_diff, rcond=None)[0] + t1, _, _ = np.linalg.lstsq(system_of_equations, origin_diff, rcond=None)[0] # Calculate the intersection point intersection_point = p1 + t1 * d1 @@ -944,6 +974,8 @@ class Axis(metaclass=AxisMeta): if shape is not None: return shape.intersect(self) + return None + class BoundBox: """A BoundingBox for a Shape""" @@ -951,7 +983,7 @@ class BoundBox: def __init__(self, bounding_box: Bnd_Box) -> None: if bounding_box.IsVoid(): - x_min, y_min, z_min, x_max, y_max, z_max = (0,) * 6 + x_min, y_min, z_min, x_max, y_max, z_max = (0.0,) * 6 else: x_min, y_min, z_min, x_max, y_max, z_max = bounding_box.Get() self.wrapped = None if bounding_box.IsVoid() else bounding_box @@ -1010,7 +1042,7 @@ class BoundBox: if isinstance(obj, tuple): tmp.Update(*obj) elif isinstance(obj, Vector): - tmp.Update(*obj.to_tuple()) + tmp.Update(*obj) elif isinstance(obj, BoundBox) and obj.wrapped is not None: tmp.Add(obj.wrapped) @@ -1059,7 +1091,6 @@ class BoundBox: shape: TopoDS_Shape, tolerance: float | None = None, optimal: bool = True, - oriented: bool = False, ) -> BoundBox: """Constructs a bounding box from a TopoDS_Shape @@ -1075,22 +1106,13 @@ class BoundBox: tolerance = TOL if tolerance is None else tolerance # tol = TOL (by default) bbox = Bnd_Box() - bbox_obb = Bnd_OBB() if optimal: - # this is 'exact' but expensive - if oriented: - BRepBndLib.AddOBB_s(shape, bbox_obb, False, True, False) - else: - BRepBndLib.AddOptimal_s(shape, bbox) + BRepBndLib.AddOptimal_s(shape, bbox) else: - # this is adds +margin but is faster - if oriented: - BRepBndLib.AddOBB_s(shape, bbox_obb) - else: - BRepBndLib.Add_s(shape, bbox, True) + BRepBndLib.Add_s(shape, bbox, True) - return cls(bbox_obb) if oriented else cls(bbox) + return cls(bbox) def is_inside(self, second_box: BoundBox) -> bool: """Is the provided bounding box inside this one? @@ -1112,7 +1134,7 @@ class BoundBox: def to_align_offset(self, align: Align2DType | Align3DType) -> Vector: """Amount to move object to achieve the desired alignment""" - return to_align_offset(self.min.to_tuple(), self.max.to_tuple(), align) + return to_align_offset(self.min, self.max, align) class Color: @@ -1195,7 +1217,7 @@ class Color: if len(args) == 2: alpha = args[1] elif len(args) >= 3: - red, green, blue = args[0:3] + red, green, blue = args[0:3] # pylint: disable=unbalanced-tuple-unpacking if len(args) == 4: alpha = args[3] @@ -1246,14 +1268,18 @@ class Color: if self.iter_index > 3: raise StopIteration - else: - value = rgb_tuple[self.iter_index] - self.iter_index += 1 + value = rgb_tuple[self.iter_index] + self.iter_index += 1 return value - # @deprecated def to_tuple(self): """Value as tuple""" + warnings.warn( + "to_tuple is deprecated and will be removed in a future version. " + "Use 'tuple(Color)' instead.", + DeprecationWarning, + stacklevel=2, + ) return tuple(self) def __copy__(self) -> Color: @@ -1312,21 +1338,21 @@ class GeomEncoder(json.JSONEncoder): """ - def default(self, obj): + def default(self, o): """Return a JSON-serializable representation of a known geometry object.""" - if isinstance(obj, Axis): - return {"Axis": (tuple(obj.position), tuple(obj.direction))} - elif isinstance(obj, Color): - return {"Color": obj.to_tuple()} - if isinstance(obj, Location): - return {"Location": obj.to_tuple()} - elif isinstance(obj, Plane): - return {"Plane": (tuple(obj.origin), tuple(obj.x_dir), tuple(obj.z_dir))} - elif isinstance(obj, Vector): - return {"Vector": tuple(obj)} - else: - # Let the base class default method raise the TypeError - return super().default(obj) + if isinstance(o, Axis): + return {"Axis": (tuple(o.position), tuple(o.direction))} + if isinstance(o, Color): + return {"Color": tuple(o)} + if isinstance(o, Location): + tup = tuple(o) + return {f"Location": (tuple(tup[0]), tuple(tup[1]))} + if isinstance(o, Plane): + return {"Plane": (tuple(o.origin), tuple(o.x_dir), tuple(o.z_dir))} + if isinstance(o, Vector): + return {"Vector": tuple(o)} + # Let the base class default method raise the TypeError + return super().default(o) @staticmethod def geometry_hook(json_dict): @@ -1377,22 +1403,20 @@ class Location: } @overload - def __init__(self): # pragma: no cover + def __init__(self): """Empty location with not rotation or translation with respect to the original location.""" @overload - def __init__(self, location: Location): # pragma: no cover + def __init__(self, location: Location): """Location with another given location.""" @overload - def __init__(self, translation: VectorLike, angle: float = 0): # pragma: no cover + def __init__(self, translation: VectorLike, angle: float = 0): """Location with translation with respect to the original location. If angle != 0 then the location includes a rotation around z-axis by angle""" @overload - def __init__( - self, translation: VectorLike, rotation: RotationLike | None = None - ): # pragma: no cover + def __init__(self, translation: VectorLike, rotation: RotationLike | None = None): """Location with translation with respect to the original location. If rotation is not None then the location includes the rotation (see also Rotation class) """ @@ -1403,122 +1427,123 @@ class Location: translation: VectorLike, rotation: RotationLike, ordering: Extrinsic | Intrinsic, - ): # pragma: no cover + ): """Location with translation with respect to the original location. If rotation is not None then the location includes the rotation (see also Rotation class) ordering defaults to Intrinsic.XYZ, but can also be set to Extrinsic """ @overload - def __init__(self, plane: Plane): # pragma: no cover + def __init__(self, plane: Plane): """Location corresponding to the location of the Plane.""" @overload - def __init__(self, plane: Plane, plane_offset: VectorLike): # pragma: no cover + def __init__(self, plane: Plane, plane_offset: VectorLike): """Location corresponding to the angular location of the Plane with translation plane_offset.""" @overload - def __init__(self, top_loc: TopLoc_Location): # pragma: no cover + def __init__(self, top_loc: TopLoc_Location): """Location wrapping the low-level TopLoc_Location object t""" @overload - def __init__(self, gp_trsf: gp_Trsf): # pragma: no cover + def __init__(self, gp_trsf: gp_Trsf): """Location wrapping the low-level gp_Trsf object t""" @overload - def __init__( - self, translation: VectorLike, direction: VectorLike, angle: float - ): # pragma: no cover + def __init__(self, translation: VectorLike, direction: VectorLike, angle: float): """Location with translation t and rotation around direction by angle with respect to the original location.""" - def __init__(self, *args): - # pylint: disable=too-many-branches - transform = gp_Trsf() + def __init__( + self, *args, **kwargs + ): # pylint: disable=too-many-branches, too-many-locals, too-many-statements - if len(args) == 0: - pass + self.location_index = 0 - elif len(args) == 1: - translation = args[0] + position = kwargs.pop("position", None) + orientation = kwargs.pop("orientation", None) + ordering = kwargs.pop("ordering", None) + angle = kwargs.pop("angle", None) + plane = kwargs.pop("plane", None) + location = kwargs.pop("location", None) + top_loc = kwargs.pop("top_loc", None) + gp_trsf = kwargs.pop("gp_trsf", None) - if isinstance(translation, (Vector, Iterable)): - transform.SetTranslationPart(Vector(translation).wrapped) - elif isinstance(translation, Plane): - coordinate_system = gp_Ax3( - translation._origin.to_pnt(), - translation.z_dir.to_dir(), - translation.x_dir.to_dir(), - ) - transform.SetTransformation(coordinate_system) - transform.Invert() - elif isinstance(args[0], Location): - self.wrapped = translation.wrapped - return - elif isinstance(translation, TopLoc_Location): - self.wrapped = translation - return - elif isinstance(translation, gp_Trsf): - transform = translation + # If any unexpected kwargs remain + if kwargs: + raise TypeError(f"Unexpected keyword arguments: {', '.join(kwargs)}") + + # Fill from positional args if not given via kwargs + if args: + if plane is None and isinstance(args[0], Plane): + plane = args[0] + elif location is None and isinstance(args[0], (Location, Rotation)): + location = args[0] + elif top_loc is None and isinstance(args[0], TopLoc_Location): + top_loc = args[0] + elif gp_trsf is None and isinstance(args[0], gp_Trsf): + gp_trsf = args[0] + elif isinstance(args[0], (Vector, Iterable)): + position = Vector(args[0]) + if len(args) > 1: + if isinstance(args[1], (Vector, Iterable)): + orientation = Vector(args[1]) + elif isinstance(args[1], (int, float)): + angle = args[1] + if len(args) > 2: + if isinstance(args[2], (int, float)) and orientation is not None: + angle = args[2] + elif isinstance(args[2], (Intrinsic, Extrinsic)): + ordering = args[2] + else: + raise TypeError( + f"Third parameter must be a float or order not {args[2]}" + ) else: - raise TypeError("Unexpected parameters") + raise TypeError(f"Invalid positional arguments: {args}") - elif len(args) == 2: - ordering = Intrinsic.XYZ - if isinstance(args[0], (Vector, Iterable)): - if isinstance(args[1], (Vector, Iterable)): - rotation = [radians(a) for a in args[1]] - quaternion = gp_Quaternion() - quaternion.SetEulerAngles(self._rot_order_dict[ordering], *rotation) - transform.SetRotation(quaternion) - elif isinstance(args[0], (Vector, tuple)) and isinstance( - args[1], (int, float) - ): - angle = radians(args[1]) - quaternion = gp_Quaternion() - quaternion.SetEulerAngles( - self._rot_order_dict[ordering], 0, 0, angle - ) - transform.SetRotation(quaternion) + # Construct transformation + trsf = gp_Trsf() - # set translation part after setting rotation (if exists) - transform.SetTranslationPart(Vector(args[0]).wrapped) - else: - translation, origin = args - coordinate_system = gp_Ax3( - Vector(origin).to_pnt(), - translation.z_dir.to_dir(), - translation.x_dir.to_dir(), - ) - transform.SetTransformation(coordinate_system) - transform.Invert() - elif len(args) == 3: - if ( - isinstance(args[0], (Vector, Iterable)) - and isinstance(args[1], (Vector, Iterable)) - and isinstance(args[2], (int, float)) - ): - translation, axis, angle = args - transform.SetRotation( - gp_Ax1(Vector().to_pnt(), Vector(axis).to_dir()), angle * pi / 180.0 - ) - elif ( - isinstance(args[0], (Vector, Iterable)) - and isinstance(args[1], (Vector, Iterable)) - and isinstance(args[2], (Extrinsic, Intrinsic)) - ): - translation = args[0] - rotation = [radians(a) for a in args[1]] - ordering = args[2] - quaternion = gp_Quaternion() - quaternion.SetEulerAngles(self._rot_order_dict[ordering], *rotation) - transform.SetRotation(quaternion) - else: - raise TypeError("Unsupported argument types for Location") + if plane: + cs = gp_Ax3( + plane.origin.to_pnt(), + plane.z_dir.to_dir(), + plane.x_dir.to_dir(), + ) + trsf.SetTransformation(cs) + trsf.Invert() - transform.SetTranslationPart(Vector(translation).wrapped) - self.wrapped = TopLoc_Location(transform) + elif gp_trsf: + trsf = gp_trsf + + elif angle is not None: + axis = gp_Ax1( + gp_Pnt(0, 0, 0), + Vector(orientation).to_dir() if orientation else gp_Dir(0, 0, 1), + ) + trsf.SetRotation(axis, radians(angle)) + + elif orientation is not None: + angles = [radians(a) for a in orientation] + rot_order = self._rot_order_dict.get( + ordering, gp_EulerSequence.gp_Intrinsic_XYZ + ) + quat = gp_Quaternion() + quat.SetEulerAngles(rot_order, *angles) + trsf.SetRotation(quat) + + if position: + trsf.SetTranslationPart(Vector(position).wrapped) + + # Final assignment based on input + if location is not None: + self.wrapped = location.wrapped + elif top_loc is not None: + self.wrapped = top_loc + else: + self.wrapped = TopLoc_Location(trsf) @property def position(self) -> Vector: @@ -1528,7 +1553,7 @@ class Location: Vector: Position part of Location """ - return Vector(self.to_tuple()[0]) + return Vector(tuple(self)[0]) @position.setter def position(self, value: VectorLike): @@ -1553,7 +1578,7 @@ class Location: Vector: orientation part of Location """ - return Vector(self.to_tuple()[1]) + return Vector(tuple(self)[1]) @orientation.setter def orientation(self, rotation: VectorLike): @@ -1626,7 +1651,9 @@ class Location: # other is a Shape if hasattr(other, "wrapped") and isinstance(other.wrapped, TopoDS_Shape): # result = other.moved(self) - downcast_LUT = { + downcast_lut: dict[ + TopAbs_ShapeEnum, Callable[[TopoDS_Shape], TopoDS_Shape] + ] = { TopAbs_ShapeEnum.TopAbs_VERTEX: TopoDS.Vertex_s, TopAbs_ShapeEnum.TopAbs_EDGE: TopoDS.Edge_s, TopAbs_ShapeEnum.TopAbs_WIRE: TopoDS.Wire_s, @@ -1637,7 +1664,7 @@ class Location: } assert other.wrapped is not None try: - f_downcast = downcast_LUT[other.wrapped.ShapeType()] + f_downcast = downcast_lut[other.wrapped.ShapeType()] except KeyError as exc: raise ValueError(f"Unknown object type {other}") from exc @@ -1703,6 +1730,30 @@ class Location: ) ) + def __iter__(self): + """Initialize to beginning""" + self.location_index = 0 + return self + + def __next__(self) -> Vector: + """return the next value""" + transformation = self.wrapped.Transformation() + trans = transformation.TranslationPart() + rot = transformation.GetRotation() + rv_trans: Vector = Vector(trans.X(), trans.Y(), trans.Z()) + rv_rot: Vector = Vector( + *[degrees(a) for a in rot.GetEulerAngles(gp_EulerSequence.gp_Intrinsic_XYZ)] + ) # type: ignore[assignment] + if self.location_index == 0: + self.location_index += 1 + value = rv_trans + elif self.location_index == 1: + self.location_index += 1 + value = rv_rot + else: + raise StopIteration + return value + def __neg__(self) -> Location: """Flip the orientation without changing the position operator -""" return Location(-Plane(self)) @@ -1729,8 +1780,8 @@ class Location: However, `build123d` requires all coordinate systems to be right-handed. Therefore, this implementation: - - Reflects the X and Z directions across the mirror plane - - Recomputes the Y direction as: `Y = X × Z` + - Reflects the X and Z directions across the mirror plane + - Recomputes the Y direction as: `Y = X × Z` This ensures the resulting Location maintains a valid right-handed frame, while remaining as close as possible to the geometric mirror. @@ -1759,11 +1810,24 @@ class Location: def to_axis(self) -> Axis: """Convert the location into an Axis""" + warnings.warn( + "to_axis is deprecated and will be removed in a future version. " + "Use 'Axis(Location)' instead.", + DeprecationWarning, + stacklevel=2, + ) return Axis.Z.located(self) def to_tuple(self) -> tuple[tuple[float, float, float], tuple[float, float, float]]: """Convert the location to a translation, rotation tuple.""" + warnings.warn( + "to_tuple is deprecated and will be removed in a future version. " + "Use 'tuple(Location)' instead.", + DeprecationWarning, + stacklevel=2, + ) + transformation = self.wrapped.Transformation() trans = transformation.TranslationPart() rot = transformation.GetRotation() @@ -1783,8 +1847,8 @@ class Location: Returns: Location as String """ - position_str = ", ".join(f"{v:.2f}" for v in self.to_tuple()[0]) - orientation_str = ", ".join(f"{v:.2f}" for v in self.to_tuple()[1]) + position_str = ", ".join(f"{v:.2f}" for v in tuple(self)[0]) + orientation_str = ", ".join(f"{v:.2f}" for v in tuple(self)[1]) return f"(p=({position_str}), o=({orientation_str}))" def __str__(self): @@ -1795,8 +1859,8 @@ class Location: Returns: Location as String """ - position_str = ", ".join(f"{v:.2f}" for v in self.to_tuple()[0]) - orientation_str = ", ".join(f"{v:.2f}" for v in self.to_tuple()[1]) + position_str = ", ".join(f"{v:.2f}" for v in tuple(self)[0]) + orientation_str = ", ".join(f"{v:.2f}" for v in tuple(self)[1]) return f"Location: (position=({position_str}), orientation=({orientation_str}))" @overload @@ -1816,6 +1880,7 @@ class Location: """Find intersection of plane and location""" def intersect(self, *args, **kwargs): + """Find intersection of geometric object and location""" axis, plane, vector, location, shape = _parse_intersect_args(*args, **kwargs) if axis is not None: @@ -1833,6 +1898,8 @@ class Location: if shape is not None: return shape.intersect(self) + return None + class LocationEncoder(json.JSONEncoder): """Custom JSON Encoder for Location values @@ -1902,10 +1969,12 @@ class OrientedBoundBox: """ if isinstance(shape, Bnd_OBB): obb = shape - else: + elif hasattr(shape, "wrapped") and isinstance(shape.wrapped, TopoDS_Shape): obb = Bnd_OBB() # Compute the oriented bounding box for the shape. BRepBndLib.AddOBB_s(shape.wrapped, obb, True) + else: + raise TypeError(f"Expected Bnd_OBB or Shape, got {type(shape).__name__}") self.wrapped = obb @property @@ -1933,9 +2002,7 @@ class OrientedBoundBox: (False, True, False): [(1, 1, 1), (1, 1, -1), (-1, 1, -1), (-1, 1, 1)], (False, False, True): [(1, 1, 1), (1, -1, 1), (-1, -1, 1), (-1, 1, 1)], # 3D object case - (False, False, False): [ - (x, y, z) for x, y, z in itertools.product((-1, 1), (-1, 1), (-1, 1)) - ], + (False, False, False): list(itertools.product((-1, 1), (-1, 1), (-1, 1))), } hs = self.size * 0.5 order = orders[(hs.X < TOLERANCE, hs.Y < TOLERANCE, hs.Z < TOLERANCE)] @@ -2131,7 +2198,7 @@ class Rotation(Location): if tuples: angles = list(*tuples) if vectors: - angles = vectors[0].to_tuple() + angles = tuple(vectors[0]) if len(angles) < 3: angles.extend([0.0] * (3 - len(angles))) rotations = list(filter(lambda item: isinstance(item, Rotation), args)) @@ -2497,85 +2564,89 @@ class Plane(metaclass=PlaneMeta): return Vector(normal) @overload - def __init__(self, gp_pln: gp_Pln): # pragma: no cover + def __init__(self, gp_pln: gp_Pln): """Return a plane from a OCCT gp_pln""" - @overload - def __init__(self, face: Face, x_dir: VectorLike | None = None): # pragma: no cover - """Return a plane extending the face. - Note: for non planar face this will return the underlying work plane""" - - @overload - def __init__(self, location: Location): # pragma: no cover - """Return a plane aligned with a given location""" - @overload def __init__( self, origin: VectorLike, x_dir: VectorLike | None = None, z_dir: VectorLike = (0, 0, 1), - ): # pragma: no cover + ): """Return a new plane at origin with x_dir and z_dir""" + @overload + def __init__(self, face: Face, x_dir: VectorLike | None = None): + """Return a plane extending the face. + Note: for non planar face this will return the underlying work plane""" + + @overload + def __init__(self, location: Location): + """Return a plane aligned with a given location""" + + @overload + def __init__(self, axis: Axis, x_dir: VectorLike | None = None): + """Return a plane with the z_dir aligned with the axis and optional x_dir direction""" + def __init__(self, *args, **kwargs): # pylint: disable=too-many-locals,too-many-branches,too-many-statements - """Create a plane from either an OCCT gp_pln or coordinates""" + """Create a plane from either an OCCT gp_pln, Face, Location, or coordinates""" - def optarg(kwargs, name, args, index, default): - if name in kwargs: - return kwargs[name] - if len(args) > index: - return args[index] - return default - - arg_plane = None - arg_face = None - arg_location = None - arg_origin = None - arg_x_dir = None - arg_z_dir = (0, 0, 1) - - arg0 = args[0] if args else None type_error_message = "Expected gp_Pln, Face, Location, or VectorLike" - if "gp_pln" in kwargs: - arg_plane = kwargs["gp_pln"] - elif isinstance(arg0, gp_Pln): - arg_plane = arg0 - elif "face" in kwargs: - arg_face = kwargs["face"] - arg_x_dir = kwargs.get("x_dir", None) - # Check for Face by using the OCCT class to avoid circular imports of the Face class - elif hasattr(arg0, "wrapped") and isinstance(arg0.wrapped, TopoDS_Face): - arg_face = arg0 - arg_x_dir = optarg(kwargs, "x_dir", args, 1, arg_x_dir) - elif "location" in kwargs: - arg_location = kwargs["location"] - elif isinstance(arg0, Location): - arg_location = arg0 - elif "origin" in kwargs: - arg_origin = kwargs["origin"] - arg_x_dir = kwargs.get("x_dir", arg_x_dir) - arg_z_dir = kwargs.get("z_dir", arg_z_dir) - else: - try: - arg_origin = Vector(arg0) - except TypeError as exc: - raise TypeError(type_error_message) from exc - arg_x_dir = optarg(kwargs, "x_dir", args, 1, arg_x_dir) - arg_z_dir = optarg(kwargs, "z_dir", args, 2, arg_z_dir) + arg_plane = kwargs.pop("gp_pln", None) + arg_face = kwargs.pop("face", None) + arg_location = kwargs.pop("location", None) + arg_axis = kwargs.pop("axis", None) + arg_origin = kwargs.pop("origin", None) + arg_x_dir = kwargs.pop("x_dir", None) + arg_z_dir = kwargs.pop("z_dir", (0, 0, 1)) + + if kwargs: + raise TypeError(f"Unexpected keyword arguments: {', '.join(kwargs)}") + + if args: + arg0 = args[0] + if arg_plane is None and isinstance(arg0, gp_Pln): + arg_plane = arg0 + elif ( + arg_face is None + and hasattr(arg0, "wrapped") + and isinstance(arg0.wrapped, TopoDS_Face) + ): + arg_face = arg0 + if arg_x_dir is None and len(args) > 1: + arg_x_dir = args[1] + elif arg_location is None and isinstance(arg0, Location): + arg_location = arg0 + elif arg_axis is None and isinstance(arg0, Axis): + arg_axis = arg0 + if len(args) > 1: + try: + arg_x_dir = Vector(args[1]) + except Exception as exc: + raise TypeError(type_error_message) from exc + elif arg_origin is None: + try: + arg_origin = Vector(arg0) + if arg_x_dir is None and len(args) > 1: + arg_x_dir = Vector(args[1]).normalized() + if len(args) > 2: + arg_z_dir = Vector(args[2]).normalized() + except Exception as exc: + raise TypeError(type_error_message) from exc if arg_plane: self.wrapped = arg_plane elif arg_face: - # Determine if face is planar surface = BRep_Tool.Surface_s(arg_face.wrapped) if not arg_face.is_planar: raise ValueError("Planes can only be created from planar faces") properties = GProp_GProps() BRepGProp.SurfaceProperties_s(arg_face.wrapped, properties) self._origin = Vector(properties.CentreOfMass()) + if isinstance(surface, Geom_BoundedSurface): point = gp_Pnt() face_x_dir = gp_Vec() @@ -2583,6 +2654,7 @@ class Plane(metaclass=PlaneMeta): surface.D1(0.5, 0.5, point, face_x_dir, tangent_v) else: face_x_dir = surface.Position().XDirection() + self.x_dir = Vector(arg_x_dir) if arg_x_dir else Vector(face_x_dir) self.x_dir = Vector(round(i, 14) for i in self.x_dir) self.z_dir = Plane.get_topods_face_normal(arg_face.wrapped) @@ -2597,10 +2669,16 @@ class Plane(metaclass=PlaneMeta): self.x_dir = Vector(round(i, 14) for i in self.x_dir) self.z_dir = Plane.get_topods_face_normal(topo_face) self.z_dir = Vector(round(i, 14) for i in self.z_dir) - elif arg_origin: + elif arg_axis: + self._origin = arg_axis.position + self.x_dir = Vector(arg_x_dir) if arg_x_dir is not None else None + self.z_dir = arg_axis.direction + elif arg_origin is not None: self._origin = Vector(arg_origin) self.x_dir = Vector(arg_x_dir) if arg_x_dir else None self.z_dir = Vector(arg_z_dir) + else: + raise TypeError(type_error_message) if hasattr(self, "wrapped"): self._origin = Vector(self.wrapped.Location()) @@ -2612,17 +2690,19 @@ class Plane(metaclass=PlaneMeta): raise ValueError("z_dir must be non null") self.z_dir = self.z_dir.normalized() - if not self.x_dir: + if self.x_dir is None: ax3 = gp_Ax3(self._origin.to_pnt(), self.z_dir.to_dir()) self.x_dir = Vector(ax3.XDirection()).normalized() else: if Vector(self.x_dir).length == 0.0: raise ValueError("x_dir must be non null") self.x_dir = Vector(self.x_dir).normalized() + self.y_dir = self.z_dir.cross(self.x_dir).normalized() self.wrapped = gp_Pln( gp_Ax3(self._origin.to_pnt(), self.z_dir.to_dir(), self.x_dir.to_dir()) ) + self.local_coord_system = None #: gp_Ax3 | None self.reverse_transform = None #: Matrix | None self.forward_transform = None #: Matrix | None @@ -2703,9 +2783,9 @@ class Plane(metaclass=PlaneMeta): Returns: Plane as String """ - origin_str = ", ".join(f"{v:.2f}" for v in self._origin.to_tuple()) - x_dir_str = ", ".join(f"{v:.2f}" for v in self.x_dir.to_tuple()) - z_dir_str = ", ".join(f"{v:.2f}" for v in self.z_dir.to_tuple()) + origin_str = ", ".join(f"{v:.2f}" for v in tuple(self._origin)) + x_dir_str = ", ".join(f"{v:.2f}" for v in tuple(self.x_dir)) + z_dir_str = ", ".join(f"{v:.2f}" for v in tuple(self.z_dir)) return f"Plane(o=({origin_str}), x=({x_dir_str}), z=({z_dir_str}))" def reverse(self) -> Plane: @@ -2832,9 +2912,9 @@ class Plane(metaclass=PlaneMeta): global_coord_system = gp_Ax3() local_coord_system = gp_Ax3( - gp_Pnt(*self._origin.to_tuple()), - gp_Dir(*self.z_dir.to_tuple()), - gp_Dir(*self.x_dir.to_tuple()), + gp_Pnt(*self._origin), + gp_Dir(*self.z_dir), + gp_Dir(*self.x_dir), ) forward_t.SetTransformation(global_coord_system, local_coord_system) @@ -2888,15 +2968,17 @@ class Plane(metaclass=PlaneMeta): local_bottom_left = global_bottom_left.transform(transform_matrix) local_top_right = global_top_right.transform(transform_matrix) local_bbox = Bnd_Box( - gp_Pnt(*local_bottom_left.to_tuple()), - gp_Pnt(*local_top_right.to_tuple()), + gp_Pnt(*local_bottom_left), + gp_Pnt(*local_top_right), ) return BoundBox(local_bbox) if hasattr(obj, "wrapped") and obj.wrapped is None: # Empty shape raise ValueError("Cant's reposition empty object") if hasattr(obj, "wrapped") and isinstance(obj.wrapped, TopoDS_Shape): # Shapes # return_value = obj.transform_shape(transform_matrix) - downcast_LUT = { + downcast_lut: dict[ + TopAbs_ShapeEnum, Callable[[TopoDS_Shape], TopoDS_Shape] + ] = { TopAbs_ShapeEnum.TopAbs_VERTEX: TopoDS.Vertex_s, TopAbs_ShapeEnum.TopAbs_EDGE: TopoDS.Edge_s, TopAbs_ShapeEnum.TopAbs_WIRE: TopoDS.Wire_s, @@ -2907,7 +2989,7 @@ class Plane(metaclass=PlaneMeta): } assert obj.wrapped is not None try: - f_downcast = downcast_LUT[obj.wrapped.ShapeType()] + f_downcast = downcast_lut[obj.wrapped.ShapeType()] except KeyError as exc: raise ValueError(f"Unknown object type {obj}") from exc @@ -3001,6 +3083,7 @@ class Plane(metaclass=PlaneMeta): """Find intersection of plane and shape""" def intersect(self, *args, **kwargs): + """Find intersection of geometric object and shape""" axis, plane, vector, location, shape = _parse_intersect_args(*args, **kwargs) @@ -3046,6 +3129,8 @@ class Plane(metaclass=PlaneMeta): if shape is not None: return shape.intersect(self) + return None + CLASS_REGISTRY = { "Axis": Axis, diff --git a/src/build123d/objects_curve.py b/src/build123d/objects_curve.py index 0eaa4b3..a883ac9 100644 --- a/src/build123d/objects_curve.py +++ b/src/build123d/objects_curve.py @@ -32,7 +32,7 @@ import copy as copy_module from collections.abc import Iterable from math import copysign, cos, radians, sin, sqrt from scipy.optimize import minimize -import sympy # type: ignore +import sympy # type: ignore from build123d.build_common import WorkplaneList, flatten_sequence, validate_inputs from build123d.build_enums import ( @@ -456,7 +456,7 @@ class Helix(BaseEdgeObject): defined by cone_angle. If cone_angle is not 0, radius is the initial helix radius at center. cone_angle > 0 - increases the final radius. cone_angle < 0 decreases the final radius. + increases the final radius. cone_angle < 0 decreases the final radius. Args: pitch (float): distance between loops @@ -564,7 +564,7 @@ class FilletPolyline(BaseLineObject): if len(edges) != 2: continue other_vertices = {ve for e in edges for ve in e.vertices() if ve != vertex} - third_edge = Edge.make_line(*[v.to_tuple() for v in other_vertices]) + third_edge = Edge.make_line(*[v for v in other_vertices]) fillet_face = Face(Wire(edges + [third_edge])).fillet_2d(radius, [vertex]) fillets.append(fillet_face.edges().filter_by(GeomType.CIRCLE)[0]) @@ -1095,9 +1095,7 @@ class PointArcTangentLine(BaseEdgeObject): tangent_point = WorkplaneList.localize(point) if context is None: # Making the plane validates points and arc are coplanar - coplane = Edge.make_line(tangent_point, arc.arc_center).common_plane( - arc - ) + coplane = Edge.make_line(tangent_point, arc.arc_center).common_plane(arc) if coplane is None: raise ValueError("PointArcTangentLine only works on a single plane.") @@ -1478,4 +1476,10 @@ class ArcArcTangentArc(BaseEdgeObject): intersect.reverse() arc = RadiusArc(intersect[0], intersect[1], radius=radius) + + # Check and flip arc if not tangent + _, _, point = start_arc.distance_to_with_closest_points(arc) + if start_arc.tangent_at(point).cross(arc.tangent_at(point)).length > TOLERANCE: + arc = RadiusArc(intersect[0], intersect[1], radius=-radius) + super().__init__(arc, mode) diff --git a/src/build123d/objects_sketch.py b/src/build123d/objects_sketch.py index f603147..eed5b59 100644 --- a/src/build123d/objects_sketch.py +++ b/src/build123d/objects_sketch.py @@ -53,6 +53,7 @@ from build123d.topology import ( Face, ShapeList, Sketch, + Vertex, Wire, tuplify, topo_explore_common_vertex, @@ -205,7 +206,7 @@ class Polygon(BaseSketchObject): self.pts = flattened_pts self.align = tuplify(align, 2) - poly_pts = [Vector(p) for p in pts] + poly_pts = [Vector(p) for p in self.pts] face = Face(Wire.make_polygon(poly_pts)) super().__init__(face, rotation, self.align, mode) @@ -386,7 +387,7 @@ class SlotArc(BaseSketchObject): self.slot_height = height arc = arc if isinstance(arc, Wire) else Wire([arc]) - face = Face(arc.offset_2d(height / 2)).rotate(Axis.Z, rotation) + face = Face(arc.offset_2d(height / 2)) super().__init__(face, rotation, None, mode) @@ -425,10 +426,10 @@ class SlotCenterPoint(BaseSketchObject): half_line = point_v - center_v - if half_line.length * 2 <= height: + if half_line.length <= 0: raise ValueError( - f"Slots must have width > height. " - "Got: {height=} width={half_line.length * 2} (computed)" + "Distance between center and point must be greater than 0 " + f"Got: distance = {half_line.length} (computed)" ) face = Face( @@ -463,7 +464,7 @@ class SlotCenterToCenter(BaseSketchObject): rotation: float = 0, mode: Mode = Mode.ADD, ): - if center_separation <= 0: + if center_separation < 0: raise ValueError( f"Requires center_separation > 0. Got: {center_separation=}" ) @@ -474,14 +475,18 @@ class SlotCenterToCenter(BaseSketchObject): self.center_separation = center_separation self.slot_height = height - face = Face( - Wire( - [ - Edge.make_line(Vector(-center_separation / 2, 0, 0), Vector()), - Edge.make_line(Vector(), Vector(+center_separation / 2, 0, 0)), - ] - ).offset_2d(height / 2) - ) + if center_separation > 0: + face = Face( + Wire( + [ + Edge.make_line(Vector(-center_separation / 2, 0, 0), Vector()), + Edge.make_line(Vector(), Vector(+center_separation / 2, 0, 0)), + ] + ).offset_2d(height / 2) + ) + else: + face = cast(Face, Circle(height / 2, mode=mode).face()) + super().__init__(face, rotation, None, mode) @@ -509,7 +514,7 @@ class SlotOverall(BaseSketchObject): align: Align | tuple[Align, Align] | None = (Align.CENTER, Align.CENTER), mode: Mode = Mode.ADD, ): - if width <= height: + if width < height: raise ValueError( f"Slot requires that width > height. Got: {width=}, {height=}" ) @@ -520,7 +525,7 @@ class SlotOverall(BaseSketchObject): self.width = width self.slot_height = height - if width != height: + if width > height: face = Face( Wire( [ @@ -531,6 +536,7 @@ class SlotOverall(BaseSketchObject): ) else: face = cast(Face, Circle(width / 2, mode=mode).face()) + super().__init__(face, rotation, align, mode) @@ -544,16 +550,16 @@ class Text(BaseSketchObject): "Arial Black". Alternatively, a specific font file can be specified with font_path. Note: Windows 10+ users must "Install for all users" for fonts to be found by name. - - Not all fonts have every FontStyle available, however ITALIC and BOLDITALIC will - still italicize the font if the respective font file is not available. - text_align specifies alignment of text inside the bounding box, while align the + Not all fonts have every FontStyle available, however ITALIC and BOLDITALIC will + still italicize the font if the respective font file is not available. + + text_align specifies alignment of text inside the bounding box, while align the aligns the bounding box itself. - Optionally, the Text can be positioned on a non-linear edge or wire with a path and + Optionally, the Text can be positioned on a non-linear edge or wire with a path and position_on_path. - + Args: txt (str): text to render font_size (float): size of the font in model units @@ -564,10 +570,10 @@ class Text(BaseSketchObject): text_align (tuple[TextAlign, TextAlign], optional): horizontal text align LEFT, CENTER, or RIGHT. Vertical text align BOTTOM, CENTER, TOP, or TOPFIRSTLINE. Defaults to (TextAlign.CENTER, TextAlign.CENTER) - align (Align | tuple[Align, Align], optional): align MIN, CENTER, or MAX of + align (Align | tuple[Align, Align], optional): align MIN, CENTER, or MAX of object. Defaults to None path (Edge | Wire, optional): path for text to follow. Defaults to None - position_on_path (float, optional): the relative location on path to position + position_on_path (float, optional): the relative location on path to position the text, values must be between 0.0 and 1.0. Defaults to 0.0 rotation (float, optional): angle to rotate object. Defaults to 0 mode (Mode, optional): combination mode. Defaults to Mode.ADD @@ -782,9 +788,15 @@ class Triangle(BaseSketchObject): self.vertex_A = topo_explore_common_vertex( self.edge_b, self.edge_c ) #: vertex 'A' + assert isinstance(self.vertex_A, Vertex) + self.vertex_A.topo_parent = self self.vertex_B = topo_explore_common_vertex( self.edge_a, self.edge_c ) #: vertex 'B' + assert isinstance(self.vertex_B, Vertex) + self.vertex_B.topo_parent = self self.vertex_C = topo_explore_common_vertex( self.edge_a, self.edge_b ) #: vertex 'C' + assert isinstance(self.vertex_C, Vertex) + self.vertex_C.topo_parent = self diff --git a/src/build123d/operations_generic.py b/src/build123d/operations_generic.py index c2b2e50..69d75cc 100644 --- a/src/build123d/operations_generic.py +++ b/src/build123d/operations_generic.py @@ -119,11 +119,11 @@ def add( ( obj.unwrap(fully=False) if isinstance(obj, Compound) - else obj._obj if isinstance(obj, Builder) else obj + else obj._obj if isinstance(obj, Builder) and obj._obj is not None else obj ) for obj in object_list + if not (isinstance(obj, Builder) and obj._obj is None) ] - validate_inputs(context, "add", object_iter) if isinstance(context, BuildPart): @@ -364,11 +364,14 @@ def chamfer( return new_sketch if target._dim == 1: - target = ( - Wire(target.wrapped) - if isinstance(target, BaseLineObject) - else target.wires()[0] - ) + if isinstance(target, BaseLineObject): + if target.wrapped is None: + target = Wire([]) # empty wire + else: + target = Wire(target.wrapped) + else: + target = target.wires()[0] + if not all([isinstance(obj, Vertex) for obj in object_list]): raise ValueError("1D fillet operation takes only Vertices") # Remove any end vertices as these can't be filleted @@ -376,14 +379,8 @@ def chamfer( object_list = ShapeList( filter( lambda v: not ( - isclose_b( - (Vector(*v.to_tuple()) - target.position_at(0)).length, - 0.0, - ) - or isclose_b( - (Vector(*v.to_tuple()) - target.position_at(1)).length, - 0.0, - ) + isclose_b((Vector(v) - target.position_at(0)).length, 0.0) + or isclose_b((Vector(v) - target.position_at(1)).length, 0.0) ), object_list, ) @@ -467,11 +464,14 @@ def fillet( return new_sketch if target._dim == 1: - target = ( - Wire(target.wrapped) - if isinstance(target, BaseLineObject) - else target.wires()[0] - ) + if isinstance(target, BaseLineObject): + if target.wrapped is None: + target = Wire([]) # empty wire + else: + target = Wire(target.wrapped) + else: + target = target.wires()[0] + if not all([isinstance(obj, Vertex) for obj in object_list]): raise ValueError("1D fillet operation takes only Vertices") # Remove any end vertices as these can't be filleted @@ -479,14 +479,8 @@ def fillet( object_list = ShapeList( filter( lambda v: not ( - isclose_b( - (Vector(*v.to_tuple()) - target.position_at(0)).length, - 0.0, - ) - or isclose_b( - (Vector(*v.to_tuple()) - target.position_at(1)).length, - 0.0, - ) + isclose_b((Vector(v) - target.position_at(0)).length, 0.0) + or isclose_b((Vector(v) - target.position_at(1)).length, 0.0) ), object_list, ) @@ -758,9 +752,7 @@ def project( # The size of the object determines the size of the target projection screen # as the screen is normal to the direction of parallel projection - shape_list = [ - Vertex(*o.to_tuple()) if isinstance(o, Vector) else o for o in object_list - ] + shape_list = [Vertex(o) if isinstance(o, Vector) else o for o in object_list] object_size = Compound(children=shape_list).bounding_box(optimal=False).diagonal vct_vrt_list = [o for o in object_list if isinstance(o, (Vector, Vertex))] diff --git a/src/build123d/operations_part.py b/src/build123d/operations_part.py index c1cd10b..7a196f7 100644 --- a/src/build123d/operations_part.py +++ b/src/build123d/operations_part.py @@ -30,12 +30,13 @@ from __future__ import annotations from typing import cast from collections.abc import Iterable -from build123d.build_enums import Mode, Until, Kind, Side +from build123d.build_enums import GeomType, Mode, Until, Kind, Side from build123d.build_part import BuildPart from build123d.geometry import Axis, Plane, Vector, VectorLike from build123d.topology import ( Compound, Curve, + DraftAngleError, Edge, Face, Shell, @@ -55,6 +56,59 @@ from build123d.build_common import ( ) +def draft( + faces: Face | Iterable[Face], + neutral_plane: Plane, + angle: float, +) -> Part: + """Part Operation: draft + + Apply a draft angle to the given faces of the part + + Args: + faces: Faces to which the draft should be applied. + neutral_plane: Plane defining the neutral direction and position. + angle: Draft angle in degrees. + """ + context: BuildPart | None = BuildPart._get_context("draft") + + face_list: ShapeList[Face] = flatten_sequence(faces) + assert all(isinstance(f, Face) for f in face_list), "all faces must be of type Face" + validate_inputs(context, "draft", face_list) + + valid_geom_types = {GeomType.PLANE, GeomType.CYLINDER, GeomType.CONE} + unsupported = [f for f in face_list if f.geom_type not in valid_geom_types] + if unsupported: + raise ValueError( + f"Draft not supported on face(s) with geometry: " + f"{', '.join(set(f.geom_type.name for f in unsupported))}" + ) + + # Check that all the faces are associated with the same Solid + topo_parents = set(f.topo_parent for f in face_list if f.topo_parent is not None) + if len(topo_parents) != 1: + raise ValueError("All faces must share the same topological parent (a Solid)") + parent_solids = next(iter(topo_parents)).solids() + if len(parent_solids) != 1: + raise ValueError("Topological parent must be a single Solid") + + # Create the drafted solid + try: + new_solid = parent_solids[0].draft(face_list, neutral_plane, angle) + except DraftAngleError as err: + raise DraftAngleError( + f"Draft operation failed. " + f"Use `err.face` and `err.problematic_shape` for more information.", + face=err.face, + problematic_shape=err.problematic_shape, + ) from err + + if context is not None: + context._add_to_context(new_solid, clean=False, mode=Mode.REPLACE) + + return Part(Compound([new_solid]).wrapped) + + def extrude( to_extrude: Face | Sketch | None = None, amount: float | None = None, @@ -250,11 +304,11 @@ def loft( new_solid = Solid.make_loft(loft_wires, ruled) # Try to recover an invalid loft - if not new_solid.is_valid(): + if not new_solid.is_valid: new_solid = Solid(Shell(new_solid.faces() + section_list)) if clean: new_solid = new_solid.clean() - if not new_solid.is_valid(): + if not new_solid.is_valid: raise RuntimeError("Failed to create valid loft") if context is not None: @@ -338,12 +392,10 @@ def make_brake_formed( raise TypeError("station_widths must be either a single number or an iterable") for vertex in line_vertices: - others = offset_vertices.sort_by_distance(Vector(vertex.X, vertex.Y, vertex.Z)) + others = offset_vertices.sort_by_distance(Vector(vertex)) for other in others[1:]: - if abs(Vector(*(vertex - other).to_tuple()).length - thickness) < 1e-2: - station_edges.append( - Edge.make_line(vertex.to_tuple(), other.to_tuple()) - ) + if abs(Vector((vertex - other)).length - thickness) < 1e-2: + station_edges.append(Edge.make_line(vertex, other)) break station_edges = station_edges.sort_by(line) diff --git a/src/build123d/operations_sketch.py b/src/build123d/operations_sketch.py index 0d4c43b..6cdf780 100644 --- a/src/build123d/operations_sketch.py +++ b/src/build123d/operations_sketch.py @@ -32,13 +32,14 @@ from __future__ import annotations from collections.abc import Iterable from scipy.spatial import Voronoi from typing import cast -from build123d.build_enums import Mode, SortBy +from build123d.build_enums import Mode, SortBy, Transition from build123d.topology import ( Compound, Curve, Edge, Face, ShapeList, + Shell, Wire, Sketch, topo_explore_connected_edges, @@ -298,10 +299,15 @@ def trace( else: raise ValueError("No objects to trace") + # Group the edges into wires to allow for nice transitions + trace_wires = Wire.combine(trace_edges) + new_faces: list[Face] = [] - for edge in trace_edges: - trace_pen = edge.perpendicular_line(line_width, 0) - new_faces.extend(Face.sweep(trace_pen, edge).faces()) + for to_trace in trace_wires: + trace_pen = to_trace.perpendicular_line(line_width, 0) + new_faces.extend( + Shell.sweep(trace_pen, to_trace, transition=Transition.RIGHT).faces() + ) if context is not None: context._add_to_context(*new_faces, mode=mode) context.pending_edges = ShapeList() diff --git a/src/build123d/topology/__init__.py b/src/build123d/topology/__init__.py index 11d00d9..6471c29 100644 --- a/src/build123d/topology/__init__.py +++ b/src/build123d/topology/__init__.py @@ -61,12 +61,13 @@ from .one_d import ( topo_explore_connected_faces, ) from .two_d import Face, Shell, Mixin2D, sort_wires_by_build_order -from .three_d import Solid, Mixin3D +from .three_d import Solid, Mixin3D, DraftAngleError from .composite import Compound, Curve, Sketch, Part __all__ = [ "Shape", "Comparable", + "DraftAngleError", "ShapePredicate", "GroupBy", "ShapeList", diff --git a/src/build123d/topology/one_d.py b/src/build123d/topology/one_d.py index 51fae46..e7b02e4 100644 --- a/src/build123d/topology/one_d.py +++ b/src/build123d/topology/one_d.py @@ -60,7 +60,7 @@ from math import radians, inf, pi, cos, copysign, ceil, floor from typing import Literal, overload, TYPE_CHECKING from typing_extensions import Self from numpy import ndarray -from scipy.optimize import minimize +from scipy.optimize import minimize, minimize_scalar from scipy.spatial import ConvexHull import OCP.TopAbs as ta @@ -176,6 +176,7 @@ from build123d.build_enums import ( from build123d.geometry import ( DEG2RAD, TOLERANCE, + TOL_DIGITS, Axis, Color, Location, @@ -436,7 +437,7 @@ class Mixin1D(Shape): if all(a0.is_coaxial(a1) for a0, a1 in combinations(as_axis, 2)): origin = as_axis[0].position x_dir = as_axis[0].direction - z_dir = as_axis[0].to_plane().x_dir + z_dir = Plane(as_axis[0]).x_dir c_plane = Plane(origin, z_dir=z_dir) result = c_plane.shift_origin((0, 0)) @@ -492,7 +493,11 @@ class Mixin1D(Shape): edge_list: ShapeList[Edge] = ShapeList() while explorer.More(): - edge_list.append(Edge(explorer.Current())) + next_edge = Edge(explorer.Current()) + next_edge.topo_parent = ( + self if self.topo_parent is None else self.topo_parent + ) + edge_list.append(next_edge) explorer.Next() return edge_list else: @@ -1563,7 +1568,7 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): Returns: Edge: linear Edge between two Edges """ - flip = first.to_axis().is_opposite(second.to_axis()) + flip = Axis(first).is_opposite(Axis(second)) pnts = [ Edge.make_line( first.position_at(i), second.position_at(1 - i if flip else i) @@ -2078,14 +2083,25 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): return None def param_at_point(self, point: VectorLike) -> float: - """Normalized parameter at point along Edge""" + """param_at_point + + Args: + point (VectorLike): point on Edge + + Raises: + ValueError: point not on edge + RuntimeError: failed to find parameter + + Returns: + float: parameter value at point on edge + """ # Note that this search algorithm would ideally be replaced with # an OCP based solution, something like that which is shown below. # However, there are known issues with the OCP methods for some # curves which may return negative values or incorrect values at - # end points. Also note that this search takes about 1.5ms while - # the OCP methods take about 0.4ms. + # end points. Also note that this search takes about 1.3ms on a + # complex curve while the OCP methods take about 0.4ms. # # curve = BRep_Tool.Curve_s(self.wrapped, float(), float()) # param_min, param_max = BRep_Tool.Range_s(self.wrapped) @@ -2095,26 +2111,47 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): point = Vector(point) - if not isclose_b(self.distance_to(point), 0, abs_tol=TOLERANCE): - raise ValueError(f"point ({point}) is not on edge") + separation = self.distance_to(point) + if not isclose_b(separation, 0, abs_tol=TOLERANCE): + raise ValueError(f"point ({point}) is {separation} from edge") - # Function to be minimized - def func(param: ndarray) -> float: - return (self.position_at(param[0]) - point).length + # This algorithm finds the normalized [0, 1] parameter of a point on an edge + # by minimizing the 3D distance between the edge and the given point. + # + # Because some edges (e.g., BSplines) can have multiple local minima in the + # distance function, we subdivide the [0, 1] domain into 2^n intervals + # (logarithmic refinement) and perform a bounded minimization in each subinterval. + # + # The first solution found with an error smaller than the geometric resolution + # is returned. If no such minimum is found after all subdivisions, a runtime error + # is raised. - # Find the u value that results in a point within tolerance of the target - initial_guess = max( - 0.0, min(1.0, (point - self.position_at(0)).length / self.length) - ) - result = minimize( - func, - x0=initial_guess, - method="Nelder-Mead", - bounds=[(0.0, 1.0)], - tol=TOLERANCE, - ) - u_value = float(result.x[0]) - return u_value + max_divisions = 10 # Logarithmic refinement depth + + for division in range(max_divisions): + intervals = 2**division + step = 1.0 / intervals + + for i in range(intervals): + lo, hi = i * step, (i + 1) * step + + result = minimize_scalar( + lambda u: (self.position_at(u) - point).length, + bounds=(lo, hi), + method="bounded", + options={"xatol": TOLERANCE / 2}, + ) + + # Early exit if we're below resolution limit + if ( + result.fun + < ( + self @ (result.x + TOLERANCE) - self @ (result.x - TOLERANCE) + ).length + ): + return round(float(result.x), TOL_DIGITS) + + raise RuntimeError("Unable to find parameter, Edge is too complex") def project_to_shape( self, @@ -2184,6 +2221,12 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): def to_axis(self) -> Axis: """Translate a linear Edge to an Axis""" + warnings.warn( + "to_axis is deprecated and will be removed in a future version. " + "Use 'Axis(Edge)' instead.", + DeprecationWarning, + stacklevel=2, + ) if self.geom_type != GeomType.LINE: raise ValueError( f"to_axis is only valid for linear Edges not {self.geom_type}" @@ -2192,6 +2235,12 @@ class Edge(Mixin1D, Shape[TopoDS_Edge]): def to_wire(self) -> Wire: """Edge as Wire""" + warnings.warn( + "to_wire is deprecated and will be removed in a future version. " + "Use 'Wire(Edge)' instead.", + DeprecationWarning, + stacklevel=2, + ) return Wire([self]) def trim(self, start: float, end: float) -> Edge: @@ -2596,7 +2645,7 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]): for edge_index, edge in enumerate(edges): for i in range(fragments_per_edge): param = i / (fragments_per_edge - 1) - points.append(edge.position_at(param).to_tuple()[:2]) + points.append(tuple(edge.position_at(param))[:2]) points_lookup[edge_index * fragments_per_edge + i] = (edge_index, param) convex_hull = ConvexHull(points) @@ -2990,13 +3039,13 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]): projection_object = BRepProj_Projection( self.wrapped, target_object.wrapped, - gp_Dir(*direction_vector.to_tuple()), + gp_Dir(*direction_vector), ) else: projection_object = BRepProj_Projection( self.wrapped, target_object.wrapped, - gp_Pnt(*center_point.to_tuple()), + gp_Pnt(*center_point), ) # Generate a list of the projected wires with aligned orientation @@ -3067,91 +3116,62 @@ class Wire(Mixin1D, Shape[TopoDS_Wire]): def to_wire(self) -> Wire: """Return Wire - used as a pair with Edge.to_wire when self is Wire | Edge""" + warnings.warn( + "to_wire is deprecated and will be removed in a future version. " + "Use 'Wire(Wire)' instead.", + DeprecationWarning, + stacklevel=2, + ) return self def trim(self: Wire, start: float, end: float) -> Wire: - """trim - - Create a new wire by keeping only the section between start and end. + """Trim a wire between [start, end] normalized over total length. Args: - start (float): 0.0 <= start < 1.0 - end (float): 0.0 < end <= 1.0 - - Raises: - ValueError: start >= end + start (float): normalized start position (0.0 to <1.0) + end (float): normalized end position (>0.0 to 1.0) Returns: - Wire: trimmed wire + Wire: trimmed Wire """ - - # pylint: disable=too-many-branches if start >= end: raise ValueError("start must be less than end") - edges = self.edges() + # Extract the edges in order + ordered_edges = self.edges().sort_by(self) # If this is really just an edge, skip the complexity of a Wire - if len(edges) == 1: - return Wire([edges[0].trim(start, end)]) + if len(ordered_edges) == 1: + return Wire([ordered_edges[0].trim(start, end)]) - # For each Edge determine the beginning and end wire parameters - # Note that u, v values are parameters along the Wire - edges_uv_values: list[tuple[float, float, Edge]] = [] - found_end_of_wire = False # for finding ends of closed wires - - for edge in edges: - u = self.param_at_point(edge.position_at(0)) - v = self.param_at_point(edge.position_at(1)) - if self.is_closed: # Avoid two beginnings or ends - u = ( - 1 - u - if found_end_of_wire and (isclose_b(u, 0) or isclose_b(u, 1)) - else u - ) - v = ( - 1 - v - if found_end_of_wire and (isclose_b(v, 0) or isclose_b(v, 1)) - else v - ) - found_end_of_wire = ( - isclose_b(u, 0) - or isclose_b(u, 1) - or isclose_b(v, 0) - or isclose_b(v, 1) - or found_end_of_wire - ) - - # Edge might be reversed and require flipping parms - u, v = (v, u) if u > v else (u, v) - - edges_uv_values.append((u, v, edge)) + total_length = self.length + start_len = start * total_length + end_len = end * total_length trimmed_edges = [] - for u, v, edge in edges_uv_values: - if v < start or u > end: # Edge not needed - continue + cur_length = 0.0 - if start <= u and v <= end: # keep whole Edge - trimmed_edges.append(edge) + for edge in ordered_edges: + edge_len = edge.length + edge_start = cur_length + edge_end = cur_length + edge_len + cur_length = edge_end - elif start >= u and end <= v: # Wire trimmed to single Edge - u_edge = edge.param_at_point(self.position_at(start)) - v_edge = edge.param_at_point(self.position_at(end)) - u_edge, v_edge = ( - (v_edge, u_edge) if u_edge > v_edge else (u_edge, v_edge) - ) - trimmed_edges.append(edge.trim(u_edge, v_edge)) + if edge_end <= start_len or edge_start >= end_len: + continue # skip - elif start <= u: # keep start of Edge - u_edge = edge.param_at_point(self.position_at(end)) - if u_edge != 0: - trimmed_edges.append(edge.trim(0, u_edge)) + if edge_start >= start_len and edge_end <= end_len: + trimmed_edges.append(edge) # keep whole Edge + else: + # Normalize trim points relative to this edge + trim_start_len = max(start_len, edge_start) + trim_end_len = min(end_len, edge_end) - else: # v <= end keep end of Edge - v_edge = edge.param_at_point(self.position_at(start)) - if v_edge != 1: - trimmed_edges.append(edge.trim(v_edge, 1)) + u0 = (trim_start_len - edge_start) / edge_len + u1 = (trim_end_len - edge_start) / edge_len + + if abs(u1 - u0) > TOLERANCE: + trimmed_edges.append(edge.trim(u0, u1)) return Wire(trimmed_edges) diff --git a/src/build123d/topology/shape_core.py b/src/build123d/topology/shape_core.py index a4b48e1..84a7c3d 100644 --- a/src/build123d/topology/shape_core.py +++ b/src/build123d/topology/shape_core.py @@ -50,24 +50,27 @@ import copy import itertools import warnings from abc import ABC, abstractmethod +from collections.abc import Callable, Iterable, Iterator +from functools import reduce from typing import ( - cast as tcast, + TYPE_CHECKING, Any, Generic, + Literal, Optional, Protocol, SupportsIndex, TypeVar, Union, - overload, - TYPE_CHECKING, ) - -from collections.abc import Callable, Iterable, Iterator +from typing import cast as tcast +from typing import overload import OCP.GeomAbs as ga import OCP.TopAbs as ta -from IPython.lib.pretty import pretty, RepresentationPrinter +from anytree import NodeMixin, RenderTree +from IPython.lib.pretty import RepresentationPrinter, pretty +from OCP.Bnd import Bnd_Box, Bnd_OBB from OCP.BOPAlgo import BOPAlgo_GlueEnum from OCP.BRep import BRep_Tool from OCP.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface @@ -98,11 +101,12 @@ from OCP.BRepGProp import BRepGProp, BRepGProp_Face from OCP.BRepIntCurveSurface import BRepIntCurveSurface_Inter from OCP.BRepMesh import BRepMesh_IncrementalMesh from OCP.BRepTools import BRepTools -from OCP.Bnd import Bnd_Box, Bnd_OBB -from OCP.GProp import GProp_GProps +from OCP.gce import gce_MakeLin from OCP.Geom import Geom_Line from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf from OCP.GeomLib import GeomLib_IsPlanarSurface +from OCP.gp import gp_Ax1, gp_Ax2, gp_Ax3, gp_Dir, gp_Pnt, gp_Trsf, gp_Vec +from OCP.GProp import GProp_GProps from OCP.ShapeAnalysis import ShapeAnalysis_Curve from OCP.ShapeCustom import ShapeCustom, ShapeCustom_RestrictionParameters from OCP.ShapeFix import ShapeFix_Shape @@ -110,26 +114,25 @@ from OCP.ShapeUpgrade import ShapeUpgrade_UnifySameDomain from OCP.TopAbs import TopAbs_Orientation, TopAbs_ShapeEnum from OCP.TopExp import TopExp, TopExp_Explorer from OCP.TopLoc import TopLoc_Location -from OCP.TopTools import ( - TopTools_IndexedDataMapOfShapeListOfShape, - TopTools_ListOfShape, - TopTools_SequenceOfShape, -) from OCP.TopoDS import ( TopoDS, TopoDS_Compound, + TopoDS_Edge, TopoDS_Face, TopoDS_Iterator, TopoDS_Shape, TopoDS_Shell, TopoDS_Solid, TopoDS_Vertex, - TopoDS_Edge, TopoDS_Wire, ) -from OCP.gce import gce_MakeLin -from OCP.gp import gp_Ax1, gp_Ax2, gp_Ax3, gp_Dir, gp_Pnt, gp_Trsf, gp_Vec -from anytree import NodeMixin, RenderTree +from OCP.TopTools import ( + TopTools_IndexedDataMapOfShapeListOfShape, + TopTools_ListOfShape, + TopTools_SequenceOfShape, +) +from typing_extensions import Self + from build123d.build_enums import CenterOf, GeomType, Keep, SortBy, Transition from build123d.geometry import ( DEG2RAD, @@ -145,19 +148,16 @@ from build123d.geometry import ( VectorLike, logger, ) -from typing_extensions import Self - -from typing import Literal - if TYPE_CHECKING: # pragma: no cover - from .zero_d import Vertex # pylint: disable=R0801 - from .one_d import Edge, Wire # pylint: disable=R0801 - from .two_d import Face, Shell # pylint: disable=R0801 - from .three_d import Solid # pylint: disable=R0801 - from .composite import Compound # pylint: disable=R0801 from build123d.build_part import BuildPart # pylint: disable=R0801 + from .composite import Compound # pylint: disable=R0801 + from .one_d import Edge, Wire # pylint: disable=R0801 + from .three_d import Solid # pylint: disable=R0801 + from .two_d import Face, Shell # pylint: disable=R0801 + from .zero_d import Vertex # pylint: disable=R0801 + Shapes = Literal["Vertex", "Edge", "Wire", "Face", "Shell", "Solid", "Compound"] TrimmingTool = Union[Plane, "Shell", "Face"] TOPODS = TypeVar("TOPODS", bound=TopoDS_Shape) @@ -422,6 +422,14 @@ class Shape(NodeMixin, Generic[TOPODS]): return True + @property + def is_null(self) -> bool: + """Returns true if this shape is null. In other words, it references no + underlying shape with the potential to be given a location and an + orientation. + """ + return self.wrapped is None or self.wrapped.IsNull() + @property def is_planar_face(self) -> bool: """Is the shape a planar face even though its geom_type may not be PLANE""" @@ -431,6 +439,35 @@ class Shape(NodeMixin, Generic[TOPODS]): is_face_planar = GeomLib_IsPlanarSurface(surface, TOLERANCE) return is_face_planar.IsPlanar() + @property + def is_valid(self) -> bool: + """Returns True if no defect is detected on the shape S or any of its + subshapes. See the OCCT docs on BRepCheck_Analyzer::IsValid for a full + description of what is checked. + """ + if self.wrapped is None: + return True + chk = BRepCheck_Analyzer(self.wrapped) + chk.SetParallel(True) + return chk.IsValid() + + @property + def global_location(self) -> Location: + """ + The location of this Shape relative to the global coordinate system. + + This property computes the composite transformation by traversing the + hierarchy from the root of the assembly to this node, combining the + location of each ancestor. It reflects the absolute position and + orientation of the shape in world space, even when the shape is deeply + nested within an assembly. + + Note: + This is only meaningful when the Shape is part of an assembly tree + where parent-child relationships define relative placements. + """ + return reduce(lambda loc, n: loc * n.location, self.path, Location()) + @property def location(self) -> Location | None: """Get this Shape's Location""" @@ -543,6 +580,11 @@ class Shape(NodeMixin, Generic[TOPODS]): (Vector(principal_props.ThirdAxisOfInertia()), principal_moments[2]), ] + @property + def shape_type(self) -> Shapes: + """Return the shape type string for this class""" + return tcast(Shapes, Shape.shape_LUT[shapetype(self.wrapped)]) + @property def static_moments(self) -> tuple[float, float, float]: """ @@ -660,15 +702,15 @@ class Shape(NodeMixin, Generic[TOPODS]): address = node.address name = "" loc = ( - "Center" + str(node.position.to_tuple()) + "Center" + str(tuple(node.position)) if show_center - else "Position" + str(node.position.to_tuple()) + else "Position" + str(tuple(node.position)) ) else: address = id(node) name = node.__class__.__name__.ljust(9) loc = ( - "Center" + str(node.center().to_tuple()) + "Center" + str(tuple(node.center())) if show_center else "Location" + repr(node.location) ) @@ -758,7 +800,7 @@ class Shape(NodeMixin, Generic[TOPODS]): [shape.__class__.cast(i) for i in shape.entities(entity_type)] ) for item in shape_list: - item.topo_parent = shape + item.topo_parent = shape if shape.topo_parent is None else shape.topo_parent return shape_list @staticmethod @@ -1188,7 +1230,7 @@ class Shape(NodeMixin, Generic[TOPODS]): """fix - try to fix shape if not valid""" if self.wrapped is None: return self - if not self.is_valid(): + if not self.is_valid: shape_copy: Shape = copy.deepcopy(self, None) shape_copy.wrapped = tcast(TOPODS, fix(self.wrapped)) @@ -1332,7 +1374,7 @@ class Shape(NodeMixin, Generic[TOPODS]): return None if ( not isinstance(shape_intersections, ShapeList) - and shape_intersections.is_null() + and shape_intersections.is_null ): return None return shape_intersections @@ -1352,18 +1394,6 @@ class Shape(NodeMixin, Generic[TOPODS]): return False return self.wrapped.IsEqual(other.wrapped) - def is_null(self) -> bool: - """Returns true if this shape is null. In other words, it references no - underlying shape with the potential to be given a location and an - orientation. - - Args: - - Returns: - - """ - return self.wrapped is None or self.wrapped.IsNull() - def is_same(self, other: Shape) -> bool: """Returns True if other and this shape are same, i.e. if they share the same TShape with the same Locations. Orientations may differ. Also see @@ -1379,22 +1409,6 @@ class Shape(NodeMixin, Generic[TOPODS]): return False return self.wrapped.IsSame(other.wrapped) - def is_valid(self) -> bool: - """Returns True if no defect is detected on the shape S or any of its - subshapes. See the OCCT docs on BRepCheck_Analyzer::IsValid for a full - description of what is checked. - - Args: - - Returns: - - """ - if self.wrapped is None: - return True - chk = BRepCheck_Analyzer(self.wrapped) - chk.SetParallel(True) - return chk.IsValid() - def locate(self, loc: Location) -> Self: """Apply a location in absolute sense to self @@ -1626,6 +1640,12 @@ class Shape(NodeMixin, Generic[TOPODS]): Args: loc (Location): new location to set for self """ + warnings.warn( + "The 'relocate' method is deprecated and will be removed in a future version." + "Use move, moved, locate, or located instead", + DeprecationWarning, + stacklevel=2, + ) if self.wrapped is None: raise ValueError("Cannot relocate an empty shape") if loc.wrapped is None: @@ -1677,10 +1697,6 @@ class Shape(NodeMixin, Generic[TOPODS]): return self._apply_transform(transformation) - def shape_type(self) -> Shapes: - """Return the shape type string for this class""" - return tcast(Shapes, Shape.shape_LUT[shapetype(self.wrapped)]) - def shell(self) -> Shell | None: """Return the Shell""" return None @@ -1916,7 +1932,9 @@ class Shape(NodeMixin, Generic[TOPODS]): ) -> Self: """to_splines - Approximate shape with b-splines of the specified degree. + A shape-processing utility that forces all geometry in a shape to be converted into + BSplines. It's useful when working with tools or export formats that require uniform + geometry, or for downstream processing that only understands BSpline representations. Args: degree (int, optional): Maximum degree. Defaults to 3. @@ -2144,7 +2162,7 @@ class Shape(NodeMixin, Generic[TOPODS]): def _ocp_section( self: Shape, other: Vertex | Edge | Wire | Face - ) -> tuple[list[Vertex], list[Edge]]: + ) -> tuple[ShapeList[Vertex], ShapeList[Edge]]: """_ocp_section Create a BRepAlgoAPI_Section object @@ -2162,38 +2180,34 @@ class Shape(NodeMixin, Generic[TOPODS]): other (Union[Vertex, Edge, Wire, Face]): shape to section with Returns: - tuple[list[Vertex], list[Edge]]: section results + tuple[ShapeList[Vertex], ShapeList[Edge]]: section results """ if self.wrapped is None or other.wrapped is None: - return ([], []) + return (ShapeList(), ShapeList()) - try: - section = BRepAlgoAPI_Section(other.geom_adaptor(), self.wrapped) - except (TypeError, AttributeError): - try: - section = BRepAlgoAPI_Section(self.geom_adaptor(), other.wrapped) - except (TypeError, AttributeError): - return ([], []) - - # Perform the intersection calculation + section = BRepAlgoAPI_Section(self.wrapped, other.wrapped) + section.SetRunParallel(True) + section.Approximation(True) + section.ComputePCurveOn1(True) + section.ComputePCurveOn2(True) section.Build() # Get the resulting shapes from the intersection - intersection_shape = section.Shape() + intersection_shape: TopoDS_Shape = section.Shape() - vertices = [] + vertices: list[Vertex] = [] # Iterate through the intersection shape to find intersection points/edges explorer = TopExp_Explorer(intersection_shape, TopAbs_ShapeEnum.TopAbs_VERTEX) while explorer.More(): vertices.append(self.__class__.cast(downcast(explorer.Current()))) explorer.Next() - edges = [] + edges: ShapeList[Edge] = ShapeList() explorer = TopExp_Explorer(intersection_shape, TopAbs_ShapeEnum.TopAbs_EDGE) while explorer.More(): edges.append(self.__class__.cast(downcast(explorer.Current()))) explorer.Next() - return (vertices, edges) + return (ShapeList(set(vertices)), edges) def _repr_html_(self): """Jupyter 3D representation support""" @@ -2326,10 +2340,27 @@ class ShapeList(list[T]): # ---- Instance Methods ---- - def __add__(self, other: ShapeList) -> ShapeList[T]: # type: ignore - """Combine two ShapeLists together operator +""" - # return ShapeList(itertools.chain(self, other)) # breaks MacOS-13 - return ShapeList(list(self) + list(other)) + def __add__(self, other: Shape | Iterable[Shape]) -> ShapeList[T]: # type: ignore + """Return a new ShapeList that includes other""" + if isinstance(other, (Vector, Shape)): + return ShapeList(tcast(list[T], list(self) + [other])) + if isinstance(other, Iterable) and all( + isinstance(o, (Shape, Vector)) for o in other + ): + return ShapeList(list(self) + list(other)) + raise TypeError(f"Cannot add object of type {type(other)} to ShapeList") + + def __iadd__(self, other: Shape | Iterable[Shape]) -> Self: # type: ignore + """In-place addition to this ShapeList""" + if isinstance(other, (Vector, Shape)): + self.append(tcast(T, other)) + elif isinstance(other, Iterable) and all( + isinstance(o, (Shape, Vector)) for o in other + ): + self.extend(other) + else: + raise TypeError(f"Cannot add object of type {type(other)} to ShapeList") + return self def __and__(self, other: ShapeList) -> ShapeList[T]: """Intersect two ShapeLists operator &""" @@ -2582,29 +2613,27 @@ class ShapeList(list[T]): if inclusive == (True, True): objects = filter( lambda o: minimum - <= axis.to_plane().to_local_coords(o).center().Z + <= Plane(axis).to_local_coords(o).center().Z <= maximum, self, ) elif inclusive == (True, False): objects = filter( lambda o: minimum - <= axis.to_plane().to_local_coords(o).center().Z + <= Plane(axis).to_local_coords(o).center().Z < maximum, self, ) elif inclusive == (False, True): objects = filter( lambda o: minimum - < axis.to_plane().to_local_coords(o).center().Z + < Plane(axis).to_local_coords(o).center().Z <= maximum, self, ) elif inclusive == (False, False): objects = filter( - lambda o: minimum - < axis.to_plane().to_local_coords(o).center().Z - < maximum, + lambda o: minimum < Plane(axis).to_local_coords(o).center().Z < maximum, self, ) diff --git a/src/build123d/topology/three_d.py b/src/build123d/topology/three_d.py index ee92394..e4131ce 100644 --- a/src/build123d/topology/three_d.py +++ b/src/build123d/topology/three_d.py @@ -68,7 +68,11 @@ from OCP.BRepClass3d import BRepClass3d_SolidClassifier from OCP.BRepFeat import BRepFeat_MakeDPrism from OCP.BRepFilletAPI import BRepFilletAPI_MakeChamfer, BRepFilletAPI_MakeFillet from OCP.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin -from OCP.BRepOffsetAPI import BRepOffsetAPI_MakePipeShell, BRepOffsetAPI_MakeThickSolid +from OCP.BRepOffsetAPI import ( + BRepOffsetAPI_DraftAngle, + BRepOffsetAPI_MakePipeShell, + BRepOffsetAPI_MakeThickSolid, +) from OCP.BRepPrimAPI import ( BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCone, @@ -88,7 +92,7 @@ from OCP.TopExp import TopExp from OCP.TopTools import TopTools_IndexedDataMapOfShapeListOfShape, TopTools_ListOfShape from OCP.TopoDS import TopoDS, TopoDS_Face, TopoDS_Shape, TopoDS_Solid, TopoDS_Wire from OCP.gp import gp_Ax2, gp_Pnt -from build123d.build_enums import CenterOf, Kind, Transition, Until +from build123d.build_enums import CenterOf, GeomType, Kind, Transition, Until from build123d.geometry import ( DEG2RAD, Axis, @@ -255,7 +259,7 @@ class Mixin3D(Shape): try: new_shape = self.__class__(chamfer_builder.Shape()) - if not new_shape.is_valid(): + if not new_shape.is_valid: raise Standard_Failure except (StdFail_NotDone, Standard_Failure) as err: raise ValueError( @@ -339,7 +343,7 @@ class Mixin3D(Shape): try: new_shape = self.__class__(fillet_builder.Shape()) - if not new_shape.is_valid(): + if not new_shape.is_valid: raise Standard_Failure except (StdFail_NotDone, Standard_Failure) as err: raise ValueError( @@ -431,7 +435,7 @@ class Mixin3D(Shape): """ solid_classifier = BRepClass3d_SolidClassifier(self.wrapped) - solid_classifier.Perform(gp_Pnt(*Vector(point).to_tuple()), tolerance) + solid_classifier.Perform(gp_Pnt(*Vector(point)), tolerance) return solid_classifier.State() == ta.TopAbs_IN or solid_classifier.IsOnAFace() @@ -481,7 +485,7 @@ class Mixin3D(Shape): # Do these numbers work? - if not try with the smaller window try: new_shape = self.__class__(fillet_builder.Shape()) - if not new_shape.is_valid(): + if not new_shape.is_valid: raise fillet_exception except fillet_exception: return __max_fillet(window_min, window_mid, current_iteration + 1) @@ -495,7 +499,7 @@ class Mixin3D(Shape): ) return return_value - if not self.is_valid(): + if not self.is_valid: raise ValueError("Invalid Shape") native_edges = [e.wrapped for e in edge_list] @@ -661,7 +665,7 @@ class Solid(Mixin3D, Shape[TopoDS_Solid]): builder.SetMode(coordinate_system) rotate = True elif isinstance(binormal, (Wire, Edge)): - builder.SetMode(binormal.to_wire().wrapped, True) + builder.SetMode(Wire(binormal).wrapped, True) return rotate @@ -1229,6 +1233,21 @@ class Solid(Mixin3D, Shape[TopoDS_Solid]): Sweep the given cross section into a prismatic solid along the provided path + The is_frenet parameter controls how the profile orientation changes as it + follows along the sweep path. If is_frenet is False, the orientation of the + profile is kept consistent from point to point. The resulting shape has the + minimum possible twisting. Unintuitively, when a profile is swept along a + helix, this results in the orientation of the profile slowly creeping + (rotating) as it follows the helix. Setting is_frenet to True prevents this. + + If is_frenet is True the orientation of the profile is based on the local + curvature and tangency vectors of the path. This keeps the orientation of the + profile consistent when sweeping along a helix (because the curvature vector of + a straight helix always points to its axis). However, when path is not a helix, + the resulting shape can have strange looking twists sometimes. For more + information, see Frenet Serret formulas + http://en.wikipedia.org/wiki/Frenet%E2%80%93Serret_formulas. + Args: section (Union[Face, Wire]): cross section to sweep path (Union[Wire, Edge]): sweep path @@ -1252,7 +1271,7 @@ class Solid(Mixin3D, Shape[TopoDS_Solid]): shapes = [] for wire in [outer_wire] + inner_wires: - builder = BRepOffsetAPI_MakePipeShell(path.to_wire().wrapped) + builder = BRepOffsetAPI_MakePipeShell(Wire(path).wrapped) rotate = False @@ -1294,6 +1313,21 @@ class Solid(Mixin3D, Shape[TopoDS_Solid]): Sweep through a sequence of profiles following a path. + The is_frenet parameter controls how the profile orientation changes as it + follows along the sweep path. If is_frenet is False, the orientation of the + profile is kept consistent from point to point. The resulting shape has the + minimum possible twisting. Unintuitively, when a profile is swept along a + helix, this results in the orientation of the profile slowly creeping + (rotating) as it follows the helix. Setting is_frenet to True prevents this. + + If is_frenet is True the orientation of the profile is based on the local + curvature and tangency vectors of the path. This keeps the orientation of the + profile consistent when sweeping along a helix (because the curvature vector of + a straight helix always points to its axis). However, when path is not a helix, + the resulting shape can have strange looking twists sometimes. For more + information, see Frenet Serret formulas + http://en.wikipedia.org/wiki/Frenet%E2%80%93Serret_formulas. + Args: profiles (Iterable[Union[Wire, Face]]): list of profiles path (Union[Wire, Edge]): The wire to sweep the face resulting from the wires over @@ -1305,7 +1339,7 @@ class Solid(Mixin3D, Shape[TopoDS_Solid]): Returns: Solid: swept object """ - path_as_wire = path.to_wire().wrapped + path_as_wire = Wire(path).wrapped builder = BRepOffsetAPI_MakePipeShell(path_as_wire) @@ -1391,3 +1425,62 @@ class Solid(Mixin3D, Shape[TopoDS_Solid]): raise RuntimeError("Error applying thicken to given surface") from err return result + + def draft(self, faces: Iterable[Face], neutral_plane: Plane, angle: float) -> Solid: + """Apply a draft angle to the given faces of the solid. + + Args: + faces: Faces to which the draft should be applied. + neutral_plane: Plane defining the neutral direction and position. + angle: Draft angle in degrees. + + Returns: + Solid with the specified draft angles applied. + + Raises: + RuntimeError: If draft application fails on any face or during build. + """ + valid_geom_types = {GeomType.PLANE, GeomType.CYLINDER, GeomType.CONE} + for face in faces: + if face.geom_type not in valid_geom_types: + raise ValueError( + f"Face {face} has unsupported geometry type {face.geom_type.name}. " + "Only PLANAR, CYLINDRICAL, and CONICAL faces are supported." + ) + + draft_angle_builder = BRepOffsetAPI_DraftAngle(self.wrapped) + + for face in faces: + draft_angle_builder.Add( + face.wrapped, + neutral_plane.z_dir.to_dir(), + radians(angle), + neutral_plane.wrapped, + Flag=True, + ) + if not draft_angle_builder.AddDone(): + raise DraftAngleError( + "Draft could not be added to a face.", + face=face, + problematic_shape=draft_angle_builder.ProblematicShape(), + ) + + try: + draft_angle_builder.Build() + result = Solid(draft_angle_builder.Shape()) + except StdFail_NotDone as err: + raise DraftAngleError( + "Draft build failed on the given solid.", + face=None, + problematic_shape=draft_angle_builder.ProblematicShape(), + ) from err + return result + + +class DraftAngleError(RuntimeError): + """Solid.draft custom exception""" + + def __init__(self, message, face=None, problematic_shape=None): + super().__init__(message) + self.face = face + self.problematic_shape = problematic_shape diff --git a/src/build123d/topology/two_d.py b/src/build123d/topology/two_d.py index a45ff70..1a1e9dc 100644 --- a/src/build123d/topology/two_d.py +++ b/src/build123d/topology/two_d.py @@ -58,18 +58,18 @@ from __future__ import annotations import copy import sys import warnings -from typing import Any, overload, TypeVar, TYPE_CHECKING - +from abc import ABC, abstractmethod from collections.abc import Iterable, Sequence +from typing import TYPE_CHECKING, Any, TypeVar, overload import OCP.TopAbs as ta -from OCP.BRep import BRep_Tool, BRep_Builder +from OCP.BRep import BRep_Builder, BRep_Tool from OCP.BRepAdaptor import BRepAdaptor_Surface from OCP.BRepAlgo import BRepAlgo from OCP.BRepAlgoAPI import BRepAlgoAPI_Common from OCP.BRepBuilderAPI import ( - BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakeEdge, + BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakeWire, ) from OCP.BRepClass3d import BRepClass3d_SolidClassifier @@ -80,30 +80,31 @@ from OCP.BRepIntCurveSurface import BRepIntCurveSurface_Inter from OCP.BRepOffsetAPI import BRepOffsetAPI_MakeFilling, BRepOffsetAPI_MakePipeShell from OCP.BRepPrimAPI import BRepPrimAPI_MakeRevol from OCP.BRepTools import BRepTools, BRepTools_ReShape -from OCP.GProp import GProp_GProps -from OCP.Geom import Geom_BezierSurface, Geom_Surface, Geom_RectangularTrimmedSurface +from OCP.gce import gce_MakeLin +from OCP.Geom import Geom_BezierSurface, Geom_RectangularTrimmedSurface, Geom_Surface +from OCP.GeomAbs import GeomAbs_C0 from OCP.GeomAPI import ( GeomAPI_ExtremaCurveCurve, GeomAPI_PointsToBSplineSurface, GeomAPI_ProjectPointOnSurf, ) -from OCP.GeomAbs import GeomAbs_C0 from OCP.GeomProjLib import GeomProjLib +from OCP.gp import gp_Pnt, gp_Vec +from OCP.GProp import GProp_GProps from OCP.Precision import Precision from OCP.ShapeFix import ShapeFix_Solid, ShapeFix_Wire from OCP.Standard import ( + Standard_ConstructionError, Standard_Failure, Standard_NoSuchObject, - Standard_ConstructionError, ) from OCP.StdFail import StdFail_NotDone -from OCP.TColStd import TColStd_HArray2OfReal from OCP.TColgp import TColgp_HArray2OfPnt +from OCP.TColStd import TColStd_HArray2OfReal from OCP.TopExp import TopExp -from OCP.TopTools import TopTools_IndexedDataMapOfShapeListOfShape, TopTools_ListOfShape from OCP.TopoDS import TopoDS, TopoDS_Face, TopoDS_Shape, TopoDS_Shell, TopoDS_Solid -from OCP.gce import gce_MakeLin -from OCP.gp import gp_Pnt, gp_Vec +from OCP.TopTools import TopTools_IndexedDataMapOfShapeListOfShape, TopTools_ListOfShape +from typing_extensions import Self from build123d.build_enums import CenterOf, GeomType, Keep, SortBy, Transition from build123d.geometry import ( @@ -117,38 +118,36 @@ from build123d.geometry import ( Vector, VectorLike, ) -from typing_extensions import Self -from .one_d import Mixin1D, Edge, Wire +from .one_d import Edge, Mixin1D, Wire from .shape_core import ( Shape, ShapeList, SkipClean, - downcast, - get_top_level_topods_shapes, _sew_topods_faces, - shapetype, _topods_entities, _topods_face_normal_at, + downcast, + get_top_level_topods_shapes, + shapetype, ) from .utils import ( _extrude_topods_shape, - find_max_dimension, _make_loft, _make_topods_face_from_wires, _topods_bool_op, + find_max_dimension, ) from .zero_d import Vertex - if TYPE_CHECKING: # pragma: no cover - from .three_d import Solid # pylint: disable=R0801 from .composite import Compound, Curve # pylint: disable=R0801 + from .three_d import Solid # pylint: disable=R0801 T = TypeVar("T", Edge, Wire, "Face") -class Mixin2D(Shape): +class Mixin2D(ABC, Shape): """Additional methods to add to Face and Shell class""" project_to_viewport = Mixin1D.project_to_viewport @@ -201,6 +200,9 @@ class Mixin2D(Shape): new_surface = copy.deepcopy(self) new_surface.wrapped = downcast(self.wrapped.Complemented()) + # As the surface has been modified, the parent is no longer valid + new_surface.topo_parent = None + return new_surface def face(self) -> Face | None: @@ -235,7 +237,7 @@ class Mixin2D(Shape): while intersect_maker.More(): inter_pt = intersect_maker.Pnt() # Calculate distance along axis - distance = other.to_plane().to_local_coords(Vector(inter_pt)).Z + distance = Plane(other).to_local_coords(Vector(inter_pt)).Z intersections.append( ( intersect_maker.Face(), # TopoDS_Face @@ -258,6 +260,11 @@ class Mixin2D(Shape): return result + @abstractmethod + def location_at(self, *args: Any, **kwargs: Any) -> Location: + """A location from a face or shell""" + pass + def offset(self, amount: float) -> Self: """Return a copy of self moved along the normal by amount""" return copy.deepcopy(self).moved(Location(self.normal_at() * amount)) @@ -292,7 +299,7 @@ class Mixin2D(Shape): Raises: RuntimeError: wrapping over surface boundary, try difference surface_loc Returns: - Edge: wraped edge + Edge: wrapped edge """ def _intersect_surface_normal( @@ -325,6 +332,9 @@ class Mixin2D(Shape): world_point, world_point - target_object_center ) + if self.wrapped is None: + raise ValueError("Can't wrap around an empty face") + # Initial setup target_object_center = self.center(CenterOf.BOUNDING_BOX) @@ -338,17 +348,26 @@ class Mixin2D(Shape): loop_count = 0 length_error = sys.float_info.max - while length_error > tolerance and loop_count < max_loops: - # Get starting point and normal - surface_origin = surface_loc.position - surface_normal = surface_loc.z_axis.direction + # Find the location on the surface to start + if planar_edge.position_at(0).length > tolerance: + # The start point isn't at the surface_loc so wrap a line to find it + to_start_edge = Edge.make_line((0, 0), planar_edge @ 0) + wrapped_to_start_edge = self._wrap_edge( + to_start_edge, surface_loc, snap_to_face=True, tolerance=tolerance + ) + start_pnt = wrapped_to_start_edge @ 1 + _, start_normal = _intersect_surface_normal( + start_pnt, (start_pnt - target_object_center) + ) + else: + # The start point is at the surface location + start_pnt = surface_loc.position + start_normal = surface_loc.z_axis.direction + while length_error > tolerance and loop_count < max_loops: # Seed the wrapped path wrapped_edge_points: list[VectorLike] = [] - planar_position = planar_edge.position_at(0) - current_point, current_normal = _find_point_on_surface( - surface_origin, surface_normal, planar_position - ) + current_point, current_normal = start_pnt, start_normal wrapped_edge_points.append(current_point) # Subdivide and propagate @@ -374,8 +393,8 @@ class Mixin2D(Shape): raise RuntimeError( f"Length error of {length_error:.6f} exceeds tolerance {tolerance}" ) - if not wrapped_edge.is_valid(): - raise RuntimeError("Wraped edge is invalid") + if wrapped_edge.wrapped is None or not wrapped_edge.is_valid: + raise RuntimeError("Wrapped edge is invalid") if not snap_to_face: return wrapped_edge @@ -520,10 +539,12 @@ class Face(Mixin2D, Shape[TopoDS_Face]): return None if self.geom_type == GeomType.CYLINDER: - return Axis(self.geom_adaptor().Cylinder().Axis()) + return Axis( + self.geom_adaptor().Cylinder().Axis() # type:ignore[attr-defined] + ) if self.geom_type == GeomType.TORUS: - return Axis(self.geom_adaptor().Torus().Axis()) + return Axis(self.geom_adaptor().Torus().Axis()) # type:ignore[attr-defined] return None @@ -729,7 +750,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): axis = self.axis_of_rotation if axis is None or self.radii is None: raise ValueError("Can't find curvature of empty object") - loc = Location(axis.to_plane()) + loc = Location(Plane(axis)) axis_circle = Edge.make_circle(self.radii[0]).locate(loc) _, pnt_on_axis_circle, _ = axis_circle.distance_to_with_closest_points( self.center() @@ -781,8 +802,8 @@ class Face(Mixin2D, Shape[TopoDS_Face]): """Return the major and minor radii of a torus otherwise None""" if self.geom_type == GeomType.TORUS: return ( - self.geom_adaptor().MajorRadius(), - self.geom_adaptor().MinorRadius(), + self.geom_adaptor().MajorRadius(), # type:ignore[attr-defined] + self.geom_adaptor().MinorRadius(), # type:ignore[attr-defined] ) return None @@ -794,7 +815,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): self.geom_type in [GeomType.CYLINDER, GeomType.SPHERE] and type(self.geom_adaptor()) != Geom_RectangularTrimmedSurface ): - return self.geom_adaptor().Radius() + return self.geom_adaptor().Radius() # type:ignore[attr-defined] else: return None @@ -832,6 +853,8 @@ class Face(Mixin2D, Shape[TopoDS_Face]): Returns: Face: extruded shape """ + if obj.wrapped is None: + raise ValueError("Can't extrude empty object") return Face(TopoDS.Face_s(_extrude_topods_shape(obj.wrapped, direction))) @classmethod @@ -978,11 +1001,13 @@ class Face(Mixin2D, Shape[TopoDS_Face]): raise ValueError("exterior must be a Wire or list of Edges") for edge in outside_edges: + if edge.wrapped is None: + raise ValueError("exterior contains empty edges") surface.Add(edge.wrapped, GeomAbs_C0) try: surface.Build() - surface_face = Face(surface.Shape()) + surface_face = Face(surface.Shape()) # type:ignore[call-overload] except ( Standard_Failure, StdFail_NotDone, @@ -994,10 +1019,10 @@ class Face(Mixin2D, Shape[TopoDS_Face]): ) from err if surface_point_vectors: for point in surface_point_vectors: - surface.Add(gp_Pnt(*point.to_tuple())) + surface.Add(gp_Pnt(*point)) try: surface.Build() - surface_face = Face(surface.Shape()) + surface_face = Face(surface.Shape()) # type:ignore[call-overload] except StdFail_NotDone as err: raise RuntimeError( "Error building non-planar face with provided surface_points" @@ -1007,6 +1032,8 @@ class Face(Mixin2D, Shape[TopoDS_Face]): if interior_wires: makeface_object = BRepBuilderAPI_MakeFace(surface_face.wrapped) for wire in interior_wires: + if wire.wrapped is None: + raise ValueError("interior_wires contain an empty wire") makeface_object.Add(wire.wrapped) try: surface_face = Face(makeface_object.Face()) @@ -1016,7 +1043,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): ) from err surface_face = surface_face.fix() - if not surface_face.is_valid(): + if not surface_face.is_valid: raise RuntimeError("non planar face is invalid") return surface_face @@ -1158,7 +1185,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): True, ) - return cls(revol_builder.Shape()) + return cls(revol_builder.Shape()) # type:ignore[call-overload] @classmethod def sew_faces(cls, faces: Iterable[Face]) -> list[ShapeList[Face]]: @@ -1189,7 +1216,8 @@ class Face(Mixin2D, Shape[TopoDS_Face]): elif isinstance(top_level_shape, TopoDS_Solid): sewn_faces.append( ShapeList( - Face(f) for f in _topods_entities(top_level_shape, "Face") + Face(f) # type:ignore[call-overload] + for f in _topods_entities(top_level_shape, "Face") ) ) else: @@ -1236,7 +1264,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): builder.Add(profile.wrapped, False, False) builder.SetTransitionMode(Shape._transModeDict[transition]) builder.Build() - result = Face(builder.Shape()) + result = Face(builder.Shape()) # type:ignore[call-overload] if SkipClean.clean: result = result.clean() @@ -1355,8 +1383,10 @@ class Face(Mixin2D, Shape[TopoDS_Face]): def inner_wires(self) -> ShapeList[Wire]: """Extract the inner or hole wires from this Face""" outer = self.outer_wire() - - return ShapeList([w for w in self.wires() if not w.is_same(outer)]) + inners = [w for w in self.wires() if not w.is_same(outer)] + for w in inners: + w.topo_parent = self if self.topo_parent is None else self.topo_parent + return ShapeList(inners) def is_coplanar(self, plane: Plane) -> bool: """Is this planar face coplanar with the provided plane""" @@ -1387,23 +1417,112 @@ class Face(Mixin2D, Shape[TopoDS_Face]): """ solid_classifier = BRepClass3d_SolidClassifier(self.wrapped) - solid_classifier.Perform(gp_Pnt(*Vector(point).to_tuple()), tolerance) + solid_classifier.Perform(gp_Pnt(*Vector(point)), tolerance) return solid_classifier.IsOnAFace() # surface = BRep_Tool.Surface_s(self.wrapped) # projector = GeomAPI_ProjectPointOnSurf(Vector(point).to_pnt(), surface) # return projector.LowerDistance() <= TOLERANCE + @overload def location_at( - self, u: float, v: float, x_dir: VectorLike | None = None - ) -> Location: - """Location at the u/v position of face""" - origin = self.position_at(u, v) - if x_dir is None: - pln = Plane(origin, z_dir=self.normal_at(u, v)) + self, + surface_point: VectorLike | None = None, + *, + x_dir: VectorLike | None = None, + ) -> Location: ... + + @overload + def location_at( + self, u: float, v: float, *, x_dir: VectorLike | None = None + ) -> Location: ... + + def location_at(self, *args, **kwargs) -> Location: + """location_at + + Get the location (origin and orientation) on the surface of the face. + + This method supports two overloads: + + 1. `location_at(u: float, v: float, *, x_dir: VectorLike | None = None) -> Location` + - Specifies the point in normalized UV parameter space of the face. + - `u` and `v` are floats between 0.0 and 1.0. + - Optionally override the local X direction using `x_dir`. + + 2. `location_at(surface_point: VectorLike, *, x_dir: VectorLike | None = None) -> Location` + - Projects the given 3D point onto the face surface. + - The point must be reasonably close to the face. + - Optionally override the local X direction using `x_dir`. + + If no arguments are provided, the location at the center of the face + (u=0.5, v=0.5) is returned. + + Args: + u (float): Normalized horizontal surface parameter (optional). + v (float): Normalized vertical surface parameter (optional). + surface_point (VectorLike): A 3D point near the surface (optional). + x_dir (VectorLike, optional): Direction for the local X axis. If not given, + the tangent in the U direction is used. + + Returns: + Location: A full 3D placement at the specified point on the face surface. + + Raises: + ValueError: If only one of `u` or `v` is provided or invalid keyword args are passed. + """ + surface_point, u, v = None, -1.0, -1.0 + + if args: + if isinstance(args[0], (Vector, Sequence)): + surface_point = args[0] + elif isinstance(args[0], (int, float)): + u = args[0] + if len(args) == 2 and isinstance(args[1], (int, float)): + v = args[1] + + unknown_args = set(kwargs.keys()).difference( + {"surface_point", "u", "v", "x_dir"} + ) + if unknown_args: + raise ValueError(f"Unexpected argument(s) {', '.join(unknown_args)}") + + surface_point = kwargs.get("surface_point", surface_point) + u = kwargs.get("u", u) + v = kwargs.get("v", v) + user_x_dir = kwargs.get("x_dir", None) + + if surface_point is None and u < 0 and v < 0: + u, v = 0.5, 0.5 + elif surface_point is None and (u < 0 or v < 0): + raise ValueError("Both u & v values must be specified") + + geom_surface: Geom_Surface = self.geom_adaptor() + u_min, u_max, v_min, v_max = self._uv_bounds() + + if surface_point is None: + u_val = u_min + u * (u_max - u_min) + v_val = v_min + v * (v_max - v_min) else: - pln = Plane(origin, x_dir=Vector(x_dir), z_dir=self.normal_at(u, v)) - return Location(pln) + projector = GeomAPI_ProjectPointOnSurf( + Vector(surface_point).to_pnt(), geom_surface + ) + u_val, v_val = projector.LowerDistanceParameters() + + # Evaluate point and partials + pnt = gp_Pnt() + du = gp_Vec() + dv = gp_Vec() + geom_surface.D1(u_val, v_val, pnt, du, dv) + + origin = Vector(pnt) + z_dir = Vector(du).cross(Vector(dv)).normalized() + x_dir = ( + Vector(user_x_dir).normalized() + if user_x_dir is not None + else Vector(du).normalized() + ) + + return Location(Plane(origin=origin, x_dir=x_dir, z_dir=z_dir)) def make_holes(self, interior_wires: list[Wire]) -> Face: """Make Holes in Face @@ -1443,7 +1562,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): ) from err surface_face = surface_face.fix() - # if not surface_face.is_valid(): + # if not surface_face.is_valid: # raise RuntimeError("non planar face is invalid") return surface_face @@ -1537,7 +1656,9 @@ class Face(Mixin2D, Shape[TopoDS_Face]): def outer_wire(self) -> Wire: """Extract the perimeter wire from this Face""" - return Wire(BRepTools.OuterWire_s(self.wrapped)) + outer = Wire(BRepTools.OuterWire_s(self.wrapped)) + outer.topo_parent = self if self.topo_parent is None else self.topo_parent + return outer def position_at(self, u: float, v: float) -> Vector: """position_at @@ -1600,7 +1721,9 @@ class Face(Mixin2D, Shape[TopoDS_Face]): (extruded_topods_self,), (target_object.wrapped,), BRepAlgoAPI_Common() ) if not topods_shape.IsNull(): - intersected_shapes.append(Face(topods_shape)) + intersected_shapes.append( + Face(topods_shape) # type:ignore[call-overload] + ) else: for target_shell in target_object.shells(): topods_shape = _topods_bool_op( @@ -1627,12 +1750,21 @@ class Face(Mixin2D, Shape[TopoDS_Face]): Approximate planar face with arcs and straight line segments. + This is a utility used internally to convert or adapt a face for Boolean operations. Its + purpose is not typically for general use, but rather as a helper within the Boolean kernel + to ensure input faces are in a compatible and canonical form. + Args: tolerance (float, optional): Approximation tolerance. Defaults to 1e-3. Returns: Face: approximated face """ + warnings.warn( + "The 'to_arcs' method is deprecated and will be removed in a future version.", + DeprecationWarning, + stacklevel=2, + ) if self.wrapped is None: raise ValueError("Cannot approximate an empty shape") @@ -1754,6 +1886,71 @@ class Face(Mixin2D, Shape[TopoDS_Face]): f"{type(planar_shape)}" ) + def wrap_faces( + self, + faces: Iterable[Face], + path: Wire | Edge, + start: float = 0.0, + ) -> ShapeList[Face]: + """wrap_faces + + Wrap a sequence of 2D faces onto a 3D surface, aligned along a guiding path. + + This method places multiple planar `Face` objects (defined in the XY plane) onto a + curved 3D surface (`self`), following a given path (Wire or Edge) that lies on or + closely follows the surface. Each face is spaced along the path according to its + original horizontal (X-axis) position, preserving the relative layout of the input + faces. + + The wrapping process attempts to maintain the shape and size of each face while + minimizing distortion. Each face is repositioned to the origin, then individually + wrapped onto the surface starting at a specific point along the path. The face's + new orientation is defined using the path's tangent direction and the surface normal + at that point. + + This is particularly useful for placing a series of features—such as embossed logos, + engraved labels, or patterned tiles—onto a freeform or cylindrical surface, aligned + along a reference edge or curve. + + Args: + faces (Iterable[Face]): An iterable of 2D planar faces to be wrapped. + path (Wire | Edge): A curve on the target surface that defines the alignment + direction. The X-position of each face is mapped to a relative position + along this path. + start (float, optional): The relative starting point on the path (between 0.0 + and 1.0) where the first face should be placed. Defaults to 0.0. + + Returns: + ShapeList[Face]: A list of wrapped face objects, aligned and conformed to the + surface. + """ + path_length = path.length + + face_list = list(faces) + first_face_min_x = face_list[0].bounding_box().min.X + + # Position each face at the origin and wrap onto surface + wrapped_faces: ShapeList[Face] = ShapeList() + for face in face_list: + bbox = face.bounding_box() + face_center_x = (bbox.min.X + bbox.max.X) / 2 + delta_x = face_center_x - first_face_min_x + relative_position_on_wire = start + delta_x / path_length + path_position = path.position_at(relative_position_on_wire) + surface_location = Location( + Plane( + path_position, + x_dir=path.tangent_at(relative_position_on_wire), + z_dir=self.normal_at(path_position), + ) + ) + assert isinstance(face.position, Vector) + face.position -= (delta_x, 0, 0) # Shift back to origin + wrapped_face = Face.wrap(self, face, surface_location) + wrapped_faces.append(wrapped_face) + + return wrapped_faces + def _uv_bounds(self) -> tuple[float, float, float, float]: """Return the u min, u max, v min, v max values""" return BRepTools.UVBounds_s(self.wrapped) @@ -1787,7 +1984,9 @@ class Face(Mixin2D, Shape[TopoDS_Face]): for w in planar_face.inner_wires() ] wrapped_face = Face.make_surface( - wrapped_perimeter, interior_wires=wrapped_holes + wrapped_perimeter, + surface_points=[surface_loc.position], + interior_wires=wrapped_holes, ) # Potentially flip the wrapped face to match the surface @@ -1889,7 +2088,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): return Wire(wrapped_edges) # - # Part 3: The first and last edges likey don't meet at this point due to + # Part 3: The first and last edges likely don't meet at this point due to # distortion caused by following the surface, so we'll need to join # them. # @@ -1947,7 +2146,7 @@ class Face(Mixin2D, Shape[TopoDS_Face]): # # Part 5: Validate # - if not wrapped_wire.is_valid(): + if not wrapped_wire.is_valid: raise RuntimeError("wrapped wire is not valid") return wrapped_wire @@ -2121,6 +2320,28 @@ class Shell(Mixin2D, Shape[TopoDS_Shell]): BRepGProp.LinearProperties_s(self.wrapped, properties) return Vector(properties.CentreOfMass()) + def location_at( + self, + surface_point: VectorLike, + *, + x_dir: VectorLike | None = None, + ) -> Location: + """location_at + + Get the location (origin and orientation) on the surface of the shell. + + Args: + surface_point (VectorLike): A 3D point near the surface. + x_dir (VectorLike, optional): Direction for the local X axis. If not given, + the tangent in the U direction is used. + + Returns: + Location: A full 3D placement at the specified point on the shell surface. + """ + # Find the closest Face and get the location from it + face = self.faces().sort_by(lambda f: f.distance_to(surface_point))[0] + return face.location_at(surface_point, x_dir=x_dir) + def sort_wires_by_build_order(wire_list: list[Wire]) -> list[list[Wire]]: """Tries to determine how wires should be combined into faces. diff --git a/src/build123d/topology/zero_d.py b/src/build123d/topology/zero_d.py index 59518c7..bd19653 100644 --- a/src/build123d/topology/zero_d.py +++ b/src/build123d/topology/zero_d.py @@ -54,6 +54,8 @@ license: from __future__ import annotations import itertools +import warnings + from typing import overload, TYPE_CHECKING from collections.abc import Iterable @@ -132,7 +134,8 @@ class Vertex(Shape[TopoDS_Vertex]): ) super().__init__(ocp_vx) - self.X, self.Y, self.Z = self.to_tuple() + pnt = BRep_Tool.Pnt_s(self.wrapped) + self.X, self.Y, self.Z = pnt.X(), pnt.Y(), pnt.Z() # ---- Properties ---- @@ -239,7 +242,7 @@ class Vertex(Shape[TopoDS_Vertex]): def __sub__(self, other: Vertex | Vector | tuple) -> Vertex: # type: ignore """Subtract - Substract a Vertex with a Vertex, Vector or Tuple from self + Subtract a Vertex with a Vertex, Vector or Tuple from self Args: other: Value to add @@ -272,6 +275,12 @@ class Vertex(Shape[TopoDS_Vertex]): def to_tuple(self) -> tuple[float, float, float]: """Return vertex as three tuple of floats""" + warnings.warn( + "to_tuple is deprecated and will be removed in a future version. " + "Use 'tuple(Vertex)' instead.", + DeprecationWarning, + stacklevel=2, + ) geom_point = BRep_Tool.Pnt_s(self.wrapped) return (geom_point.X(), geom_point.Y(), geom_point.Z()) diff --git a/tests/test_build_common.py b/tests/test_build_common.py index a4c6e0e..419e433 100644 --- a/tests/test_build_common.py +++ b/tests/test_build_common.py @@ -237,18 +237,16 @@ class TestCommonOperations(unittest.TestCase): def test_matmul(self): self.assertTupleAlmostEquals( - (Edge.make_line((0, 0, 0), (1, 1, 1)) @ 0.5).to_tuple(), (0.5, 0.5, 0.5), 5 + Edge.make_line((0, 0, 0), (1, 1, 1)) @ 0.5, (0.5, 0.5, 0.5), 5 ) def test_mod(self): - self.assertTupleAlmostEquals( - (Wire.make_circle(10) % 0.5).to_tuple(), (0, -1, 0), 5 - ) + self.assertTupleAlmostEquals(Wire.make_circle(10) % 0.5, (0, -1, 0), 5) def test_xor(self): helix_loc = Edge.make_helix(2 * pi, 1, 1) ^ 0 - self.assertTupleAlmostEquals(helix_loc.position.to_tuple(), (1, 0, 0), 5) - self.assertTupleAlmostEquals(helix_loc.orientation.to_tuple(), (-45, 0, 180), 5) + self.assertTupleAlmostEquals(helix_loc.position, (1, 0, 0), 5) + self.assertTupleAlmostEquals(helix_loc.orientation, (-45, 0, 180), 5) class TestLocations(unittest.TestCase): @@ -256,11 +254,11 @@ class TestLocations(unittest.TestCase): locs = PolarLocations(1, 5, 45, 90, False).local_locations for i, angle in enumerate(range(45, 135, 18)): self.assertTupleAlmostEquals( - locs[i].position.to_tuple(), - Vector(1, 0).rotate(Axis.Z, angle).to_tuple(), + locs[i].position, + Vector(1, 0).rotate(Axis.Z, angle), 5, ) - self.assertTupleAlmostEquals(locs[i].orientation.to_tuple(), (0, 0, 0), 5) + self.assertTupleAlmostEquals(locs[i].orientation, (0, 0, 0), 5) def test_polar_endpoint(self): locs = PolarLocations( @@ -284,7 +282,7 @@ class TestLocations(unittest.TestCase): def test_no_centering(self): with BuildSketch(): with GridLocations(4, 4, 2, 2, align=(Align.MIN, Align.MIN)) as l: - pts = [loc.to_tuple()[0] for loc in l.locations] + pts = [tuple(loc)[0] for loc in l.locations] self.assertTupleAlmostEquals(pts[0], (0, 0, 0), 5) self.assertTupleAlmostEquals(pts[1], (0, 4, 0), 5) self.assertTupleAlmostEquals(pts[2], (4, 0, 0), 5) @@ -329,11 +327,11 @@ class TestLocations(unittest.TestCase): self.assertAlmostEqual(hloc.radius, 1, 7) self.assertAlmostEqual(hloc.diagonal, 2, 7) self.assertAlmostEqual(hloc.apothem, 3**0.5 / 2, 7) - + def test_centering(self): with BuildSketch(): with GridLocations(4, 4, 2, 2, align=(Align.CENTER, Align.CENTER)) as l: - pts = [loc.to_tuple()[0] for loc in l.locations] + pts = [tuple(loc)[0] for loc in l.locations] self.assertTupleAlmostEquals(pts[0], (-2, -2, 0), 5) self.assertTupleAlmostEquals(pts[1], (-2, 2, 0), 5) self.assertTupleAlmostEquals(pts[2], (2, -2, 0), 5) @@ -343,7 +341,7 @@ class TestLocations(unittest.TestCase): with BuildSketch(): with Locations((-2, -2), (2, 2)): with GridLocations(1, 1, 2, 2) as nested_grid: - pts = [loc.to_tuple()[0] for loc in nested_grid.local_locations] + pts = [tuple(loc)[0] for loc in nested_grid.local_locations] self.assertTupleAlmostEquals(pts[0], (-2.50, -2.50, 0.00), 5) self.assertTupleAlmostEquals(pts[1], (-2.50, -1.50, 0.00), 5) self.assertTupleAlmostEquals(pts[2], (-1.50, -2.50, 0.00), 5) @@ -357,8 +355,8 @@ class TestLocations(unittest.TestCase): with BuildSketch(): with PolarLocations(6, 3): with GridLocations(1, 1, 2, 2) as polar_grid: - pts = [loc.to_tuple()[0] for loc in polar_grid.local_locations] - ort = [loc.to_tuple()[1] for loc in polar_grid.local_locations] + pts = [tuple(loc)[0] for loc in polar_grid.local_locations] + ort = [tuple(loc)[1] for loc in polar_grid.local_locations] self.assertTupleAlmostEquals(pts[0], (5.50, -0.50, 0.00), 2) self.assertTupleAlmostEquals(pts[1], (5.50, 0.50, 0.00), 2) @@ -390,22 +388,18 @@ class TestLocations(unittest.TestCase): square = Face.make_rect(1, 1, Plane.XZ) with BuildPart(): loc = Locations(square).locations[0] - self.assertTupleAlmostEquals( - loc.position.to_tuple(), Location(Plane.XZ).position.to_tuple(), 5 - ) - self.assertTupleAlmostEquals( - loc.orientation.to_tuple(), Location(Plane.XZ).orientation.to_tuple(), 5 - ) + self.assertTupleAlmostEquals(loc.position, Location(Plane.XZ).position, 5) + self.assertTupleAlmostEquals(loc.orientation, Location(Plane.XZ).orientation, 5) def test_from_plane(self): with BuildPart(): loc = Locations(Plane.XY.offset(1)).locations[0] - self.assertTupleAlmostEquals(loc.position.to_tuple(), (0, 0, 1), 5) + self.assertTupleAlmostEquals(loc.position, (0, 0, 1), 5) def test_from_axis(self): with BuildPart(): loc = Locations(Axis((1, 1, 1), (0, 0, 1))).locations[0] - self.assertTupleAlmostEquals(loc.position.to_tuple(), (1, 1, 1), 5) + self.assertTupleAlmostEquals(loc.position, (1, 1, 1), 5) def test_multiplication(self): circles = GridLocations(2, 2, 2, 2) * Circle(1) @@ -416,25 +410,17 @@ class TestLocations(unittest.TestCase): def test_grid_attributes(self): grid = GridLocations(5, 10, 3, 4) - self.assertTupleAlmostEquals(grid.size.to_tuple(), (10, 30, 0), 5) - self.assertTupleAlmostEquals(grid.min.to_tuple(), (-5, -15, 0), 5) - self.assertTupleAlmostEquals(grid.max.to_tuple(), (5, 15, 0), 5) + self.assertTupleAlmostEquals(grid.size, (10, 30, 0), 5) + self.assertTupleAlmostEquals(grid.min, (-5, -15, 0), 5) + self.assertTupleAlmostEquals(grid.max, (5, 15, 0), 5) def test_mixed_sequence_list(self): locs = Locations((0, 1), [(2, 3), (4, 5)], (6, 7)) self.assertEqual(len(locs.locations), 4) - self.assertTupleAlmostEquals( - locs.locations[0].position.to_tuple(), (0, 1, 0), 5 - ) - self.assertTupleAlmostEquals( - locs.locations[1].position.to_tuple(), (2, 3, 0), 5 - ) - self.assertTupleAlmostEquals( - locs.locations[2].position.to_tuple(), (4, 5, 0), 5 - ) - self.assertTupleAlmostEquals( - locs.locations[3].position.to_tuple(), (6, 7, 0), 5 - ) + self.assertTupleAlmostEquals(locs.locations[0].position, (0, 1, 0), 5) + self.assertTupleAlmostEquals(locs.locations[1].position, (2, 3, 0), 5) + self.assertTupleAlmostEquals(locs.locations[2].position, (4, 5, 0), 5) + self.assertTupleAlmostEquals(locs.locations[3].position, (6, 7, 0), 5) class TestProperties(unittest.TestCase): @@ -449,27 +435,25 @@ class TestRotation(unittest.TestCase): def test_init(self): thirty_by_three = Rotation(30, 30, 30) box_vertices = Solid.make_box(1, 1, 1).moved(thirty_by_three).vertices() + self.assertTupleAlmostEquals(tuple(box_vertices[0]), (0.5, -0.4330127, 0.75), 5) + self.assertTupleAlmostEquals(tuple(box_vertices[1]), (0.0, 0.0, 0.0), 7) self.assertTupleAlmostEquals( - box_vertices[0].to_tuple(), (0.5, -0.4330127, 0.75), 5 - ) - self.assertTupleAlmostEquals(box_vertices[1].to_tuple(), (0.0, 0.0, 0.0), 7) - self.assertTupleAlmostEquals( - box_vertices[2].to_tuple(), (0.0669872, 0.191987, 1.399519), 5 + tuple(box_vertices[2]), (0.0669872, 0.191987, 1.399519), 5 ) self.assertTupleAlmostEquals( - box_vertices[3].to_tuple(), (-0.4330127, 0.625, 0.6495190), 5 + tuple(box_vertices[3]), (-0.4330127, 0.625, 0.6495190), 5 ) self.assertTupleAlmostEquals( - box_vertices[4].to_tuple(), (1.25, 0.2165063, 0.625), 5 + tuple(box_vertices[4]), (1.25, 0.2165063, 0.625), 5 ) self.assertTupleAlmostEquals( - box_vertices[5].to_tuple(), (0.75, 0.649519, -0.125), 5 + tuple(box_vertices[5]), (0.75, 0.649519, -0.125), 5 ) self.assertTupleAlmostEquals( - box_vertices[6].to_tuple(), (0.816987, 0.841506, 1.274519), 5 + tuple(box_vertices[6]), (0.816987, 0.841506, 1.274519), 5 ) self.assertTupleAlmostEquals( - box_vertices[7].to_tuple(), (0.3169872, 1.2745190, 0.52451905), 5 + tuple(box_vertices[7]), (0.3169872, 1.2745190, 0.52451905), 5 ) @@ -706,7 +690,7 @@ class TestShapeList(unittest.TestCase): def test_shapes(self): with BuildPart() as test: Box(1, 1, 1) - self.assertIsNone(test._shapes(Compound)) + self.assertEqual(test._shapes(Compound), []) def test_operators(self): with BuildPart() as test: @@ -744,12 +728,12 @@ class TestValidateInputs(unittest.TestCase): class TestVectorExtensions(unittest.TestCase): def test_vector_localization(self): self.assertTupleAlmostEquals( - (Vector(1, 1, 1) + (1, 2)).to_tuple(), + (Vector(1, 1, 1) + (1, 2)), (2, 3, 1), 5, ) self.assertTupleAlmostEquals( - (Vector(3, 3, 3) - (1, 2)).to_tuple(), + (Vector(3, 3, 3) - (1, 2)), (2, 1, 3), 5, ) @@ -759,16 +743,14 @@ class TestVectorExtensions(unittest.TestCase): Vector(1, 2, 3) - "four" with BuildLine(Plane.YZ): + self.assertTupleAlmostEquals(WorkplaneList.localize((1, 2)), (0, 1, 2), 5) self.assertTupleAlmostEquals( - WorkplaneList.localize((1, 2)).to_tuple(), (0, 1, 2), 5 - ) - self.assertTupleAlmostEquals( - WorkplaneList.localize(Vector(1, 1, 1) + (1, 2)).to_tuple(), + WorkplaneList.localize(Vector(1, 1, 1) + (1, 2)), (1, 2, 3), 5, ) self.assertTupleAlmostEquals( - WorkplaneList.localize(Vector(3, 3, 3) - (1, 2)).to_tuple(), + WorkplaneList.localize(Vector(3, 3, 3) - (1, 2)), (3, 2, 1), 5, ) @@ -780,7 +762,7 @@ class TestVectorExtensions(unittest.TestCase): with BuildLine(pln): n3 = Line((-50, -40), (0, 0)) n4 = Line(n3 @ 1, n3 @ 1 + (0, 10)) - self.assertTupleAlmostEquals((n4 @ 1).to_tuple(), (0, 0, -25), 5) + self.assertTupleAlmostEquals((n4 @ 1), (0, 0, -25), 5) class TestWorkplaneList(unittest.TestCase): @@ -794,8 +776,8 @@ class TestWorkplaneList(unittest.TestCase): def test_localize(self): with BuildLine(Plane.YZ): pnts = WorkplaneList.localize((1, 2), (2, 3)) - self.assertTupleAlmostEquals(pnts[0].to_tuple(), (0, 1, 2), 5) - self.assertTupleAlmostEquals(pnts[1].to_tuple(), (0, 2, 3), 5) + self.assertTupleAlmostEquals(pnts[0], (0, 1, 2), 5) + self.assertTupleAlmostEquals(pnts[1], (0, 2, 3), 5) def test_invalid_workplane(self): with self.assertRaises(ValueError): diff --git a/tests/test_build_generic.py b/tests/test_build_generic.py index 46a6c34..ac02ca5 100644 --- a/tests/test_build_generic.py +++ b/tests/test_build_generic.py @@ -72,7 +72,7 @@ class AddTests(unittest.TestCase): # Add Edge with BuildLine() as test: add(Edge.make_line((0, 0, 0), (1, 1, 1))) - self.assertTupleAlmostEquals((test.wires()[0] @ 1).to_tuple(), (1, 1, 1), 5) + self.assertTupleAlmostEquals(test.wires()[0] @ 1, (1, 1, 1), 5) # Add Wire with BuildLine() as wire: Polyline((0, 0, 0), (1, 1, 1), (2, 0, 0), (3, 1, 1)) @@ -94,13 +94,11 @@ class AddTests(unittest.TestCase): add(Solid.make_box(10, 10, 10), rotation=(0, 0, 45)) self.assertAlmostEqual(test.part.volume, 1000, 5) self.assertTupleAlmostEquals( - ( - test.part.edges() - .group_by(Axis.Z)[-1] - .group_by(Axis.X)[-1] - .sort_by(Axis.Y)[0] - % 1 - ).to_tuple(), + test.part.edges() + .group_by(Axis.Z)[-1] + .group_by(Axis.X)[-1] + .sort_by(Axis.Y)[0] + % 1, (sqrt(2) / 2, sqrt(2) / 2, 0), 5, ) @@ -405,7 +403,7 @@ class LocationsTests(unittest.TestCase): with BuildPart(): with Locations(Location(Vector())): self.assertTupleAlmostEquals( - LocationList._get_context().locations[0].to_tuple()[0], (0, 0, 0), 5 + tuple(LocationList._get_context().locations[0])[0], (0, 0, 0), 5 ) def test_errors(self): @@ -524,7 +522,7 @@ class OffsetTests(unittest.TestCase): def test_face_offset_with_holes(self): sk = Rectangle(100, 100) - GridLocations(80, 80, 2, 2) * Circle(5) sk2 = offset(sk, -5) - self.assertTrue(sk2.face().is_valid()) + self.assertTrue(sk2.face().is_valid) self.assertLess(sk2.area, sk.area) self.assertEqual(len(sk2), 1) @@ -680,12 +678,12 @@ class ProjectionTests(unittest.TestCase): def test_project_point(self): pnt: Vector = project(Vector(1, 2, 3), Plane.XY)[0] - self.assertTupleAlmostEquals(pnt.to_tuple(), (1, 2, 0), 5) + self.assertTupleAlmostEquals(pnt, (1, 2, 0), 5) pnt: Vector = project(Vertex(1, 2, 3), Plane.XZ)[0] - self.assertTupleAlmostEquals(pnt.to_tuple(), (1, 3, 0), 5) + self.assertTupleAlmostEquals(pnt, (1, 3, 0), 5) with BuildSketch(Plane.YZ) as s1: pnt = project(Vertex(1, 2, 3), mode=Mode.PRIVATE)[0] - self.assertTupleAlmostEquals(pnt.to_tuple(), (2, 3, 0), 5) + self.assertTupleAlmostEquals(pnt, (2, 3, 0), 5) def test_multiple_results(self): with BuildLine() as l1: @@ -883,7 +881,7 @@ class TestSweep(unittest.TestCase): Rectangle(2 * lip, 2 * lip, align=(Align.CENTER, Align.CENTER)) sweep(sections=sk2.sketch, path=topedgs, mode=Mode.SUBTRACT) - self.assertTrue(p.part.is_valid()) + self.assertTrue(p.part.is_valid) def test_path_error(self): e1 = Edge.make_line((0, 0), (1, 0)) diff --git a/tests/test_build_line.py b/tests/test_build_line.py index b473077..d6c939b 100644 --- a/tests/test_build_line.py +++ b/tests/test_build_line.py @@ -205,7 +205,7 @@ class BuildLineTests(unittest.TestCase): l3 = Line((0, 0), (10, 10)) l4 = IntersectingLine((0, 10), (1, -1), l3) - self.assertTupleAlmostEquals((l4 @ 1).to_tuple(), (5, 5, 0), 5) + self.assertTupleAlmostEquals(l4 @ 1, (5, 5, 0), 5) self.assertTrue(isinstance(l4, Edge)) with self.assertRaises(ValueError): @@ -214,22 +214,20 @@ class BuildLineTests(unittest.TestCase): def test_jern_arc(self): with BuildLine() as jern: j1 = JernArc((1, 0), (0, 1), 1, 90) - self.assertTupleAlmostEquals((jern.line @ 1).to_tuple(), (0, 1, 0), 5) + self.assertTupleAlmostEquals(jern.line @ 1, (0, 1, 0), 5) self.assertAlmostEqual(j1.radius, 1) 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.assertTupleAlmostEquals(offset_l.line @ 1, (0, 1, 1), 5) self.assertAlmostEqual(off1.radius, 1) 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, (-sqrt(2), -sqrt(2), 0), 5) self.assertAlmostEqual(iso1.radius, 1) self.assertAlmostEqual(iso1.length, pi) @@ -240,11 +238,11 @@ class BuildLineTests(unittest.TestCase): self.assertFalse(l2.is_closed) 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) + self.assertTupleAlmostEquals(circle_face.center(), (0, 1, 0), 5) + self.assertTupleAlmostEquals(l1.vertex(), l2.start, 5) l1 = JernArc((0, 0), (1, 0), 1, 90) - self.assertTupleAlmostEquals((l1 @ 1).to_tuple(), (1, 1, 0), 5) + self.assertTupleAlmostEquals(l1 @ 1, (1, 1, 0), 5) self.assertTrue(isinstance(l1, Edge)) def test_polar_line(self): @@ -252,38 +250,38 @@ class BuildLineTests(unittest.TestCase): with BuildLine(): a1 = PolarLine((0, 0), sqrt(2), 45) d1 = PolarLine((0, 0), sqrt(2), direction=(1, 1)) - self.assertTupleAlmostEquals((a1 @ 1).to_tuple(), (1, 1, 0), 5) - self.assertTupleAlmostEquals((a1 @ 1).to_tuple(), (d1 @ 1).to_tuple(), 5) + self.assertTupleAlmostEquals(a1 @ 1, (1, 1, 0), 5) + self.assertTupleAlmostEquals(a1 @ 1, d1 @ 1, 5) self.assertTrue(isinstance(a1, Edge)) self.assertTrue(isinstance(d1, Edge)) with BuildLine(): a2 = PolarLine((0, 0), 1, 30) d2 = PolarLine((0, 0), 1, direction=(sqrt(3), 1)) - self.assertTupleAlmostEquals((a2 @ 1).to_tuple(), (sqrt(3) / 2, 0.5, 0), 5) - self.assertTupleAlmostEquals((a2 @ 1).to_tuple(), (d2 @ 1).to_tuple(), 5) + self.assertTupleAlmostEquals(a2 @ 1, (sqrt(3) / 2, 0.5, 0), 5) + self.assertTupleAlmostEquals(a2 @ 1, d2 @ 1, 5) with BuildLine(): a3 = PolarLine((0, 0), 1, 150) d3 = PolarLine((0, 0), 1, direction=(-sqrt(3), 1)) - self.assertTupleAlmostEquals((a3 @ 1).to_tuple(), (-sqrt(3) / 2, 0.5, 0), 5) - self.assertTupleAlmostEquals((a3 @ 1).to_tuple(), (d3 @ 1).to_tuple(), 5) + self.assertTupleAlmostEquals(a3 @ 1, (-sqrt(3) / 2, 0.5, 0), 5) + self.assertTupleAlmostEquals(a3 @ 1, d3 @ 1, 5) with BuildLine(): a4 = PolarLine((0, 0), 1, angle=30, length_mode=LengthMode.HORIZONTAL) d4 = PolarLine( (0, 0), 1, direction=(sqrt(3), 1), length_mode=LengthMode.HORIZONTAL ) - self.assertTupleAlmostEquals((a4 @ 1).to_tuple(), (1, 1 / sqrt(3), 0), 5) - self.assertTupleAlmostEquals((a4 @ 1).to_tuple(), (d4 @ 1).to_tuple(), 5) + self.assertTupleAlmostEquals(a4 @ 1, (1, 1 / sqrt(3), 0), 5) + self.assertTupleAlmostEquals(a4 @ 1, d4 @ 1, 5) with BuildLine(Plane.XZ): a5 = PolarLine((0, 0), 1, angle=30, length_mode=LengthMode.VERTICAL) d5 = PolarLine( (0, 0), 1, direction=(sqrt(3), 1), length_mode=LengthMode.VERTICAL ) - self.assertTupleAlmostEquals((a5 @ 1).to_tuple(), (sqrt(3), 0, 1), 5) - self.assertTupleAlmostEquals((a5 @ 1).to_tuple(), (d5 @ 1).to_tuple(), 5) + self.assertTupleAlmostEquals(a5 @ 1, (sqrt(3), 0, 1), 5) + self.assertTupleAlmostEquals(a5 @ 1, d5 @ 1, 5) with self.assertRaises(ValueError): PolarLine((0, 0), 1) @@ -292,7 +290,7 @@ class BuildLineTests(unittest.TestCase): """Test spline with no tangents""" with BuildLine() as test: 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, (2, 0, 0), 5) self.assertTrue(isinstance(s1, Edge)) def test_radius_arc(self): @@ -333,19 +331,17 @@ class BuildLineTests(unittest.TestCase): """Test center arc as arc and circle""" with BuildLine() as arc: CenterArc((0, 0), 10, 0, 180) - self.assertTupleAlmostEquals((arc.edges()[0] @ 1).to_tuple(), (-10, 0, 0), 5) + self.assertTupleAlmostEquals(arc.edges()[0] @ 1, (-10, 0, 0), 5) with BuildLine() as arc: CenterArc((0, 0), 10, 0, 360) - self.assertTupleAlmostEquals( - (arc.edges()[0] @ 0).to_tuple(), (arc.edges()[0] @ 1).to_tuple(), 5 - ) + self.assertTupleAlmostEquals(arc.edges()[0] @ 0, arc.edges()[0] @ 1, 5) with BuildLine(Plane.XZ) as arc: CenterArc((0, 0), 10, 0, 360) self.assertTrue(Face(arc.wires()[0]).is_coplanar(Plane.XZ)) with BuildLine(Plane.XZ) as arc: CenterArc((-100, 0), 100, -45, 90) - self.assertTupleAlmostEquals((arc.edges()[0] @ 0.5).to_tuple(), (0, 0, 0), 5) + self.assertTupleAlmostEquals(arc.edges()[0] @ 0.5, (0, 0, 0), 5) arc = CenterArc((-100, 0), 100, 0, 360) self.assertTrue(Face(Wire([arc])).is_coplanar(Plane.XY)) @@ -729,6 +725,15 @@ class BuildLineTests(unittest.TestCase): self.assertGreater(side_sign * coincident_dir, 0) self.assertGreater(center_dir, 0) + # Verify arc is tangent for a reversed start arc + c1 = CenterArc((0, 80), 40, 0, -180) + c2 = CenterArc((80, 0), 40, 90, 180) + arc = ArcArcTangentArc(c1, c2, 25, side=Side.RIGHT) + _, _, point = c1.distance_to_with_closest_points(arc) + self.assertAlmostEqual( + c1.tangent_at(point).cross(arc.tangent_at(point)).length, 0, 5 + ) + ## Error Handling start_arc = CenterArc(start_point, start_r, 0, 360) end_arc = CenterArc(end_point, end_r, 0, 360) diff --git a/tests/test_build_part.py b/tests/test_build_part.py index f573847..d6fb66a 100644 --- a/tests/test_build_part.py +++ b/tests/test_build_part.py @@ -28,6 +28,8 @@ license: import unittest from math import pi, sin +from unittest.mock import MagicMock, patch + from build123d import * from build123d import LocationList, WorkplaneList @@ -56,7 +58,6 @@ class TestAlign(unittest.TestCase): class TestMakeBrakeFormed(unittest.TestCase): def test_make_brake_formed(self): - # TODO: Fix so this test doesn't raise a DeprecationWarning from NumPy with BuildPart() as bp: with BuildLine() as bl: Polyline((0, 0), (5, 6), (10, 1)) @@ -71,6 +72,67 @@ class TestMakeBrakeFormed(unittest.TestCase): self.assertAlmostEqual(sheet_metal.bounding_box().max.Z, 1, 2) +class TestPartOperationDraft(unittest.TestCase): + + def setUp(self): + self.box = Box(10, 10, 10).solid() + self.sides = self.box.faces().filter_by(Axis.Z, reverse=True) + self.bottom_face = self.box.faces().sort_by(Axis.Z)[0] + self.neutral_plane = Plane(self.bottom_face) + + def test_successful_draft(self): + """Test that a draft operation completes successfully""" + result = draft(self.sides, self.neutral_plane, 5) + self.assertIsInstance(result, Part) + self.assertLess(self.box.volume, result.volume) + + with BuildPart() as draft_box: + Box(10, 10, 10) + draft( + draft_box.faces().filter_by(Axis.Z, reverse=True), + Plane.XY.offset(-5), + 5, + ) + self.assertLess(draft_box.part.volume, 1000) + + def test_invalid_face_type(self): + """Test that a ValueError is raised for unsupported face types""" + torus = Torus(5, 1).solid() + with self.assertRaises(ValueError) as cm: + draft([torus.faces()[0]], self.neutral_plane, 5) + + def test_faces_from_multiple_solids(self): + """Test that using faces from different solids raises an error""" + box2 = Box(5, 5, 5).solid() + mixed = [self.sides[0], box2.faces()[0]] + with self.assertRaises(ValueError) as cm: + draft(mixed, self.neutral_plane, 5) + self.assertIn("same topological parent", str(cm.exception)) + + def test_faces_from_multiple_parts(self): + """Test that using faces from different solids raises an error""" + box2 = Box(5, 5, 5).solid() + part: Part = Part() + [self.box, Pos(X=10) * box2] + mixed = [part.faces().sort_by(Axis.X)[0], part.faces().sort_by(Axis.X)[-1]] + with self.assertRaises(ValueError) as cm: + draft(mixed, self.neutral_plane, 5) + + def test_bad_draft_faces(self): + with self.assertRaises(DraftAngleError): + draft(self.bottom_face, self.neutral_plane, 10) + + @patch("build123d.topology.three_d.BRepOffsetAPI_DraftAngle") + def test_draftangleerror_from_solid_draft(self, mock_draft_angle): + """Simulate a failure in AddDone and catch DraftAngleError""" + mock_builder = MagicMock() + mock_builder.AddDone.return_value = False + mock_builder.ProblematicShape.return_value = "ShapeX" + mock_draft_angle.return_value = mock_builder + + with self.assertRaises(DraftAngleError) as cm: + draft(self.sides, self.neutral_plane, 5) + + class TestBuildPart(unittest.TestCase): """Test the BuildPart Builder derived class""" @@ -171,7 +233,7 @@ class TestBuildPart(unittest.TestCase): def test_named_plane(self): with BuildPart(Plane.YZ) as test: self.assertTupleAlmostEquals( - WorkplaneList._get_context().workplanes[0].z_dir.to_tuple(), + WorkplaneList._get_context().workplanes[0].z_dir, (1, 0, 0), 5, ) diff --git a/tests/test_build_sketch.py b/tests/test_build_sketch.py index d57fbf7..c00a504 100644 --- a/tests/test_build_sketch.py +++ b/tests/test_build_sketch.py @@ -92,9 +92,7 @@ class TestBuildSketch(unittest.TestCase): with BuildLine(): l1 = Line((0, 0), (10, 0)) Line(l1 @ 1, (10, 10)) - self.assertTupleAlmostEquals( - (test.consolidate_edges() @ 1).to_tuple(), (10, 10, 0), 5 - ) + self.assertTupleAlmostEquals(test.consolidate_edges() @ 1, (10, 10, 0), 5) def test_mode_intersect(self): with BuildSketch() as test: @@ -224,6 +222,11 @@ class TestBuildSketchObjects(unittest.TestCase): self.assertAlmostEqual(test.sketch.area, 0.5, 5) self.assertEqual(p.faces()[0].normal_at(), Vector(0, 0, 1)) + # test iterable input + points_nervure = [(0.0, 0.0), (10.0, 0.0), (0.0, 5.0)] + riri = Polygon(points_nervure, align=Align.NONE) + self.assertEqual(len(riri.vertices()), 3) + def test_rectangle(self): with BuildSketch() as test: r = Rectangle(20, 10) @@ -263,9 +266,7 @@ class TestBuildSketchObjects(unittest.TestCase): self.assertEqual(r.align, (Align.CENTER, Align.CENTER)) self.assertEqual(r.mode, Mode.ADD) self.assertAlmostEqual(test.sketch.area, (3 * sqrt(3) / 2) * 2**2, 5) - self.assertTupleAlmostEquals( - test.sketch.faces()[0].normal_at().to_tuple(), (0, 0, 1), 5 - ) + self.assertTupleAlmostEquals(test.sketch.faces()[0].normal_at(), (0, 0, 1), 5) self.assertAlmostEqual(r.apothem, 2 * sqrt(3) / 2) def test_regular_polygon_minor_radius(self): @@ -277,9 +278,7 @@ class TestBuildSketchObjects(unittest.TestCase): self.assertEqual(r.align, (Align.CENTER, Align.CENTER)) self.assertEqual(r.mode, Mode.ADD) self.assertAlmostEqual(test.sketch.area, (3 * sqrt(3) / 4) * (0.5 * 2) ** 2, 5) - self.assertTupleAlmostEquals( - test.sketch.faces()[0].normal_at().to_tuple(), (0, 0, 1), 5 - ) + self.assertTupleAlmostEquals(test.sketch.faces()[0].normal_at(), (0, 0, 1), 5) def test_regular_polygon_align(self): with BuildSketch() as align: @@ -303,7 +302,7 @@ class TestBuildSketchObjects(unittest.TestCase): poly_pts = [Vector(v) for v in regular_poly.vertices()] polar_pts = [p.position for p in PolarLocations(1, side_count)] for poly_pt, polar_pt in zip(poly_pts, polar_pts): - self.assertTupleAlmostEquals(poly_pt.to_tuple(), polar_pt.to_tuple(), 5) + self.assertTupleAlmostEquals(poly_pt, polar_pt, 5) def test_regular_polygon_min_sides(self): with self.assertRaises(ValueError): @@ -325,8 +324,8 @@ class TestBuildSketchObjects(unittest.TestCase): def test_slot_center_point(self): with BuildSketch() as test: s = SlotCenterPoint((0, 0), (2, 0), 2) - self.assertTupleAlmostEquals(s.slot_center.to_tuple(), (0, 0, 0), 5) - self.assertTupleAlmostEquals(s.point.to_tuple(), (2, 0, 0), 5) + self.assertTupleAlmostEquals(s.slot_center, (0, 0, 0), 5) + self.assertTupleAlmostEquals(s.point, (2, 0, 0), 5) self.assertEqual(s.slot_height, 2) self.assertEqual(s.rotation, 0) self.assertEqual(s.mode, Mode.ADD) @@ -334,25 +333,39 @@ class TestBuildSketchObjects(unittest.TestCase): self.assertEqual(s.faces()[0].normal_at(), Vector(0, 0, 1)) def test_slot_center_to_center(self): + height = 2 with BuildSketch() as test: - s = SlotCenterToCenter(4, 2) + s = SlotCenterToCenter(4, height) self.assertEqual(s.center_separation, 4) - self.assertEqual(s.slot_height, 2) + self.assertEqual(s.slot_height, height) self.assertEqual(s.rotation, 0) self.assertEqual(s.mode, Mode.ADD) - self.assertAlmostEqual(test.sketch.area, pi + 4 * 2, 5) + self.assertAlmostEqual(test.sketch.area, pi + 4 * height, 5) self.assertEqual(s.faces()[0].normal_at(), Vector(0, 0, 1)) + # Circle degenerate + s1 = SlotCenterToCenter(0, height) + self.assertTrue(len(s1.edges()) == 1) + self.assertEqual(s1.edge().geom_type, GeomType.CIRCLE) + self.assertAlmostEqual(s1.edge().radius, height / 2) + def test_slot_overall(self): + height = 2 with BuildSketch() as test: - s = SlotOverall(6, 2) + s = SlotOverall(6, height) self.assertEqual(s.width, 6) - self.assertEqual(s.slot_height, 2) + self.assertEqual(s.slot_height, height) self.assertEqual(s.rotation, 0) self.assertEqual(s.mode, Mode.ADD) - self.assertAlmostEqual(test.sketch.area, pi + 4 * 2, 5) + self.assertAlmostEqual(test.sketch.area, pi + 4 * height, 5) self.assertEqual(s.faces()[0].normal_at(), Vector(0, 0, 1)) + # Circle degenerat + s1 = SlotOverall(2, height) + self.assertTrue(len(s1.edges()) == 1) + self.assertEqual(s1.edge().geom_type, GeomType.CIRCLE) + self.assertAlmostEqual(s1.edge().radius, height / 2) + def test_text(self): with BuildSketch() as test: t = Text("test", 2) @@ -419,6 +432,9 @@ class TestBuildSketchObjects(unittest.TestCase): self.assertTupleAlmostEquals(tri.vertex_A, (3, 4, 0), 5) self.assertTupleAlmostEquals(tri.vertex_B, (0, 0, 0), 5) self.assertTupleAlmostEquals(tri.vertex_C, (3, 0, 0), 5) + self.assertEqual(tri.vertex_A.topo_parent, tri) + self.assertEqual(tri.vertex_B.topo_parent, tri) + self.assertEqual(tri.vertex_C.topo_parent, tri) tri = Triangle(c=5, C=90, a=3) self.assertAlmostEqual(tri.area, (3 * 4) / 2, 5) @@ -525,7 +541,7 @@ class TestBuildSketchObjects(unittest.TestCase): self.assertLess(tri_round.area, tri.area) # Test flipping the face - flipped = -Rectangle(34, 10).face() + flipped = -Face.make_rect(34, 10) rounded = full_round((flipped.edges() << Axis.X)[0]).face() self.assertEqual(flipped.normal_at(), rounded.normal_at()) @@ -533,9 +549,9 @@ class TestBuildSketchObjects(unittest.TestCase): @pytest.mark.parametrize( "slot,args", [ - (SlotOverall, (5, 10)), + (SlotOverall, (9, 10)), (SlotCenterToCenter, (-1, 10)), - (SlotCenterPoint, ((0, 0, 0), (2, 0, 0), 10)), + (SlotCenterPoint, ((0, 0, 0), (0, 0, 0), 10)), ], ) def test_invalid_slots(slot, args): diff --git a/tests/test_direct_api/test_axis.py b/tests/test_direct_api/test_axis.py index bdc921e..c0bbd46 100644 --- a/tests/test_direct_api/test_axis.py +++ b/tests/test_direct_api/test_axis.py @@ -33,7 +33,7 @@ import unittest import numpy as np from OCP.gp import gp_Ax1, gp_Dir, gp_Pnt from build123d.geometry import Axis, Location, Plane, Vector -from build123d.topology import Edge +from build123d.topology import Edge, Vertex class AlwaysEqual: @@ -65,10 +65,18 @@ class TestAxis(unittest.TestCase): self.assertAlmostEqual(test_axis.position, (1, 2, 3), 5) self.assertAlmostEqual(test_axis.direction, (0, 0, 1), 5) + with self.assertRaises(ValueError): + Axis("one") with self.assertRaises(ValueError): Axis("one", "up") with self.assertRaises(ValueError): Axis(one="up") + with self.assertRaises(ValueError): + bad_edge = Edge() + bad_edge.wrapped = Vertex(0, 1, 2).wrapped + Axis(edge=bad_edge) + with self.assertRaises(ValueError): + Axis(gp_ax1=Edge.make_line((0, 0), (1, 0))) def test_axis_from_occt(self): occt_axis = gp_Ax1(gp_Pnt(1, 1, 1), gp_Dir(0, 1, 0)) @@ -100,11 +108,16 @@ class TestAxis(unittest.TestCase): self.assertAlmostEqual(y_axis.position, (0, 0, 1), 5) self.assertAlmostEqual(y_axis.direction, (0, 1, 0), 5) - def test_axis_to_plane(self): - x_plane = Axis.X.to_plane() - self.assertTrue(isinstance(x_plane, Plane)) - self.assertAlmostEqual(x_plane.origin, (0, 0, 0), 5) - self.assertAlmostEqual(x_plane.z_dir, (1, 0, 0), 5) + def test_from_location(self): + axis = Axis(Location((1, 2, 3), (-90, 0, 0))) + self.assertAlmostEqual(axis.position, (1, 2, 3), 6) + self.assertAlmostEqual(axis.direction, (0, 1, 0), 6) + + # def test_axis_to_plane(self): + # x_plane = Axis.X.to_plane() + # self.assertTrue(isinstance(x_plane, Plane)) + # self.assertAlmostEqual(x_plane.origin, (0, 0, 0), 5) + # self.assertAlmostEqual(x_plane.z_dir, (1, 0, 0), 5) def test_axis_is_coaxial(self): self.assertTrue(Axis.X.is_coaxial(Axis((0, 0, 0), (1, 0, 0)))) @@ -179,7 +192,7 @@ class TestAxis(unittest.TestCase): self.assertIsNone(Axis.X.intersect(Axis((0, 1, 1), (0, 0, 1)))) intersection = Axis((1, 2, 3), (0, 0, 1)) & Plane.XY - self.assertAlmostEqual(intersection.to_tuple(), (1, 2, 0), 5) + self.assertAlmostEqual(intersection, (1, 2, 0), 5) arc = Edge.make_circle(20, start_angle=0, end_angle=180) ax0 = Axis((-20, 30, 0), (4, -3, 0)) @@ -213,10 +226,10 @@ class TestAxis(unittest.TestCase): # self.assertTrue(len(intersections.vertices(), 2)) # np.testing.assert_allclose( - # intersection.vertices()[0].to_tuple(), (-1, 0, 5), 5 + # intersection.vertices()[0], (-1, 0, 5), 5 # ) # np.testing.assert_allclose( - # intersection.vertices()[1].to_tuple(), (1, 0, 5), 5 + # intersection.vertices()[1], (1, 0, 5), 5 # ) def test_axis_equal(self): diff --git a/tests/test_direct_api/test_compound.py b/tests/test_direct_api/test_compound.py index e4eb6f2..9f93460 100644 --- a/tests/test_direct_api/test_compound.py +++ b/tests/test_direct_api/test_compound.py @@ -51,10 +51,10 @@ class TestCompound(unittest.TestCase): box1 = Solid.make_box(1, 1, 1) box2 = Solid.make_box(1, 1, 1, Plane((1, 0, 0))) combined = Compound([box1]).fuse(box2, glue=True) - self.assertTrue(combined.is_valid()) + self.assertTrue(combined.is_valid) self.assertAlmostEqual(combined.volume, 2, 5) fuzzy = Compound([box1]).fuse(box2, tol=1e-6) - self.assertTrue(fuzzy.is_valid()) + self.assertTrue(fuzzy.is_valid) self.assertAlmostEqual(fuzzy.volume, 2, 5) def test_remove(self): diff --git a/tests/test_direct_api/test_edge.py b/tests/test_direct_api/test_edge.py index 292b94e..8599c44 100644 --- a/tests/test_direct_api/test_edge.py +++ b/tests/test_direct_api/test_edge.py @@ -27,6 +27,7 @@ license: """ import math +import numpy as np import unittest from unittest.mock import patch, PropertyMock @@ -36,7 +37,7 @@ from build123d.geometry import Axis, Plane, Vector from build123d.objects_curve import CenterArc, EllipticalCenterArc from build123d.objects_sketch import Circle, Rectangle, RegularPolygon from build123d.operations_generic import sweep -from build123d.topology import Edge, Face +from build123d.topology import Edge, Face, Wire from OCP.GeomProjLib import GeomProjLib @@ -121,7 +122,7 @@ class TestEdge(unittest.TestCase): for end in [0, 1]: self.assertAlmostEqual( edge.position_at(end), - edge.to_wire().position_at(end), + Wire(edge).position_at(end), 5, ) @@ -233,7 +234,7 @@ class TestEdge(unittest.TestCase): for i, loc in enumerate(locs): self.assertAlmostEqual( loc.position, - Vector(1, 0, 0).rotate(Axis.Z, i * 90).to_tuple(), + Vector(1, 0, 0).rotate(Axis.Z, i * 90), 5, ) self.assertAlmostEqual(loc.orientation, (0, 0, 0), 5) @@ -272,6 +273,27 @@ class TestEdge(unittest.TestCase): with self.assertRaises(ValueError): edge.param_at_point((-1, 1)) + def test_param_at_point_bspline(self): + # Define a complex spline with inflections and non-monotonic behavior + curve = Edge.make_spline( + [ + (-2, 0, 0), + (-10, 1, 0), + (0, 0, 0), + (1, -2, 0), + (2, 0, 0), + (1, 1, 0), + ] + ) + + # Sample N points along the curve using position_at and check that + # param_at_point returns approximately the same param (inverted) + N = 20 + for u in np.linspace(0.0, 1.0, N): + p = curve.position_at(u) + u_back = curve.param_at_point(p) + self.assertAlmostEqual(u, u_back, delta=1e-6, msg=f"u={u}, u_back={u_back}") + def test_conical_helix(self): helix = Edge.make_helix(1, 4, 1, normal=(-1, 0, 0), angle=10, lefthand=True) self.assertAlmostEqual(helix.bounding_box().min.X, -4, 5) diff --git a/tests/test_direct_api/test_face.py b/tests/test_direct_api/test_face.py index 0bde6c6..7bf2f5f 100644 --- a/tests/test_direct_api/test_face.py +++ b/tests/test_direct_api/test_face.py @@ -50,6 +50,7 @@ from build123d.objects_sketch import ( Polygon, Rectangle, RegularPolygon, + Text, Triangle, ) from build123d.operations_generic import fillet, offset @@ -64,7 +65,7 @@ class TestFace(unittest.TestCase): bottom_edge = Edge.make_circle(radius=1, end_angle=90) top_edge = Edge.make_circle(radius=1, plane=Plane((0, 0, 1)), end_angle=90) curved = Face.make_surface_from_curves(bottom_edge, top_edge) - self.assertTrue(curved.is_valid()) + self.assertTrue(curved.is_valid) self.assertAlmostEqual(curved.area, math.pi / 2, 5) self.assertAlmostEqual( curved.normal_at(), (math.sqrt(2) / 2, math.sqrt(2) / 2, 0), 5 @@ -73,7 +74,7 @@ class TestFace(unittest.TestCase): bottom_wire = Wire.make_circle(1) top_wire = Wire.make_circle(1, Plane((0, 0, 1))) curved = Face.make_surface_from_curves(bottom_wire, top_wire) - self.assertTrue(curved.is_valid()) + self.assertTrue(curved.is_valid) self.assertAlmostEqual(curved.area, 2 * math.pi, 5) def test_center(self): @@ -168,6 +169,13 @@ class TestFace(unittest.TestCase): flipped_square = -square self.assertAlmostEqual(flipped_square.normal_at(), (0, 0, -1), 5) + # Ensure the topo_parent is cleared when a face is negated + # (otherwise the original Rectangle would be the topo_parent) + flipped = -Rectangle(34, 10).face() + left_edge = flipped.edges().sort_by(Axis.X)[0] + parent_face = left_edge.topo_parent + self.assertAlmostEqual(flipped.normal_at(), parent_face.normal_at(), 5) + def test_offset(self): bbox = Face.make_rect(2, 2, Plane.XY).offset(5).bounding_box() self.assertAlmostEqual(bbox.min, (-1, -1, 5), 5) @@ -182,7 +190,7 @@ class TestFace(unittest.TestCase): happy = Face(outer, inners) self.assertAlmostEqual(happy.area, math.pi * (10**2 - 2), 5) - outer = Edge.make_circle(10, end_angle=180).to_wire() + outer = Wire(Edge.make_circle(10, end_angle=180)) with self.assertRaises(ValueError): Face(outer, inners) with self.assertRaises(ValueError): @@ -191,7 +199,7 @@ class TestFace(unittest.TestCase): outer = Wire.make_circle(10) inners = [ Wire.make_circle(1).locate(Location((-2, 2, 0))), - Edge.make_circle(1, end_angle=180).to_wire().locate(Location((2, 2, 0))), + Wire(Edge.make_circle(1, end_angle=180)).locate(Location((2, 2, 0))), ] with self.assertRaises(ValueError): Face(outer, inners) @@ -302,7 +310,7 @@ class TestFace(unittest.TestCase): for j in range(4 - i % 2) ] cylinder_walls_with_holes = cylinder_wall.make_holes(projected_wires) - self.assertTrue(cylinder_walls_with_holes.is_valid()) + self.assertTrue(cylinder_walls_with_holes.is_valid) self.assertLess(cylinder_walls_with_holes.area, cylinder_wall.area) def test_is_inside(self): @@ -376,7 +384,7 @@ class TestFace(unittest.TestCase): surface_points=[Vector(0, 0, -5)], interior_wires=[hole], ) - self.assertTrue(surface.is_valid()) + self.assertTrue(surface.is_valid) self.assertEqual(surface.geom_type, GeomType.BSPLINE) bbox = surface.bounding_box() self.assertAlmostEqual(bbox.min, (-50.5, -24.5, -5.113393280136395), 5) @@ -422,15 +430,15 @@ class TestFace(unittest.TestCase): with self.assertRaises(ValueError): Face.sweep(edge, Polyline((0, 0), (0.1, 0), (0.2, 0.1))) - def test_to_arcs(self): - with BuildSketch() as bs: - with BuildLine() as bl: - Polyline((0, 0), (1, 0), (1.5, 0.5), (2, 0), (2, 1), (0, 1), (0, 0)) - fillet(bl.vertices(), radius=0.1) - make_face() - smooth = bs.faces()[0] - fragmented = smooth.to_arcs() - self.assertLess(len(smooth.edges()), len(fragmented.edges())) + # def test_to_arcs(self): + # with BuildSketch() as bs: + # with BuildLine() as bl: + # Polyline((0, 0), (1, 0), (1.5, 0.5), (2, 0), (2, 1), (0, 1), (0, 0)) + # fillet(bl.vertices(), radius=0.1) + # make_face() + # smooth = bs.faces()[0] + # fragmented = smooth.to_arcs() + # self.assertLess(len(smooth.edges()), len(fragmented.edges())) def test_outer_wire(self): face = (Face.make_rect(1, 1) - Face.make_rect(0.5, 0.5)).face() @@ -457,6 +465,37 @@ class TestFace(unittest.TestCase): face = Cylinder(1, 1).faces().filter_by(GeomType.CYLINDER)[0] self.assertAlmostEqual(face.normal_at(0, 1), (1, 0, 0), 5) + def test_location_at(self): + face = Face.make_rect(1, 1) + + # Default center (u=0, v=0) + loc = face.location_at(0, 0) + self.assertAlmostEqual(loc.position, (-0.5, -0.5, 0), 5) + self.assertAlmostEqual(loc.z_axis.direction, (0, 0, 1), 5) + + # Using surface_point instead of u,v + point = face.position_at(0, 0) + loc2 = face.location_at(point) + self.assertAlmostEqual(loc2.position, (-0.5, -0.5, 0), 5) + self.assertAlmostEqual(loc2.z_axis.direction, (0, 0, 1), 5) + + # Bad args + with self.assertRaises(ValueError): + face.location_at(0) + with self.assertRaises(ValueError): + face.location_at(center=(0, 0)) + + # Curved surface: verify z-direction is outward normal + face = Cylinder(1, 1).faces().filter_by(GeomType.CYLINDER)[0] + loc3 = face.location_at(0, 1) + self.assertAlmostEqual(loc3.z_axis.direction, (1, 0, 0), 5) + + # Curved surface: verify center + face = Cylinder(1, 1).faces().filter_by(GeomType.CYLINDER)[0] + loc4 = face.location_at() + self.assertAlmostEqual(loc4.position, (-1, 0, 0), 5) + self.assertAlmostEqual(loc4.z_axis.direction, (-1, 0, 0), 5) + def test_without_holes(self): # Planar test frame = (Rectangle(1, 1) - Rectangle(0.5, 0.5)).face() @@ -876,7 +915,7 @@ class TestFace(unittest.TestCase): with self.assertRaises(RuntimeError): surface.wrap(star.outer_wire(), target) - @patch.object(Wire, "is_valid", return_value=False) + @patch.object(Wire, "is_valid", new_callable=PropertyMock, return_value=False) def test_wrap_invalid_wire(self, mock_is_valid): surface = Cone(5, 2, 10).faces().filter_by(GeomType.PLANE, reverse=True)[0] target = surface.location_at(0.5, 0.5, x_dir=(1, 0, 0)) @@ -888,6 +927,22 @@ class TestFace(unittest.TestCase): with self.assertRaises(RuntimeError): surface.wrap(star, target) + def test_wrap_faces(self): + sphere = Solid.make_sphere(50, angle1=-90).face() + surface = sphere.face() + path: Edge = ( + sphere.cut( + Solid.make_cylinder(80, 100, Plane.YZ).locate(Location((-50, 0, -70))) + ) + .edges() + .sort_by(Axis.Z)[0] + .reversed() + ) + text = Text(txt="ei", font_size=15, align=(Align.MIN, Align.CENTER)) + wrapped_faces = surface.wrap_faces(text.faces(), path, 0.2) + self.assertEqual(len(wrapped_faces), 3) + self.assertTrue(all(not f.is_planar_face for f in wrapped_faces)) + def test_revolve(self): l1 = Edge.make_line((3, 0), (3, 2)) revolved = Face.revolve(l1, 360, Axis.Y) diff --git a/tests/test_direct_api/test_import_export.py b/tests/test_direct_api/test_import_export.py index 9b22dd5..8f9f29e 100644 --- a/tests/test_direct_api/test_import_export.py +++ b/tests/test_direct_api/test_import_export.py @@ -40,11 +40,11 @@ class TestImportExport(unittest.TestCase): original_box = Solid.make_box(1, 1, 1) export_step(original_box, "test_box.step") step_box = import_step("test_box.step") - self.assertTrue(step_box.is_valid()) + self.assertTrue(step_box.is_valid) self.assertAlmostEqual(step_box.volume, 1, 5) export_brep(step_box, "test_box.brep") brep_box = import_brep("test_box.brep") - self.assertTrue(brep_box.is_valid()) + self.assertTrue(brep_box.is_valid) self.assertAlmostEqual(brep_box.volume, 1, 5) os.remove("test_box.step") os.remove("test_box.brep") diff --git a/tests/test_direct_api/test_json.py b/tests/test_direct_api/test_json.py index e253148..b18be83 100644 --- a/tests/test_direct_api/test_json.py +++ b/tests/test_direct_api/test_json.py @@ -27,7 +27,6 @@ license: """ import json -import os import unittest from build123d.geometry import ( Axis, @@ -52,7 +51,7 @@ class TestGeomEncode(unittest.TestCase): c_json = json.dumps(Color("red"), cls=GeomEncoder) color = json.loads(c_json, object_hook=GeomEncoder.geometry_hook) - self.assertEqual(Color("red").to_tuple(), color.to_tuple()) + self.assertEqual(tuple(Color("red")), tuple(color)) loc = Location((0, 1, 2), (4, 8, 16)) l_json = json.dumps(loc, cls=GeomEncoder) diff --git a/tests/test_direct_api/test_location.py b/tests/test_direct_api/test_location.py index 4d4298e..40f0e0a 100644 --- a/tests/test_direct_api/test_location.py +++ b/tests/test_direct_api/test_location.py @@ -26,7 +26,6 @@ license: """ -# Always equal to any other object, to test that __eq__ cooperation is working import copy import json import math @@ -34,7 +33,6 @@ import os import unittest from random import uniform -import numpy as np from OCP.gp import ( gp_Ax1, gp_Dir, @@ -51,6 +49,8 @@ from build123d.topology import Edge, Solid, Vertex class AlwaysEqual: + """Always equal to any other object, to test that __eq__ cooperation is working""" + def __eq__(self, other): return True @@ -59,7 +59,7 @@ class TestLocation(unittest.TestCase): def test_location(self): loc0 = Location() T = loc0.wrapped.Transformation().TranslationPart() - np.testing.assert_allclose((T.X(), T.Y(), T.Z()), (0, 0, 0), 1e-6) + self.assertAlmostEqual((T.X(), T.Y(), T.Z()), (0, 0, 0), 5) angle = math.degrees( loc0.wrapped.Transformation().GetRotation().GetRotationAngle() ) @@ -69,19 +69,19 @@ class TestLocation(unittest.TestCase): loc0 = Location((0, 0, 1)) T = loc0.wrapped.Transformation().TranslationPart() - np.testing.assert_allclose((T.X(), T.Y(), T.Z()), (0, 0, 1), 1e-6) + self.assertAlmostEqual((T.X(), T.Y(), T.Z()), (0, 0, 1), 5) # List loc0 = Location([0, 0, 1]) T = loc0.wrapped.Transformation().TranslationPart() - np.testing.assert_allclose((T.X(), T.Y(), T.Z()), (0, 0, 1), 1e-6) + self.assertAlmostEqual((T.X(), T.Y(), T.Z()), (0, 0, 1), 5) # Vector loc1 = Location(Vector(0, 0, 1)) T = loc1.wrapped.Transformation().TranslationPart() - np.testing.assert_allclose((T.X(), T.Y(), T.Z()), (0, 0, 1), 1e-6) + self.assertAlmostEqual((T.X(), T.Y(), T.Z()), (0, 0, 1), 5) # rotation + translation loc2 = Location(Vector(0, 0, 1), Vector(0, 0, 1), 45) @@ -103,13 +103,8 @@ class TestLocation(unittest.TestCase): # Test creation from the OCP.gp.gp_Trsf object loc4 = Location(gp_Trsf()) - np.testing.assert_allclose(loc4.to_tuple()[0], (0, 0, 0), 1e-7) - np.testing.assert_allclose(loc4.to_tuple()[1], (0, 0, 0), 1e-7) - - # Test creation from Plane and Vector - loc4 = Location(Plane.XY, (0, 0, 1)) - np.testing.assert_allclose(loc4.to_tuple()[0], (0, 0, 1), 1e-7) - np.testing.assert_allclose(loc4.to_tuple()[1], (0, 0, 0), 1e-7) + self.assertAlmostEqual(tuple(loc4)[0], (0, 0, 0), 5) + self.assertAlmostEqual(tuple(loc4)[1], (0, 0, 0), 5) # Test composition loc4 = Location((0, 0, 0), Vector(0, 0, 1), 15) @@ -119,7 +114,7 @@ class TestLocation(unittest.TestCase): loc7 = loc4**2 T = loc5.wrapped.Transformation().TranslationPart() - np.testing.assert_allclose((T.X(), T.Y(), T.Z()), (0, 0, 1), 1e-6) + self.assertAlmostEqual((T.X(), T.Y(), T.Z()), (0, 0, 1), 5) angle5 = math.degrees( loc5.wrapped.Transformation().GetRotation().GetRotationAngle() @@ -165,21 +160,46 @@ class TestLocation(unittest.TestCase): t.SetRotationPart(q) loc2 = Location(t) - np.testing.assert_allclose(loc1.to_tuple()[0], loc2.to_tuple()[0], 1e-6) - np.testing.assert_allclose(loc1.to_tuple()[1], loc2.to_tuple()[1], 1e-6) + self.assertAlmostEqual(tuple(loc1)[0], tuple(loc2)[0], 5) + self.assertAlmostEqual(tuple(loc1)[1], tuple(loc2)[1], 5) loc1 = Location((1, 2), 34) - np.testing.assert_allclose(loc1.to_tuple()[0], (1, 2, 0), 1e-6) - np.testing.assert_allclose(loc1.to_tuple()[1], (0, 0, 34), 1e-6) + self.assertAlmostEqual(tuple(loc1)[0], (1, 2, 0), 5) + self.assertAlmostEqual(tuple(loc1)[1], (0, 0, 34), 5) rot_angles = (-115.00, 35.00, -135.00) loc2 = Location((1, 2, 3), rot_angles) - np.testing.assert_allclose(loc2.to_tuple()[0], (1, 2, 3), 1e-6) - np.testing.assert_allclose(loc2.to_tuple()[1], rot_angles, 1e-6) + self.assertAlmostEqual(tuple(loc2)[0], (1, 2, 3), 5) + self.assertAlmostEqual(tuple(loc2)[1], rot_angles, 5) loc3 = Location(loc2) - np.testing.assert_allclose(loc3.to_tuple()[0], (1, 2, 3), 1e-6) - np.testing.assert_allclose(loc3.to_tuple()[1], rot_angles, 1e-6) + self.assertAlmostEqual(tuple(loc3)[0], (1, 2, 3), 5) + self.assertAlmostEqual(tuple(loc3)[1], rot_angles, 5) + + def test_location_kwarg_parameters(self): + loc = Location(position=(10, 20, 30)) + self.assertAlmostEqual(loc.position, (10, 20, 30), 5) + + loc = Location(position=(10, 20, 30), orientation=(10, 20, 30)) + self.assertAlmostEqual(loc.position, (10, 20, 30), 5) + self.assertAlmostEqual(loc.orientation, (10, 20, 30), 5) + + loc = Location( + position=(10, 20, 30), orientation=(90, 0, 90), ordering=Extrinsic.XYZ + ) + self.assertAlmostEqual(loc.position, (10, 20, 30), 5) + self.assertAlmostEqual(loc.orientation, (0, 90, 90), 5) + + loc = Location((10, 20, 30), orientation=(10, 20, 30)) + self.assertAlmostEqual(loc.position, (10, 20, 30), 5) + self.assertAlmostEqual(loc.orientation, (10, 20, 30), 5) + + loc = Location(plane=Plane.isometric) + self.assertAlmostEqual(loc.position, (0, 0, 0), 5) + self.assertAlmostEqual(loc.orientation, (45.00, 35.26, 30.00), 2) + + loc = Location(location=Location()) + self.assertAlmostEqual(loc.position, (0, 0, 0), 5) def test_location_parameters(self): loc = Location((10, 20, 30)) @@ -240,15 +260,16 @@ class TestLocation(unittest.TestCase): loc1 = Location((1, 2, 3), (90, 45, 22.5)) loc2 = copy.copy(loc1) loc3 = copy.deepcopy(loc1) - self.assertAlmostEqual(loc1.position, loc2.position.to_tuple(), 6) - self.assertAlmostEqual(loc1.orientation, loc2.orientation.to_tuple(), 6) - self.assertAlmostEqual(loc1.position, loc3.position.to_tuple(), 6) - self.assertAlmostEqual(loc1.orientation, loc3.orientation.to_tuple(), 6) + self.assertAlmostEqual(loc1.position, loc2.position, 6) + self.assertAlmostEqual(loc1.orientation, loc2.orientation, 6) + self.assertAlmostEqual(loc1.position, loc3.position, 6) + self.assertAlmostEqual(loc1.orientation, loc3.orientation, 6) - def test_to_axis(self): - axis = Location((1, 2, 3), (-90, 0, 0)).to_axis() - self.assertAlmostEqual(axis.position, (1, 2, 3), 6) - self.assertAlmostEqual(axis.direction, (0, 1, 0), 6) + # deprecated + # def test_to_axis(self): + # axis = Location((1, 2, 3), (-90, 0, 0)).to_axis() + # self.assertAlmostEqual(axis.position, (1, 2, 3), 6) + # self.assertAlmostEqual(axis.direction, (0, 1, 0), 6) def test_equal(self): loc = Location((1, 2, 3), (4, 5, 6)) diff --git a/tests/test_direct_api/test_mixin1_d.py b/tests/test_direct_api/test_mixin1_d.py index 106c805..df8f2d4 100644 --- a/tests/test_direct_api/test_mixin1_d.py +++ b/tests/test_direct_api/test_mixin1_d.py @@ -40,7 +40,9 @@ from build123d.build_enums import ( from build123d.geometry import Axis, Location, Plane, Vector from build123d.objects_curve import Polyline from build123d.objects_part import Box, Cylinder -from build123d.topology import Compound, Edge, Face, Wire +from build123d.operations_part import extrude +from build123d.operations_generic import fillet +from build123d.topology import Compound, Edge, Face, Solid, Wire class TestMixin1D(unittest.TestCase): @@ -53,10 +55,8 @@ class TestMixin1D(unittest.TestCase): 5, ) # Not sure what PARAMETER mode returns - but it's in the ballpark - point = ( - Edge.make_line((0, 0, 0), (1, 1, 1)) - .position_at(0.5, position_mode=PositionMode.PARAMETER) - .to_tuple() + point = Edge.make_line((0, 0, 0), (1, 1, 1)).position_at( + 0.5, position_mode=PositionMode.PARAMETER ) self.assertTrue(all([0.0 < v < 1.0 for v in point])) @@ -119,10 +119,8 @@ class TestMixin1D(unittest.TestCase): (-1, 0, 0), 5, ) - tangent = ( - Edge.make_circle(1, start_angle=0, end_angle=90) - .tangent_at(0.0, position_mode=PositionMode.PARAMETER) - .to_tuple() + tangent = Edge.make_circle(1, start_angle=0, end_angle=90).tangent_at( + 0.0, position_mode=PositionMode.PARAMETER ) self.assertTrue(all([0.0 <= v <= 1.0 for v in tangent])) @@ -364,6 +362,29 @@ class TestMixin1D(unittest.TestCase): wire = Wire.make_rect(1, 1) self.assertAlmostEqual(wire.volume, 0, 5) + def test_edges(self): + box = Solid.make_box(1, 1, 1) + top_x = box.faces().sort_by(Axis.Z)[-1].edges().sort_by(Axis.X)[-1] + self.assertEqual(top_x.topo_parent, box) + self.assertTrue(isinstance(top_x, Edge)) + self.assertAlmostEqual(top_x.center(), (1, 0.5, 1), 5) + + def test_edges_topo_parent(self): + phone_case_plan = Face.make_rect(80, 150) - Face.make_rect( + 25, 25, Plane((-20, 55)) + ) + phone_case = extrude(phone_case_plan, 2) + window_edges = phone_case.faces().sort_by(Axis.Z)[-1].inner_wires()[0].edges() + for e in window_edges: + self.assertEqual(e.topo_parent, phone_case) + phone_case_f = fillet(window_edges, 1) + self.assertLess(phone_case_f.volume, phone_case.volume) + perimeter = phone_case_f.faces().sort_by(Axis.Z)[-1].outer_wire().edges() + for e in perimeter: + self.assertEqual(e.topo_parent, phone_case_f) + phone_case_ff = fillet(perimeter, 1) + self.assertLess(phone_case_ff.volume, phone_case_f.volume) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_direct_api/test_mixin3_d.py b/tests/test_direct_api/test_mixin3_d.py index 5e04e7b..1bee8fc 100644 --- a/tests/test_direct_api/test_mixin3_d.py +++ b/tests/test_direct_api/test_mixin3_d.py @@ -27,7 +27,7 @@ license: """ import unittest -from unittest.mock import patch +from unittest.mock import patch, PropertyMock from build123d.build_enums import CenterOf, Kind from build123d.geometry import Axis, Plane @@ -67,7 +67,7 @@ class TestMixin3D(unittest.TestCase): face = box.faces().sort_by(Axis.Z)[0] self.assertRaises(ValueError, box.chamfer, 0.1, None, edge, face=face) - @patch.object(Shape, "is_valid", return_value=False) + @patch.object(Shape, "is_valid", new_callable=PropertyMock, return_value=False) def test_chamfer_invalid_shape_raises_error(self, mock_is_valid): box = Solid.make_box(1, 1, 1) @@ -111,7 +111,7 @@ class TestMixin3D(unittest.TestCase): d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism( None, [f], additive=False ) - self.assertTrue(d.is_valid()) + self.assertTrue(d.is_valid) self.assertAlmostEqual(d.volume, 1 - 0.5**2, 5) # face with depth @@ -119,7 +119,7 @@ class TestMixin3D(unittest.TestCase): d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism( None, [f], depth=0.5, thru_all=False, additive=False ) - self.assertTrue(d.is_valid()) + self.assertTrue(d.is_valid) self.assertAlmostEqual(d.volume, 1 - 0.5**3, 5) # face until @@ -128,7 +128,7 @@ class TestMixin3D(unittest.TestCase): d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism( None, [f], up_to_face=limit, thru_all=False, additive=False ) - self.assertTrue(d.is_valid()) + self.assertTrue(d.is_valid) self.assertAlmostEqual(d.volume, 1 - 0.5**3, 5) # wire @@ -136,7 +136,7 @@ class TestMixin3D(unittest.TestCase): d = Solid.make_box(1, 1, 1, Plane((-0.5, -0.5, 0))).dprism( None, [w], additive=False ) - self.assertTrue(d.is_valid()) + self.assertTrue(d.is_valid) self.assertAlmostEqual(d.volume, 1 - 0.5**2, 5) def test_center(self): diff --git a/tests/test_direct_api/test_plane.py b/tests/test_direct_api/test_plane.py index df4f31b..e9e7faa 100644 --- a/tests/test_direct_api/test_plane.py +++ b/tests/test_direct_api/test_plane.py @@ -123,6 +123,8 @@ class TestPlane(unittest.TestCase): Plane() with self.assertRaises(TypeError): Plane(o, z_dir="up") + with self.assertRaises(TypeError): + Plane(o, forward="up") # rotated location around z loc = Location((0, 0, 0), (0, 0, 45)) @@ -211,6 +213,54 @@ class TestPlane(unittest.TestCase): self.assertAlmostEqual(p.y_dir, expected[i][1], 6) self.assertAlmostEqual(p.z_dir, expected[i][2], 6) + def test_plane_from_axis(self): + origin = Vector(1, 2, 3) + direction = Vector(0, 0, 1) + axis = Axis(origin, direction) + plane = Plane(axis) + + self.assertEqual(plane.origin, origin) + self.assertTrue(plane.z_dir, direction.normalized()) + self.assertAlmostEqual(plane.x_dir.length, 1.0, places=12) + self.assertAlmostEqual(plane.y_dir.length, 1.0, places=12) + self.assertAlmostEqual(plane.z_dir.length, 1.0, places=12) + + def test_plane_from_axis_with_x_dir(self): + origin = Vector(0, 0, 0) + z_dir = Vector(0, 0, 1) + x_dir = Vector(1, 0, 0) + axis = Axis(origin, z_dir) + plane = Plane(axis, x_dir) + + self.assertEqual(plane.origin, origin) + self.assertEqual(plane.z_dir, z_dir.normalized()) + self.assertEqual(plane.x_dir, x_dir.normalized()) + self.assertEqual(plane.y_dir, z_dir.cross(x_dir).normalized()) + + def test_plane_from_axis_with_kwargs(self): + axis = Axis((0, 0, 0), (0, 1, 0)) + x_dir = Vector(1, 0, 0) + plane = Plane(axis=axis, x_dir=x_dir) + + self.assertEqual(plane.z_dir, Vector(0, 1, 0)) + self.assertEqual(plane.x_dir, x_dir.normalized()) + + def test_plane_from_axis_without_x_dir(self): + axis = Axis((0, 0, 0), (1, 0, 0)) + plane = Plane(axis) + + self.assertEqual(plane.z_dir, Vector(1, 0, 0)) + self.assertAlmostEqual(plane.x_dir.length, 1.0, places=12) + self.assertAlmostEqual(plane.y_dir.length, 1.0, places=12) + self.assertGreater(plane.z_dir.cross(plane.x_dir).dot(plane.y_dir), 0.99) + + def test_plane_from_axis_invalid_x_dir(self): + axis = Axis((0, 0, 0), (0, 0, 1)) + with self.assertRaises(ValueError): + Plane(axis, x_dir=(0, 0, 0)) + with self.assertRaises(TypeError): + Plane(axis, "front") + def test_plane_neg(self): p = Plane( origin=(1, 2, 3), @@ -273,11 +323,13 @@ class TestPlane(unittest.TestCase): np.testing.assert_allclose(target_point, local_box_vertices[i], 1e-7) def test_localize_vertex(self): - vertex = Vertex(random.random(), random.random(), random.random()) - np.testing.assert_allclose( - Plane.YZ.to_local_coords(vertex).to_tuple(), - Plane.YZ.to_local_coords(Vector(vertex)).to_tuple(), - 5, + v_x, v_y, v_z = (random.random(), random.random(), random.random()) + vertex = Vertex(v_x, v_y, v_z) + self.assertAlmostEqual( + Plane.YZ.to_local_coords(Vector(vertex)), (v_y, v_z, v_x), 5 + ) + self.assertAlmostEqual( + Vector(Plane.YZ.to_local_coords(vertex)), (v_y, v_z, v_x), 5 ) def test_repr(self): diff --git a/tests/test_direct_api/test_projection.py b/tests/test_direct_api/test_projection.py index 5fbb7bd..8b0da03 100644 --- a/tests/test_direct_api/test_projection.py +++ b/tests/test_direct_api/test_projection.py @@ -94,10 +94,6 @@ class TestProjection(unittest.TestCase): self.assertAlmostEqual(projection[0].position_at(0), (0, 1, 0), 5) self.assertAlmostEqual(projection[0].arc_center, (0, 0, 0), 5) - def test_to_axis(self): - with self.assertRaises(ValueError): - Edge.make_circle(1, end_angle=30).to_axis() - if __name__ == "__main__": unittest.main() diff --git a/tests/test_direct_api/test_shape.py b/tests/test_direct_api/test_shape.py index 835b70b..f31ee32 100644 --- a/tests/test_direct_api/test_shape.py +++ b/tests/test_direct_api/test_shape.py @@ -29,10 +29,11 @@ license: # Always equal to any other object, to test that __eq__ cooperation is working import unittest from random import uniform -from unittest.mock import patch +from unittest.mock import PropertyMock, patch import numpy as np -from build123d.build_enums import CenterOf, Keep +from anytree import PreOrderIter +from build123d.build_enums import CenterOf, GeomType, Keep from build123d.geometry import ( Axis, Color, @@ -43,7 +44,7 @@ from build123d.geometry import ( Rotation, Vector, ) -from build123d.objects_part import Box, Cylinder +from build123d.objects_part import Box, Cone, Cylinder, Sphere from build123d.objects_sketch import Circle from build123d.operations_part import extrude from build123d.topology import ( @@ -100,7 +101,7 @@ class TestShape(unittest.TestCase): Shape.combined_center(objs, center_of=CenterOf.GEOMETRY) def test_shape_type(self): - self.assertEqual(Vertex().shape_type(), "Vertex") + self.assertEqual(Vertex().shape_type, "Vertex") def test_scale(self): self.assertAlmostEqual(Solid.make_box(1, 1, 1).scale(2).volume, 2**3, 5) @@ -109,10 +110,10 @@ class TestShape(unittest.TestCase): box1 = Solid.make_box(1, 1, 1) box2 = Solid.make_box(1, 1, 1, Plane((1, 0, 0))) combined = box1.fuse(box2, glue=True) - self.assertTrue(combined.is_valid()) + self.assertTrue(combined.is_valid) self.assertAlmostEqual(combined.volume, 2, 5) fuzzy = box1.fuse(box2, tol=1e-6) - self.assertTrue(fuzzy.is_valid()) + self.assertTrue(fuzzy.is_valid) self.assertAlmostEqual(fuzzy.volume, 2, 5) def test_faces_intersected_by_axis(self): @@ -245,7 +246,7 @@ class TestShape(unittest.TestCase): # invalid_object = box.fillet(0.75, box.edges()) # invalid_object.max_fillet(invalid_object.edges()) - @patch.object(Shape, "is_valid", return_value=False) + @patch.object(Shape, "is_valid", new_callable=PropertyMock, return_value=False) def test_max_fillet_invalid_shape_raises_error(self, mock_is_valid): box = Solid.make_box(1, 1, 1) @@ -317,8 +318,8 @@ class TestShape(unittest.TestCase): c0 = Edge.make_circle(1).locate(Location((0, 2.1, 0))) c1 = Edge.make_circle(1) closest = c0.closest_points(c1) - self.assertAlmostEqual(closest[0], c0.position_at(0.75).to_tuple(), 5) - self.assertAlmostEqual(closest[1], c1.position_at(0.25).to_tuple(), 5) + self.assertAlmostEqual(closest[0], c0.position_at(0.75), 5) + self.assertAlmostEqual(closest[1], c1.position_at(0.25), 5) def test_distance_to(self): c0 = Edge.make_circle(1).locate(Location((0, 2.1, 0))) @@ -347,19 +348,19 @@ class TestShape(unittest.TestCase): obj = Solid() self.assertIs(obj, obj.clean()) - def test_relocate(self): - box = Solid.make_box(10, 10, 10).move(Location((20, -5, -5))) - cylinder = Solid.make_cylinder(2, 50).move(Location((0, 0, 0), (0, 90, 0))) + # def test_relocate(self): + # box = Solid.make_box(10, 10, 10).move(Location((20, -5, -5))) + # cylinder = Solid.make_cylinder(2, 50).move(Location((0, 0, 0), (0, 90, 0))) - box_with_hole = box.cut(cylinder) - box_with_hole.relocate(box.location) + # box_with_hole = box.cut(cylinder) + # box_with_hole.relocate(box.location) - self.assertEqual(box.location, box_with_hole.location) + # self.assertEqual(box.location, box_with_hole.location) - bbox1 = box.bounding_box() - bbox2 = box_with_hole.bounding_box() - self.assertAlmostEqual(bbox1.min, bbox2.min, 5) - self.assertAlmostEqual(bbox1.max, bbox2.max, 5) + # bbox1 = box.bounding_box() + # bbox2 = box_with_hole.bounding_box() + # self.assertAlmostEqual(bbox1.min, bbox2.min, 5) + # self.assertAlmostEqual(bbox1.max, bbox2.max, 5) def test_project_to_viewport(self): # Basic test @@ -459,44 +460,56 @@ class TestShape(unittest.TestCase): def test_ocp_section(self): # Vertex verts, edges = Vertex(1, 2, 0)._ocp_section(Vertex(1, 2, 0)) - self.assertListEqual(verts, []) # ? - self.assertListEqual(edges, []) + self.assertEqual(len(verts), 1) + self.assertEqual(len(edges), 0) + self.assertAlmostEqual(Vector(verts[0]), (1, 2, 0), 5) verts, edges = Vertex(1, 2, 0)._ocp_section(Edge.make_line((0, 0), (2, 4))) - self.assertListEqual(verts, []) # ? - self.assertListEqual(edges, []) + self.assertEqual(len(verts), 1) + self.assertEqual(len(edges), 0) + self.assertAlmostEqual(Vector(verts[0]), (1, 2, 0), 5) verts, edges = Vertex(1, 2, 0)._ocp_section(Face.make_rect(5, 5)) - np.testing.assert_allclose(tuple(verts[0]), (1, 2, 0), 1e-5) + self.assertAlmostEqual(Vector(verts[0]), (1, 2, 0), 5) self.assertListEqual(edges, []) verts, edges = Vertex(1, 2, 0)._ocp_section(Face.make_plane(Plane.XY)) - np.testing.assert_allclose(tuple(verts[0]), (1, 2, 0), 1e-5) + self.assertAlmostEqual(Vector(verts[0]), (1, 2, 0), 5) self.assertListEqual(edges, []) - # spline = Spline((-10, 10, -10), (-10, -5, -5), (20, 0, 5)) - # cylinder = Pos(Z=-10) * extrude(Circle(5), 20) - # cylinder2 = (Rot((0, 90, 0)) * cylinder).face() - # pln = Plane.XY - # box1 = Box(10, 10, 10, align=(Align.CENTER, Align.CENTER, Align.MIN)) - # box2 = Pos(Z=-10) * box1 + cylinder = Face.extrude(Edge.make_circle(5, Plane.XY.offset(-10)), (0, 0, 20)) + cylinder2 = Face.extrude(Edge.make_circle(5, Plane.YZ.offset(-10)), (20, 0, 0)) + pln = Plane.XY - # # vertices, edges = ocp_section(spline, Face.make_rect(1e6, 1e6, pln)) - # vertices1, edges1 = spline.ocp_section(Face.make_plane(pln)) - # print(vertices1, edges1) + v_edge = Edge.make_line((-5, 0, -20), (-5, 0, 20)) + vertices1, edges1 = cylinder._ocp_section(v_edge) + vertices1 = ShapeList(vertices1).sort_by(Axis.Z) + self.assertEqual(len(vertices1), 2) - # vertices2, edges2 = cylinder.ocp_section(Face.make_plane(pln)) - # print(vertices2, edges2) + self.assertAlmostEqual(Vector(vertices1[0]), (-5, 0, -10), 5) + self.assertAlmostEqual(Vector(vertices1[1]), (-5, 0, 10), 5) + self.assertEqual(len(edges1), 1) + self.assertAlmostEqual(edges1[0].length, 20, 5) - # vertices3, edges3 = cylinder2.ocp_section(Face.make_plane(pln)) - # print(vertices3, edges3) + vertices2, edges2 = cylinder._ocp_section(Face.make_plane(pln)) + self.assertEqual(len(vertices2), 1) + self.assertEqual(len(edges2), 1) + self.assertAlmostEqual(Vector(vertices2[0]), (5, 0, 0), 5) + self.assertEqual(edges2[0].geom_type, GeomType.CIRCLE) + self.assertAlmostEqual(edges2[0].radius, 5, 5) - # # vertices4, edges4 = cylinder2.ocp_section(cylinder) + vertices4, edges4 = cylinder2._ocp_section(cylinder) + self.assertGreaterEqual(len(vertices4), 0) + self.assertGreaterEqual(len(edges4), 2) + self.assertTrue(all(e.geom_type == GeomType.ELLIPSE for e in edges4)) - # vertices5, edges5 = box1.ocp_section(Face.make_plane(pln)) - # print(vertices5, edges5) + cylinder3 = Cylinder(5, 20).solid() + cylinder4 = Rotation(0, 90, 0) * cylinder3 - # vertices6, edges6 = box1.ocp_section(box2.faces().sort_by(Axis.Z)[-1]) + vertices5, edges5 = cylinder3._ocp_section(cylinder4) + self.assertGreaterEqual(len(vertices5), 0) + self.assertGreaterEqual(len(edges5), 2) + self.assertTrue(all(e.geom_type == GeomType.ELLIPSE for e in edges5)) def test_copy_attributes_to(self): box = Box(1, 1, 1) @@ -526,7 +539,7 @@ class TestShape(unittest.TestCase): self.assertEqual(hash(empty), 0) self.assertFalse(empty.is_same(Solid())) self.assertFalse(empty.is_equal(Solid())) - self.assertTrue(empty.is_valid()) + self.assertTrue(empty.is_valid) empty_bbox = empty.bounding_box() self.assertEqual(tuple(empty_bbox.size), (0, 0, 0)) self.assertIs(empty, empty.mirror(Plane.XY)) @@ -560,10 +573,10 @@ class TestShape(unittest.TestCase): empty.moved(Location()) with self.assertRaises(ValueError): box.moved(empty_loc) - with self.assertRaises(ValueError): - empty.relocate(Location()) - with self.assertRaises(ValueError): - box.relocate(empty_loc) + # with self.assertRaises(ValueError): + # empty.relocate(Location()) + # with self.assertRaises(ValueError): + # box.relocate(empty_loc) with self.assertRaises(ValueError): empty.distance_to(Vector(1, 1, 1)) with self.assertRaises(ValueError): @@ -615,5 +628,57 @@ class TestShape(unittest.TestCase): self.assertIsNone(Vertex(1, 1, 1).compound()) +class TestGlobalLocation(unittest.TestCase): + def test_global_location_hierarchy(self): + # Create a hierarchy: root → child → grandchild + root = Box(1, 1, 1) + root.location = Location((10, 0, 0)) + + child = Box(1, 1, 1) + child.location = Location((0, 20, 0)) + child.parent = root + + grandchild = Box(1, 1, 1) + grandchild.location = Location((0, 0, 30)) + grandchild.parent = child + + # Compute expected global location manually + expected_location = root.location * child.location * grandchild.location + + self.assertAlmostEqual( + grandchild.global_location.position, expected_location.position + ) + self.assertAlmostEqual( + grandchild.global_location.orientation, expected_location.orientation + ) + + def test_global_location_in_assembly(self): + cone = Cone(2, 1, 3) + cone.label = "Cone" + box = Box(1, 2, 3) + box.label = "Box" + sphere = Sphere(1) + sphere.label = "Sphere" + + assembly1 = Compound(label="Assembly1", children=[cone]) + assembly1.move(Location((3, 3, 3), (90, 0, 0))) + assembly2 = Compound(label="Assembly2", children=[assembly1, box]) + assembly2.move(Location((2, 4, 6), (0, 0, 90))) + assembly3 = Compound(label="Assembly3", children=[assembly2, sphere]) + assembly3.move(Location((3, 6, 9))) + deep_shape: Shape = next( + iter(PreOrderIter(assembly3, filter_=lambda n: n.label in ("Cone"))) + ) + # print(deep_shape.path) + self.assertAlmostEqual( + deep_shape.global_location.position, (2, 13, 18), places=6 + ) + self.assertAlmostEqual( + deep_shape.global_location.orientation, (0, 90, 90), places=6 + ) + + +from ocp_vscode import show + if __name__ == "__main__": unittest.main() diff --git a/tests/test_direct_api/test_shape_list.py b/tests/test_direct_api/test_shape_list.py index 643221d..535e034 100644 --- a/tests/test_direct_api/test_shape_list.py +++ b/tests/test_direct_api/test_shape_list.py @@ -32,7 +32,6 @@ import math import re import unittest -import numpy as np from IPython.lib import pretty from build123d.build_common import GridLocations, PolarLocations from build123d.build_enums import GeomType, SortBy @@ -64,7 +63,9 @@ class TestShapeList(unittest.TestCase): actual_lines = actual.splitlines() self.assertEqual(len(actual_lines), len(expected_lines)) for actual_line, expected_line in zip(actual_lines, expected_lines): - start, end = re.split(r"at 0x[0-9a-f]+", expected_line, maxsplit=2, flags=re.I) + start, end = re.split( + r"at 0x[0-9a-f]+", expected_line, maxsplit=2, flags=re.I + ) self.assertTrue(actual_line.startswith(start)) self.assertTrue(actual_line.endswith(end)) @@ -302,7 +303,7 @@ class TestShapeList(unittest.TestCase): def test_vertex(self): sl = ShapeList([Edge.make_circle(1)]) - np.testing.assert_allclose(sl.vertex().to_tuple(), (1, 0, 0), 1e-5) + self.assertAlmostEqual(tuple(sl.vertex()), (1, 0, 0), 5) sl = ShapeList([Face.make_rect(1, 1), Face.make_rect(1, 1, Plane((4, 4)))]) with self.assertWarns(UserWarning): sl.vertex() @@ -403,5 +404,59 @@ class TestShapeList(unittest.TestCase): ) +class TestShapeListAddition(unittest.TestCase): + def setUp(self): + # Create distinct faces to test with + self.face1 = Box(1, 1, 1).faces().sort_by(Axis.Z)[0] # bottom face + self.face2 = Box(1, 1, 1).faces().sort_by(Axis.Z)[-1] # top face + self.face3 = Box(1, 1, 1).faces().sort_by(Axis.X)[0] # side face + + def test_add_single_shape(self): + sl = ShapeList([self.face1]) + result = sl + self.face2 + self.assertIsInstance(result, ShapeList) + self.assertEqual(len(result), 2) + self.assertIn(self.face1, result) + self.assertIn(self.face2, result) + + def test_add_shape_list(self): + sl1 = ShapeList([self.face1]) + sl2 = ShapeList([self.face2, self.face3]) + result = sl1 + sl2 + self.assertIsInstance(result, ShapeList) + self.assertEqual(len(result), 3) + self.assertListEqual(result, [self.face1, self.face2, self.face3]) + + def test_iadd_single_shape(self): + sl = ShapeList([self.face1]) + sl_id_before = id(sl) + sl += self.face2 + self.assertEqual(id(sl), sl_id_before) # in-place mutation + self.assertEqual(len(sl), 2) + self.assertListEqual(sl, [self.face1, self.face2]) + + def test_iadd_shape_list(self): + sl = ShapeList([self.face1]) + sl += ShapeList([self.face2, self.face3]) + self.assertEqual(len(sl), 3) + self.assertListEqual(sl, [self.face1, self.face2, self.face3]) + + def test_add_vector(self): + vector = Vector(1, 2, 3) + sl = ShapeList([vector]) + sl += Vector(4, 5, 6) + self.assertEqual(len(sl), 2) + self.assertIsInstance(sl[0], Vector) + self.assertIsInstance(sl[1], Vector) + + def test_add_invalid_type(self): + sl = ShapeList([self.face1]) + with self.assertRaises(TypeError): + _ = sl + 123 # type: ignore + + with self.assertRaises(TypeError): + sl += "not a shape" # type: ignore + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_direct_api/test_shells.py b/tests/test_direct_api/test_shells.py index 6c9023b..bd81945 100644 --- a/tests/test_direct_api/test_shells.py +++ b/tests/test_direct_api/test_shells.py @@ -41,12 +41,12 @@ class TestShells(unittest.TestCase): def test_shell_init(self): box_faces = Solid.make_box(1, 1, 1).faces() box_shell = Shell(box_faces) - self.assertTrue(box_shell.is_valid()) + self.assertTrue(box_shell.is_valid) def test_shell_init_single_face(self): face = Solid.make_cone(1, 0, 2).faces().filter_by(GeomType.CONE).first shell = Shell(face) - self.assertTrue(shell.is_valid()) + self.assertTrue(shell.is_valid) def test_center(self): box_faces = Solid.make_box(1, 1, 1).faces() @@ -71,9 +71,9 @@ class TestShells(unittest.TestCase): x_section = Rot(90) * Spline((0, -5), (-3, -2), (-2, 0), (-3, 2), (0, 5)) surface = sweep(x_section, Circle(5).wire()) single_face = Shell(surface.face()) - self.assertTrue(single_face.is_valid()) + self.assertTrue(single_face.is_valid) single_face = Shell(surface.faces()) - self.assertTrue(single_face.is_valid()) + self.assertTrue(single_face.is_valid) def test_sweep(self): path_c1 = JernArc((0, 0), (-1, 0), 1, 180) @@ -116,6 +116,13 @@ class TestShells(unittest.TestCase): outer_vol = 3 * 12 * 7 self.assertAlmostEqual(thick.volume, outer_vol - inner_vol) + def test_location_at(self): + shell = Solid.make_cylinder(1, 2).shell() + top_center = shell.location_at((0, 0, 2)) + self.assertAlmostEqual(top_center.position, (0, 0, 2), 5) + self.assertAlmostEqual(top_center.z_axis.direction, (0, 0, 1), 5) + self.assertAlmostEqual(top_center.x_axis.direction, (1, 0, 0), 5) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_direct_api/test_solid.py b/tests/test_direct_api/test_solid.py index df44353..a0fa0f3 100644 --- a/tests/test_direct_api/test_solid.py +++ b/tests/test_direct_api/test_solid.py @@ -29,19 +29,27 @@ license: import math import unittest +# Mocks for testing failure cases +from unittest.mock import MagicMock, patch + from build123d.build_enums import GeomType, Kind, Until -from build123d.geometry import ( - Axis, - BoundBox, - Location, - OrientedBoundBox, - Plane, - Pos, - Vector, -) +from build123d.geometry import Axis, Location, Plane, Pos, Vector from build123d.objects_curve import Spline +from build123d.objects_part import Box, Torus from build123d.objects_sketch import Circle, Rectangle -from build123d.topology import Compound, Edge, Face, Shell, Solid, Vertex, Wire +from build123d.topology import ( + Compound, + DraftAngleError, + Edge, + Face, + Shell, + Solid, + Vertex, + Wire, +) +import build123d +from OCP.BRepOffsetAPI import BRepOffsetAPI_DraftAngle +from OCP.StdFail import StdFail_NotDone class TestSolid(unittest.TestCase): @@ -51,7 +59,7 @@ class TestSolid(unittest.TestCase): box = Solid(box_shell) self.assertAlmostEqual(box.area, 6, 5) self.assertAlmostEqual(box.volume, 1, 5) - self.assertTrue(box.is_valid()) + self.assertTrue(box.is_valid) def test_extrude(self): v = Edge.extrude(Vertex(1, 1, 1), (0, 0, 1)) @@ -254,5 +262,66 @@ class TestSolid(unittest.TestCase): self.assertAlmostEqual(obb2.volume, 40, 4) +class TestSolidDraft(unittest.TestCase): + + def setUp(self): + # Create a simple box to test draft + self.box: Solid = Box(10, 10, 10).solid() + self.sides = self.box.faces().filter_by(Axis.Z, reverse=True) + self.bottom_face: Face = self.box.faces().sort_by(Axis.Z)[0] + self.neutral_plane = Plane(self.bottom_face) + + def test_successful_draft(self): + """Test that a draft operation completes successfully on a planar face""" + drafted = self.box.draft(self.sides, self.neutral_plane, 5) + self.assertIsInstance(drafted, Solid) + self.assertNotEqual(drafted.volume, self.box.volume) + + def test_unsupported_geometry(self): + """Test that a ValueError is raised on unsupported face geometry""" + # Create toroidal face to simulate unsupported geometry + torus = Torus(5, 1).solid() + with self.assertRaises(ValueError) as cm: + torus.draft([torus.faces()[0]], self.neutral_plane, 5) + self.assertIn("unsupported geometry type", str(cm.exception)) + + @patch("build123d.topology.three_d.BRepOffsetAPI_DraftAngle") + def test_adddone_failure_raises_draftangleerror(self, mock_draft_api): + """Test that failure of AddDone() raises DraftAngleError""" + mock_builder = MagicMock() + mock_builder.AddDone.return_value = False + mock_builder.ProblematicShape.return_value = "BadShape" + mock_draft_api.return_value = mock_builder + + with self.assertRaises(DraftAngleError) as cm: + self.box.draft(self.sides, self.neutral_plane, 5) + self.assertEqual(cm.exception.face, self.sides[0]) + self.assertEqual(cm.exception.problematic_shape, "BadShape") + self.assertIn("Draft could not be added", str(cm.exception)) + + @patch.object( + build123d.topology.three_d.BRepOffsetAPI_DraftAngle, + "Build", + side_effect=StdFail_NotDone, + ) + def test_build_failure_raises_draftangleerror(self, mock_draft_api): + """Test that Build() failure raises DraftAngleError""" + + with self.assertRaises(DraftAngleError) as cm: + self.box.draft(self.sides, self.neutral_plane, 5) + self.assertIsNone(cm.exception.face) + self.assertEqual( + cm.exception.problematic_shape, cm.exception.problematic_shape + ) # Not None + self.assertIn("Draft build failed", str(cm.exception)) + + def test_draftangleerror_contents(self): + """Test that DraftAngleError stores face and problematic shape""" + err = DraftAngleError("msg", face="face123", problematic_shape="shape456") + self.assertEqual(str(err), "msg") + self.assertEqual(err.face, "face123") + self.assertEqual(err.problematic_shape, "shape456") + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_direct_api/test_wire.py b/tests/test_direct_api/test_wire.py index 3391b88..51e398e 100644 --- a/tests/test_direct_api/test_wire.py +++ b/tests/test_direct_api/test_wire.py @@ -191,26 +191,26 @@ class TestWire(unittest.TestCase): e1 = Edge.make_line((1, 0), (1, 1)) w0 = Wire.make_circle(1) w1 = Wire(e0) - self.assertTrue(w1.is_valid()) + self.assertTrue(w1.is_valid) w2 = Wire([e0]) self.assertAlmostEqual(w2.length, 1, 5) - self.assertTrue(w2.is_valid()) + self.assertTrue(w2.is_valid) w3 = Wire([e0, e1]) - self.assertTrue(w3.is_valid()) + self.assertTrue(w3.is_valid) self.assertAlmostEqual(w3.length, 2, 5) w4 = Wire(w0.wrapped) - self.assertTrue(w4.is_valid()) + self.assertTrue(w4.is_valid) w5 = Wire(obj=w0.wrapped) - self.assertTrue(w5.is_valid()) + self.assertTrue(w5.is_valid) w6 = Wire(obj=w0.wrapped, label="w6", color=Color("red")) - self.assertTrue(w6.is_valid()) + self.assertTrue(w6.is_valid) self.assertEqual(w6.label, "w6") np.testing.assert_allclose(tuple(w6.color), (1.0, 0.0, 0.0, 1.0), 1e-5) w7 = Wire(w6) - self.assertTrue(w7.is_valid()) + self.assertTrue(w7.is_valid) c0 = Polyline((0, 0), (1, 0), (1, 1)) w8 = Wire(c0) - self.assertTrue(w8.is_valid()) + self.assertTrue(w8.is_valid) with self.assertRaises(ValueError): Wire(bob="fred") diff --git a/tests/test_drafting.py b/tests/test_drafting.py index 3d6ab41..1bb97ab 100644 --- a/tests/test_drafting.py +++ b/tests/test_drafting.py @@ -260,6 +260,11 @@ class DimensionLineTestCase(unittest.TestCase): with self.assertRaises(ValueError): DimensionLine([(0, 0, 0), (5, 0, 0)], draft=metric, arrows=(False, False)) + def test_vertical(self): + d_line = DimensionLine([(0, 0), (0, 100)], Draft()) + bbox = d_line.bounding_box() + self.assertAlmostEqual(bbox.size.Y, 100, 5) # numbers within + class ExtensionLineTestCase(unittest.TestCase): def test_min_x(self): diff --git a/tests/test_exporters3d.py b/tests/test_exporters3d.py index 9ca11b7..644ac3e 100644 --- a/tests/test_exporters3d.py +++ b/tests/test_exporters3d.py @@ -30,8 +30,10 @@ import json import os import re import unittest -from typing import Optional +from datetime import datetime from pathlib import Path +from typing import Optional +from zoneinfo import ZoneInfo import pytest @@ -39,7 +41,7 @@ from build123d.build_common import GridLocations from build123d.build_enums import Unit from build123d.build_line import BuildLine from build123d.build_sketch import BuildSketch -from build123d.exporters3d import export_gltf, export_step, export_brep, export_stl +from build123d.exporters3d import export_brep, export_gltf, export_step, export_stl from build123d.geometry import Color, Pos, Vector, VectorLike from build123d.objects_curve import Line from build123d.objects_part import Box, Sphere @@ -144,6 +146,29 @@ class TestExportStep(DirectApiTestCase): os.chmod("box_read_only.step", 0o777) # Make the file read/write os.remove("box_read_only.step") + def test_export_step_timestamp_datetime(self): + b = Box(1, 1, 1) + t = datetime(2025, 5, 6, 21, 30, 25) + self.assertTrue(export_step(b, "box.step", timestamp=t)) + with open("box.step", "r") as file: + step_data = file.read() + os.remove("box.step") + self.assertEqual( + re.findall("FILE_NAME\\('[^']*','([^']*)'", step_data), + ["2025-05-06T21:30:25"], + ) + + def test_export_step_timestamp_str(self): + b = Box(1, 1, 1) + self.assertTrue(export_step(b, "box.step", timestamp="0000-00-00T00:00:00")) + with open("box.step", "r") as file: + step_data = file.read() + os.remove("box.step") + self.assertEqual( + re.findall("FILE_NAME\\('[^']*','([^']*)'", step_data), + ["0000-00-00T00:00:00"], + ) + class TestExportGltf(DirectApiTestCase): def test_export_gltf(self): diff --git a/tests/test_mesher.py b/tests/test_mesher.py index 65edaff..0be4ccb 100644 --- a/tests/test_mesher.py +++ b/tests/test_mesher.py @@ -208,7 +208,7 @@ class TestHollowImport(unittest.TestCase): export_stl(test_shape, "test.stl") importer = Mesher() stl = importer.read("test.stl") - self.assertTrue(stl[0].is_valid()) + self.assertTrue(stl[0].is_valid) class TestImportDegenerateTriangles(unittest.TestCase): @@ -221,7 +221,7 @@ class TestImportDegenerateTriangles(unittest.TestCase): stl = importer.read("cyl_w_rect_hole.stl")[0] self.assertEqual(type(stl), Solid) self.assertTrue(stl.is_manifold) - self.assertTrue(stl.is_valid()) + self.assertTrue(stl.is_valid) self.assertEqual(sum(f.area == 0 for f in stl.faces()), 0)