.. _joints: ###### Joints ###### :class:`~topology.Joint`'s enable Solid and Compound objects to be arranged relative to each other in an intuitive manner - with the same degree of motion that is found with the equivalent physical joints. :class:`~topology.Joint`'s always work in pairs - a :class:`~topology.Joint` can only be connected to another :class:`~topology.Joint` as follows: +---------------------------------------+---------------------------------------------------------------------+--------------------+ | :class:`~topology.Joint` | connect_to | Example | +=======================================+=====================================================================+====================+ | :class:`~joints.BallJoint` | :class:`~joints.RigidJoint` | Gimbal | +---------------------------------------+---------------------------------------------------------------------+--------------------+ | :class:`~joints.CylindricalJoint` | :class:`~joints.RigidJoint` | Screw | +---------------------------------------+---------------------------------------------------------------------+--------------------+ | :class:`~joints.LinearJoint` | :class:`~joints.RigidJoint`, :class:`~joints.RevoluteJoint` | Slider or Pin Slot | +---------------------------------------+---------------------------------------------------------------------+--------------------+ | :class:`~joints.RevoluteJoint` | :class:`~joints.RigidJoint` | Hinge | +---------------------------------------+---------------------------------------------------------------------+--------------------+ | :class:`~joints.RigidJoint` | :class:`~joints.RigidJoint` | Fixed | +---------------------------------------+---------------------------------------------------------------------+--------------------+ 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 has built-in support for displaying joints). .. note:: If joints are created within the scope of a :class:`~build_part.BuildPart` builder, the ``to_part`` parameter need not be specified as the builder will, on exit, automatically transfer the joints created in its scope to the part created. The following sections provide more detail on the available joints and describes how they are used. .. py:module:: joints *********** Rigid Joint *********** 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 :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 repositioning another part relative to ``self`` which stay fixed - as follows: .. code-block:: python pipe.joints["outlet"].connect_to(flange_outlet.joints["pipe"]) .. note:: Within a part all of the joint labels must be unique. The :meth:`~topology.Joint.connect_to` method only does a one time re-position of a part and does not bind them in any way; however, putting them into an :ref:`assembly` will maintain there relative locations as will combining parts with boolean operations or within a :class:`~build_part.BuildPart` context. As a example of creating parts with joints and connecting them together, consider the following code where flanges are attached to the ends of a curved pipe: .. image:: assets/rigid_joints_pipe.png .. literalinclude:: rigid_joints_pipe.py :emphasize-lines: 19-20, 23-24 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 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). .. autoclass:: RigidJoint .. :exclude-members: connect_to .. method:: connect_to(other: BallJoint, *, angles: RotationLike = None) .. method:: connect_to(other: CylindricalJoint, *, position: float = None, angle: float = None) :noindex: .. method:: connect_to(other: LinearJoint, *, position: float = None) :noindex: .. method:: connect_to(other: RevoluteJoint, *, angle: float = None) :noindex: .. method:: connect_to(other: RigidJoint) :noindex: Connect the ``RigidJoint`` to another Joint. .. ************** 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 Rigid Joints: ``axis``, ``angle_reference``, and ``range`` that allow the circular motion to be fully defined. When :meth:`~topology.Joint.connect_to` with a Revolute Joint, an extra ``angle`` parameter is present which allows one to change the relative position of joined parts by changing a single value. .. autoclass:: RevoluteJoint .. :exclude-members: connect_to .. method:: connect_to(other: RigidJoint, *, angle: float = None) Connect the ``RevoluteJoint`` to a ``RigidJoint`` ************ Linear Joint ************ Component moves along a single axis as with a sliding latch shown here: .. image:: assets/joint-latch-slide.png The code to generate these components follows: .. literalinclude:: slide_latch.py :emphasize-lines: 30, 52, 55 .. image:: assets/joint-latch.png :width: 65 % .. image:: assets/joint-slide.png :width: 27.5 % Note how the slide is constructed in a different orientation than the direction of motion. The three highlighted lines of code show how the joints are created and connected together: * The :class:`~joints.LinearJoint` has an axis and limits of movement * The :class:`~joints.RigidJoint` has a single location, orientated such that the knob will ultimately be "up" * The ``connect_to`` specifies a position that must be within the predefined limits. The slider can be moved back and forth by just changing the ``position`` value. Values outside of the limits will raise an exception. .. autoclass:: LinearJoint .. :exclude-members: connect_to .. method:: connect_to(other: RevoluteJoint, *, position: float = None, angle: float = None) .. method:: connect_to(other: RigidJoint, *, position: float = None) :noindex: Connect the ``LinearJoint`` to either a ``RevoluteJoint`` or ``RigidJoint`` ***************** 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. .. code-block::python hinge_outer.joints["hole2"].connect_to(m6_joint, position=5 * MM, angle=30) .. autoclass:: CylindricalJoint .. :exclude-members: connect_to .. method:: connect_to(other: RigidJoint, *, position: float = None, angle: float = None) Connect the ``CylindricalJoint`` to a ``RigidJoint`` ********** Ball Joint ********** A component rotates around all 3 axes using a gimbal system (3 nested rotations). A :class:`~joints.BallJoint` is found within a rod end as shown here: .. image:: assets/rod_end.png .. 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 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) Connect a ``BallJoint`` to a ``RigidJoint``