Joints

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. Joint’s always work in pairs - a Joint can only be connected to another Joint as follows:

Joint

connect_to

Example

BallJoint

RigidJoint

Gimbal

CylindricalJoint

RigidJoint

Screw

LinearJoint

RigidJoint, RevoluteJoint

Slider or Pin Slot

RevoluteJoint

RigidJoint

Hinge

RigidJoint

RigidJoint

Fixed

Objects may have many joints bound to them each with an identifying label. All 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 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.

Rigid Joint

A rigid joint positions two components relative to each another with no freedom of movement. When a 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 Location) - as follows:

RigidJoint(label="outlet", to_part=pipe, joint_location=path.location_at(1))

Once a joint is bound to a part this way, the connect_to() method can be used to repositioning another part relative to self which stay fixed - as follows:

pipe.joints["outlet"].connect_to(flange_outlet.joints["pipe"])

Note

Within a part all of the joint labels must be unique.

The 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 Assemblies will maintain there relative locations as will combining parts with boolean operations or within a 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:

_images/rigid_joints_pipe.png
import copy
from build123d import *
from bd_warehouse.flange import WeldNeckFlange
from bd_warehouse.pipe import PipeSection
from ocp_vscode import *

flange_inlet = WeldNeckFlange(nps="10", flange_class=300)
flange_outlet = copy.copy(flange_inlet)

with BuildPart() as pipe_builder:
    # Create the pipe
    with BuildLine():
        path = TangentArc((0, 0, 0), (2 * FT, 0, 1 * FT), tangent=(1, 0, 0))
    with BuildSketch(Plane(origin=path @ 0, z_dir=path % 0)):
        PipeSection("10", material="stainless", identifier="40S")
    sweep()

    # Add the joints
    RigidJoint(label="inlet", joint_location=-path.location_at(0))
    RigidJoint(label="outlet", joint_location=path.location_at(1))

# Place the flanges at the ends of the pipe
pipe_builder.part.joints["inlet"].connect_to(flange_inlet.joints["pipe"])
pipe_builder.part.joints["outlet"].connect_to(flange_outlet.joints["pipe"])

show(pipe_builder, flange_inlet, flange_outlet, render_joints=True)

Note how the locations of the joints are determined by the 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 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).

class RigidJoint(label: str, to_part: ~typing.Optional[~typing.Union[~build123d.topology.Solid, ~build123d.topology.Compound]] = None, joint_location: ~build123d.geometry.Location = (p=(0.00, 0.00, 0.00), o=(-0.00, 0.00, -0.00)))[source]

A rigid joint fixes two components to one another.

Parameters
  • label (str) – joint label

  • to_part (Union[Solid, Compound], optional) – object to attach joint to

  • joint_location (Location) – global location of joint

Variables

relative_location (Location) – joint location relative to bound object

connect_to(other: BallJoint, *, angles: Union[tuple[float, float, float], Rotation] = None, **kwargs)[source]
connect_to(other: CylindricalJoint, *, position: float = None, angle: float = None)
connect_to(other: LinearJoint, *, position: float = None)
connect_to(other: RevoluteJoint, *, angle: float = None)
connect_to(other: RigidJoint)

Connect the RigidJoint to another Joint

Parameters
  • other (Joint) – joint to connect to

  • angle (float, optional) – angle in degrees. Defaults to range min.

  • angles (RotationLike, optional) – angles about axes in degrees. Defaults to range minimums.

  • position (float, optional) – linear position. Defaults to linear range min.

relative_to(other: BallJoint, *, angles: Union[tuple[float, float, float], Rotation] = None)[source]
relative_to(other: CylindricalJoint, *, position: float = None, angle: float = None)
relative_to(other: LinearJoint, *, position: float = None)
relative_to(other: RevoluteJoint, *, angle: float = None)
relative_to(other: RigidJoint)

Relative location of RigidJoint to another Joint

Parameters
  • other (RigidJoint) – relative to joint

  • angle (float, optional) – angle in degrees. Defaults to range min.

  • angles (RotationLike, optional) – angles about axes in degrees. Defaults to range minimums.

  • position (float, optional) – linear position. Defaults to linear range min.

Raises

TypeError – other must be of a type in: BallJoint, CylindricalJoint, LinearJoint, RevoluteJoint, RigidJoint.

property symbol: Compound

A CAD symbol (XYZ indicator) as bound to part

Revolute Joint

Component rotates around axis like a hinge. The Joint Tutorial covers Revolute Joints in detail.

During instantiation of a 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 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.

class RevoluteJoint(label: str, to_part: Optional[Union[Solid, Compound]] = None, axis: Axis = ((0.0, 0.0, 0.0), (0.0, 0.0, 1.0)), angle_reference: Optional[Union[Vector, tuple[float, float], tuple[float, float, float], Iterable[float]]] = None, angular_range: tuple[float, float] = (0, 360))[source]

