FoundationAssembliesJoints & Anchors

Joints & Anchors

Anchors and joints position components relative to each other without any transform math. They are the foundation-native way to express “this connects to that” — and the base layer domain libraries build richer connections on (LEGO studs, screwed connections, wood joinery, tube fittings).

Anchors

An anchor is a named connector frame on a part or assembly (a “mate connector” in Onshape terms). Convention: +Z is the mate axis / outward normal of the mating face, +X is the clocking direction.

from cadbuildr.foundation import Part, make_anchor
 
class Plate(Part):
    def __init__(self):
        # ... sketch + extrude 6mm thick plate ...
        self.add_anchor(make_anchor("mount", (10.0, 10.0, 6.0)))          # top face, +Z up
        self.add_anchor(make_anchor("underside", (0.0, 0.0, 0.0), z_down=True))

Look anchors up by name with part.anchor("mount"). Derive new anchors from existing ones — derived anchors follow their base through any assembly nesting:

a = plate.anchor("mount")
a.offset([16.0, 8.0, 0.0])       # translated in the anchor's local frame
a.rotated(math.pi / 2)           # spun about the mate axis
a.flipped()                      # 180° about X: reverses the mate axis

Joints

A joint connects a parent anchor (on something already placed — or the assembly itself) to a child anchor (on the component being placed) and computes the child’s position deterministically. There is no constraint solver: each joint places exactly one component.

from cadbuildr.foundation import Assembly, RigidJoint
 
assembly = Assembly()
assembly.add_component(base)
assembly.add_joint(RigidJoint(
    parent_anchor=base.anchor("top"),
    child_anchor=brick.anchor("bottom"),
))

By default (flip=True) the anchors mate face-to-face: the parent’s +Z opposes the child’s +Z, so two outward-facing surfaces land against each other. Pass flip=BoolParameter(value=False) to align the Z axes instead (useful for “attach frame” anchors like a Lego seat).

Ground a first component against the assembly origin with assembly.ground(component.anchor("base")).

Joint types

JointDegrees of freedom
RigidJointnone — fastened
RevoluteJointangle about +Z (hinge)
SliderJointoffset along +Z
CylindricalJointangle + offset
PlanarJointdx, dy in-plane + angle about the normal
BallJointorientation quaternion
PinSlotJointangle about +Z + offset along +X (the slot)
ScrewJointangle, with travel coupled by pitch

DOF values are plain parameters with optional limits, and setters re-resolve the placement:

hinge = RevoluteJoint(
    parent_anchor=frame.anchor("hinge"),
    child_anchor=door.anchor("hinge"),
    limits=JointLimits(min=FloatParameter(value=0.0), max=FloatParameter(value=2.1)),
)
assembly.add_joint(hinge)
hinge.set_angle(1.2)   # re-places the door; raises if outside limits

Connections: joints that also shape parts

A Connection bundles one joint with geometry contributions to the connected parts (PartModifier) — drill the clearance hole, cut the mortise, cope the tube end. This is the hook domain libraries use to make one-liners that both place and machine:

from cadbuildr.stdlib.fasteners.connections import connect_with_screw
from cadbuildr_projects.woodworking import mortise_and_tenon
from cadbuildr.stdlib.profiles.tubes import tube_tee
 
connect_with_screw(assembly, DIN912Screw("M4", 16), plate.anchor("mount_1"))
mortise_and_tenon(assembly, rail, leg, face="px", at=550)
tube_tee(assembly, leg_tube, handrail, at=400)

Each of those single lines places the child component with a joint and applies the implied cuts (clearance bore / tenon shoulders + mortise pocket / saddle cope) through the parts’ normal operation path.

Building your own interface library

The extension pattern, end to end:

  1. Put anchors on your parts at their natural connection points (LegoBrick has top/bottom/seat; a screw has head_seat; a beam has ends and faces).
  2. Wrap the vocabulary of your domain in functions that derive anchors and add joints — interface.on_top_of(brick, at=(2, 3)) is just a RigidJoint on a derived stud anchor.
  3. Use Connection + PartModifier when the relationship implies machining, sketching cuts on anchor_plane(anchor).

See cadbuildr_projects/lego (StudInterface), cadbuildr/stdlib (fasteners + tubes), cadbuildr_projects/woodworking, and cadbuildr_projects/playground for a full structure composed exclusively of these one-liners.

Notes

  • A component can be placed once (one joint, or one add_component) — a second placement raises. Connect further components to its anchors.
  • Joints resolve at authoring time; the serialized DAG carries concrete frames plus the joints/anchors for provenance, so viewers need nothing special to render jointed assemblies.
  • translate() / rotate() on a component before jointing is overwritten: joints are absolute placements.