diff --git a/docs/assets/joint-latch-slide.png b/docs/assets/joint-latch-slide.png new file mode 100644 index 0000000..a5f6ff8 Binary files /dev/null and b/docs/assets/joint-latch-slide.png differ diff --git a/docs/assets/joint-latch.png b/docs/assets/joint-latch.png new file mode 100644 index 0000000..8c9534f Binary files /dev/null and b/docs/assets/joint-latch.png differ diff --git a/docs/assets/joint-slide.png b/docs/assets/joint-slide.png new file mode 100644 index 0000000..d94dd0c Binary files /dev/null and b/docs/assets/joint-slide.png differ diff --git a/docs/assets/rod_end.png b/docs/assets/rod_end.png new file mode 100644 index 0000000..915144b Binary files /dev/null and b/docs/assets/rod_end.png differ diff --git a/docs/joints.rst b/docs/joints.rst index 913f7f6..75c8afd 100644 --- a/docs/joints.rst +++ b/docs/joints.rst @@ -76,27 +76,72 @@ one at the face end - both of which are shown in the above image (generated by o Revolute Joint ************** -Component rotates around axis like a hinge. +Component rotates around axis like a hinge. The :ref:`joint_tutorial` covers Revolute Joints in detail. + +During instantiation of a :class:`~topology.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. + ************ Linear Joint ************ -Component moves along a single axis. +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:`~topology.LinearJoint` has an axis and limits of movement +* The :class:`~topology.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. ***************** Cylindrical Joint ***************** -Component rotates around and moves along a single axis like a screw. +A :class:`~topology.CylindricalJoint` allows a component to rotate around and moves along a single axis +like a screw combining the functionality of a :class:`~topology.LinearJoint` and a +:class:`~topology.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) + ********** Ball Joint ********** -A component rotates around all 3 axes using a gimbal system (3 nested rotations). +A component rotates around all 3 axes using a gimbal system (3 nested rotations). A :class:`~topology.BallJoint` +is found within a rod end as shown here: -********** -Conclusion -********** +.. image:: assets/rod_end.png + +.. literalinclude:: rod_end.py + :emphasize-lines: 37-42,49,51 + +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 interfer with the rod end itself. The ``connect_to`` sets the three angles +(only two are significant in this example). diff --git a/docs/rod_end.py b/docs/rod_end.py new file mode 100644 index 0000000..81463e4 --- /dev/null +++ b/docs/rod_end.py @@ -0,0 +1,53 @@ +from build123d import * +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") +) + +with BuildPart() as rod_end: + # Create the outer shape + with BuildSketch(): + Circle(22.25 / 2) + with Locations((0, -12)): + Rectangle(8, 1) + make_hull() + split(bisect_by=Plane.YZ) + revolve(axis=Axis.Y) + # Refine the shape + with BuildSketch(Plane.YZ) as s2: + Rectangle(25, 8, align=(Align.MIN, Align.CENTER)) + Rectangle(9, 10, align=(Align.MIN, Align.CENTER)) + chamfer(s2.vertices(), 0.5) + revolve(axis=Axis.Z, mode=Mode.INTERSECT) + # Add the screw shaft + Cylinder( + thread.min_radius, + 30, + rotation=(90, 0, 0), + align=(Align.CENTER, Align.CENTER, Align.MIN), + ) + # Cutout the ball socket + Sphere(15.89 / 2, mode=Mode.SUBTRACT) # Add thread + with Locations((0, -30, 0)): + add(thread, rotation=(-90, 0, 0)) + # Create the ball joint + BallJoint( + "socket", + rod_end.part, + joint_location=Location(), + angular_range=((-14, 14), (-14, 14), (0, 360)), + ) + +with BuildPart() as ball: + Sphere(15.88 / 2) + Box(50, 50, 13, mode=Mode.INTERSECT) + Hole(4) + ball.part.color = Color("aliceblue") + RigidJoint("ball", ball.part, joint_location=Location()) + +rod_end.part.joints["socket"].connect_to(ball.part.joints["ball"], angles=(5, 10, 0)) + +show(rod_end.part, ball.part) diff --git a/docs/slide_latch.py b/docs/slide_latch.py new file mode 100644 index 0000000..3049355 --- /dev/null +++ b/docs/slide_latch.py @@ -0,0 +1,59 @@ +from build123d import * +from ocp_vscode import * + +with BuildPart() as latch: + # Basic box shape to start with filleted corners + Box(70, 30, 14) + end = latch.faces().sort_by(Axis.X)[-1] # save the end with the hole + fillet(latch.edges().filter_by(Axis.Z), 2) + fillet(latch.edges().sort_by(Axis.Z)[-1], 1) + # Make screw tabs + with BuildSketch(latch.faces().sort_by(Axis.Z)[0]) as l4: + with Locations((-30, 0), (30, 0)): + SlotOverall(50, 10, rotation=90) + Rectangle(50, 30) + fillet(l4.vertices(Select.LAST), radius=2) + extrude(amount=-2) + with GridLocations(60, 40, 2, 2): + Hole(2) + # Create the hole from the end saved previously + with BuildSketch(end) as slide_hole: + add(end) + offset(amount=-2) + fillet(slide_hole.vertices(), 1) + extrude(amount=-68, mode=Mode.SUBTRACT) + # Slot for the hangle to slide in + with BuildSketch(latch.faces().sort_by(Axis.Z)[-1]): + SlotOverall(32, 8) + extrude(amount=-2, mode=Mode.SUBTRACT) + # The slider will move align the x axis 12mm in each direction + LinearJoint("latch", latch.part, axis=Axis.X, linear_range=(-12, 12)) + +with BuildPart() as slide: + # The slide will be a little smaller than the hole + with BuildSketch() as s1: + add(slide_hole.sketch) + offset(amount=-0.25) + # The extrusions aren't symmetric + extrude(amount=46) + extrude(slide.faces().sort_by(Axis.Z)[0], amount=20) + # Round off the ends + fillet(slide.edges().group_by(Axis.Z)[0], 1) + fillet(slide.edges().group_by(Axis.Z)[-1], 1) + # Create the knob + with BuildSketch() as s2: + with Locations((12, 0)): + SlotOverall(15, 4, rotation=90) + Rectangle(12, 7, align=(Align.MIN, Align.CENTER)) + fillet(s2.vertices(Select.LAST), 1) + split(bisect_by=Plane.XZ) + revolve(axis=Axis.X) + # Align the joint to Plane.ZY flipped + RigidJoint("slide", slide.part, Location(-Plane.ZY)) + +# Position the slide in the latch: -12 >= position <= 12 +latch.part.joints["latch"].connect_to(slide.part.joints["slide"], position=12) + +# show(latch.part, render_joints=True) +# show(slide.part, render_joints=True) +show(latch.part, slide.part, render_joints=True)