Component rotates around axis like a hinge.

Parameters
  • label (str) – joint label

  • to_part (Union[Solid, Compound], optional) – object to attach joint to

  • axis (Axis) – axis of rotation

  • angle_reference (VectorLike, optional) – direction normal to axis defining where angles will be measured from. Defaults to None.

  • range (tuple[float, float], optional) – (min,max) angle of joint. Defaults to (0, 360).

Variables
  • angle (float) – angle of joint

  • angle_reference (Vector) – reference for angular positions

  • angular_range (tuple[float,float]) – min and max angular position of joint

  • relative_axis (Axis) – joint axis relative to bound part

Raises

ValueError – angle_reference must be normal to axis

connect_to(other: RigidJoint, *, angle: Optional[float] = None)[source]

Connect RevoluteJoint and RigidJoint

Parameters
  • other (RigidJoint) – relative to joint

  • angle (float, optional) – angle in degrees. Defaults to range min.

Returns

other must of type RigidJoint ValueError: angle out of range

Return type

TypeError

relative_to(other: RigidJoint, *, angle: Optional[float] = None)[source]

Relative location of RevoluteJoint to RigidJoint

Parameters
  • other (RigidJoint) – relative to joint

  • angle (float, optional) – angle in degrees. Defaults to range min.

Raises
  • TypeError – other must of type RigidJoint

  • ValueError – angle out of range

property symbol: Compound

A CAD symbol representing the axis of rotation as bound to part

Linear Joint

Component moves along a single axis as with a sliding latch shown here:

_images/joint-latch-slide.png

The code to generate these components follows:

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 handle 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", 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", joint_location=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)
_images/joint-latch.png _images/joint-slide.png

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 LinearJoint has an axis and limits of movement

  • The 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.

class LinearJoint(label: str, to_part: Optional[Union[Solid, Compound]] = None, axis: Axis = ((0.0, 0.0, 0.0), (0.0, 0.0, 1.0)), linear_range: tuple[float, float] = (0, inf))[source]

Component moves along a single axis.

Parameters
  • label (str) – joint label

  • to_part (Union[Solid, Compound], optional) – object to attach joint to

  • axis (Axis) – axis of linear motion

  • range (tuple[float, float], optional) – (min,max) position of joint. Defaults to (0, inf).

Variables
  • axis (Axis) – joint axis

  • angle (float) – angle of joint

  • linear_range (tuple[float,float]) – min and max positional values

  • position (float) – joint position

  • relative_axis (Axis) – joint axis relative to bound part

connect_to(other: RevoluteJoint, *, position: float = None, angle: float = None)[source]
connect_to(other: RigidJoint, *, position: float = None)

Connect LinearJoint to another Joint

Parameters
  • other (Joint) – joint to connect to

  • angle (float, optional) – angle in degrees. Defaults to range min.

  • position (float, optional) – linear position. Defaults to linear range min.

Raises
  • TypeError – other must be of type RevoluteJoint or RigidJoint

  • ValueError – position out of range

  • ValueError – angle out of range

relative_to(other: RigidJoint, *, position: float = None)[source]
relative_to(other: RevoluteJoint, *, position: float = None, angle: float = None)

Relative location of LinearJoint to RevoluteJoint or RigidJoint

Parameters
  • other (Joint) – joint to connect to

  • angle (float, optional) – angle in degrees. Defaults to range min.

  • position (float, optional) – linear position. Defaults to linear range min.

Raises
  • TypeError – other must be of type RevoluteJoint or RigidJoint

  • ValueError – position out of range

  • ValueError – angle out of range

property symbol: Compound

A CAD symbol of the linear axis positioned relative to_part

Cylindrical Joint

A CylindricalJoint allows a component to rotate around and moves along a single axis like a screw combining the functionality of a LinearJoint and a RevoluteJoint joint. The connect_to for these joints have both position and angle parameters as shown below extracted from the joint tutorial.

class CylindricalJoint(label: str, to_part: Optional[Union[Solid, Compound]] = None, axis: Axis = ((0.0, 0.0, 0.0), (0.0, 0.0, 1.0)), angle_reference: Optional[Union[Vector, tuple[float, float], tuple[float, float, float], Iterable[float]]] = None, linear_range: tuple[float, float] = (0, inf), angular_range: tuple[float, float] = (0, 360))[source]

Component rotates around and moves along a single axis like a screw.

Parameters
  • label (str) – joint label

  • to_part (Union[Solid, Compound], optional) – object to attach joint to

  • axis (Axis) – axis of rotation and linear motion

  • angle_reference (VectorLike, optional) – direction normal to axis defining where angles will be measured from. Defaults to None.

  • linear_range (tuple[float, float], optional) – (min,max) position of joint. Defaults to (0, inf).

  • angular_range (tuple[float, float], optional) – (min,max) angle of joint. Defaults to (0, 360).

