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 axisJoints
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
| Joint | Degrees of freedom |
|---|---|
RigidJoint | none — fastened |
RevoluteJoint | angle about +Z (hinge) |
SliderJoint | offset along +Z |
CylindricalJoint | angle + offset |
PlanarJoint | dx, dy in-plane + angle about the normal |
BallJoint | orientation quaternion |
PinSlotJoint | angle about +Z + offset along +X (the slot) |
ScrewJoint | angle, 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 limitsConnections: 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:
- Put anchors on your parts at their natural connection points
(
LegoBrickhastop/bottom/seat; a screw hashead_seat; a beam has ends and faces). - Wrap the vocabulary of your domain in functions that derive anchors
and add joints —
interface.on_top_of(brick, at=(2, 3))is just aRigidJointon a derived stud anchor. - Use
Connection+PartModifierwhen the relationship implies machining, sketching cuts onanchor_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.