Variables
  • axis (Axis) – joint axis

  • linear_position (float) – linear joint position

  • rotational_position (float) – revolute joint angle in degrees

  • angle_reference (Vector) – reference for angular positions

  • angular_range (tuple[float,float]) – min and max angular position of joint

  • linear_range (tuple[float,float]) – min and max positional values

  • relative_axis (Axis) – joint axis relative to bound part

  • position (float) – joint position

  • angle (float) – angle of joint

Raises

ValueError – angle_reference must be normal to axis

connect_to(other: RigidJoint, *, position: Optional[float] = None, angle: Optional[float] = None)[source]

Connect CylindricalJoint and RigidJoint”

Parameters
  • other (Joint) – joint to connect to

  • position (float, optional) – linear position. Defaults to linear range min.

  • angle (float, optional) – angle in degrees. Defaults to range min.

Raises
  • TypeError – other must be of type RigidJoint

  • ValueError – position out of range

  • ValueError – angle out of range

relative_to(other: RigidJoint, *, position: Optional[float] = None, angle: Optional[float] = None)[source]

Relative location of CylindricalJoint to RigidJoint

Parameters
  • other (Joint) – joint to connect to

  • position (float, optional) – linear position. Defaults to linear range min.

  • angle (float, optional) – angle in degrees. Defaults to range min.

Raises
  • TypeError – other must be of type RigidJoint

  • ValueError – position out of range

  • ValueError – angle out of range

property symbol: Compound

A CAD symbol representing the cylindrical axis as bound to part

Ball Joint

A component rotates around all 3 axes using a gimbal system (3 nested rotations). A BallJoint is found within a rod end as shown here:

_images/rod_end.png
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")
)
inner_radius = 15.89 / 2
inner_gap = 0.2

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(inner_radius, mode=Mode.SUBTRACT)
    # Add thread
    with Locations((0, -30, 0)):
        add(thread, rotation=(-90, 0, 0))
    # Create the ball joint
    BallJoint(
        "socket",
        joint_location=Location(),
        angular_range=((-14, 14), (-14, 14), (0, 360)),
    )

with BuildPart() as ball:
    Sphere(inner_radius - inner_gap)
    Box(50, 50, 13, mode=Mode.INTERSECT)
    Hole(4)
    ball.part.color = Color("aliceblue")
    RigidJoint("ball", joint_location=Location())

rod_end.part.joints["socket"].connect_to(ball.part.joints["ball"], angles=(5, 10, 0))

show(rod_end.part, ball.part)

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).

class BallJoint(label: str, to_part: ~typing.Optional[~typing.Union[~build123d.topology.Solid, ~build123d.topology.Compound]] = None, joint_location: ~build123d.geometry.Location = (p=(0.00, 0.00, 0.00), o=(-0.00, 0.00, -0.00)), angular_range: tuple[tuple[float, float], tuple[float, float], tuple[float, float]] = ((0, 360), (0, 360), (0, 360)), angle_reference: ~build123d.geometry.Plane = Plane(o=(0.00, 0.00, 0.00), x=(1.00, 0.00, 0.00), z=(0.00, 0.00, 1.00)))[source]

A component rotates around all 3 axes using a gimbal system (3 nested rotations).

Parameters
  • label (str) – joint label

  • to_part (Union[Solid, Compound], optional) – object to attach joint to

  • joint_location (Location) – global location of joint

  • angular_range – (tuple[ tuple[float, float], tuple[float, float], tuple[float, float] ], optional): X, Y, Z angle (min, max) pairs. Defaults to ((0, 360), (0, 360), (0, 360)).

  • angle_reference (Plane, optional) – plane relative to part defining zero degrees of rotation. Defaults to Plane.XY.

Variables
  • relative_location (Location) – joint location relative to bound part

  • angular_range – (tuple[ tuple[float, float], tuple[float, float], tuple[float, float] ]): X, Y, Z angle (min, max) pairs.

  • angle_reference (Plane) – plane relative to part defining zero degrees of

connect_to(other: RigidJoint, *, angles: Optional[Union[tuple[float, float, float], Rotation]] = None)[source]

Connect BallJoint and RigidJoint

Parameters
  • other (RigidJoint) – joint to connect to

  • angles (RotationLike, optional) – angles about axes in degrees. Defaults to range minimums.

Raises
  • TypeError – invalid other joint type

  • ValueError – angles out of range

relative_to(other: RigidJoint, *, angles: Optional[Union[tuple[float, float, float], Rotation]] = None)[source]

relative_to - BallJoint

Return the relative location from this joint to the RigidJoint of another object

Parameters
  • other (RigidJoint) – joint to connect to

  • angles (RotationLike, optional) – angles about axes in degrees. Defaults to range minimums.

Raises
  • TypeError – invalid other joint type

  • ValueError – angles out of range

property symbol: Compound

A CAD symbol representing joint as bound to part