How CAD Skills Handles Assemblies and Joints in Text-to-CAD
The CAD Skills subsystem represents mechanical structures as hierarchical assemblies defined by AssemblySpec dataclasses, flattens the hierarchy for export, and instantiates build123d Joint objects—including RevoluteJoint, RigidJoint, and LinearJoint—to model mechanical relationships between parts.
The earthtojake/text-to-cad repository implements a complete pipeline for converting text descriptions into manufacturable CAD artifacts. Understanding how CAD Skills handles assemblies and joints requires examining the specification layer, the flattening logic, and the joint instantiation code that bridges abstract descriptions with physical OCCT scenes.
Assembly Specification and Hierarchy
At the core of the system lies a strict data model that separates instance definitions from hierarchical structure.
Defining AssemblyInstanceSpec and AssemblyNodeSpec
The specification layer in assembly_spec.py defines two primary dataclasses that govern how assemblies are structured:
AssemblyInstanceSpecdescribes concrete STEP parts, encapsulating the instance ID, source file path, local path, 4×4 transform matrix, and color usage flags.AssemblyNodeSpecdescribes nodes in the hierarchy, tracking instance IDs, names, local transforms, and optional child nodes to build parent-child relationships.
These dataclasses enforce type safety across the generation pipeline and provide the schema for the AssemblySpec container object.
Validating Payloads with assembly_spec_from_payload
When users supply Python payloads—either as a list of children or a dictionary containing an instances list—the helper assembly_spec_from_payload() performs strict validation:
from cadpy.assembly_spec import assembly_spec_from_payload
from pathlib import Path
assembly_payload = {
"instances": [
{
"instance_id": "base",
"source_path": Path("models/base.step"),
"path": "base.step",
"name": "BasePlate",
"transform": (1,0,0, 0,1,0, 0,0,1, 0,0,0, 1),
"use_source_colors": True,
},
{
"instance_id": "motor",
"source_path": Path("models/motor.step"),
"path": "motor.step",
"name": "Motor",
"transform": (1,0,0, 0,1,0, 0,0,1, 0,0,20, 1),
"use_source_colors": True,
},
]
}
spec = assembly_spec_from_payload(Path("my_assembly.py"), assembly_payload)
This function forbids duplicate sibling names within the same parent node and normalizes the hierarchy into a validated AssemblySpec object ready for geometry processing.
Building the Assembly Pipeline
Once validated, the AssemblySpec traverses a three-stage pipeline to produce renderable geometry.
Flattening Hierarchies with assembly_flatten.py
The assembly_flatten.py module walks the hierarchical specification tree and produces a flat list of leaf instances. During traversal, it multiplies cumulative transforms using multiply_transforms to resolve each component's final world-space position. This flattening step is essential for preparing instances for both STEP export and GLB serialization.
Composition Strategies in assembly_composition.py
The assembly_composition.py module determines how to construct the actual OCCT scene. It selects between:
- Native composition: Direct OCCT-based assembly building for maximum control over geometry and metadata.
- Linked composition: Integration with the
build123dlibrary for simplified scene construction.
The choice of composition strategy affects how joints are attached and how the final Compound object is structured before export.
Joint Creation and Management
Joints represent mechanical relationships—degrees of freedom, axes of rotation, and attachment points—between assembly components.
Supported Joint Types
According to the source code in assembly.py, CAD Skills supports five joint primitives from the build123d library:
RigidJoint– Fixes two components together with no degrees of freedom.RevoluteJoint– Allows rotation around a single axis.LinearJoint– Permits translation along an axis.CylindricalJoint– Combines rotation and translation along the same axis.BallJoint– Provides spherical rotation with multiple degrees of freedom.
Creating Joints in assembly.py
The assembly.py file contains the core joint instantiation logic. For each leaf instance in the flattened hierarchy, the code creates the appropriate build123d Joint object and attaches it to a labeled Compound representing the complete assembly.
The helper function add_axis_frame configures the joint's origin, axis vector, motion limits, and visualization options:
def _add_revolute_joint(part, name, axis, **joint_options):
# Used by the assembly builder to create a RevoluteJoint
return add_axis_frame(
part,
name,
axis,
joint_type="RevoluteJoint",
**joint_options,
)
Joint parameters include coordinate frames, angular limits for revolute joints, and linear limits for prismatic motion, all serialized into the final CAD artifact.
Exporting Assemblies with Joints
The generation pipeline culminates in export modules that preserve joint metadata. The generation.py module orchestrates the workflow:
from cadpy.generation import (
build_native_assembly_composition,
export_assembly_step_scene,
)
# Build the OCCT scene (includes Build123d joints)
scene = build_native_assembly_composition(spec)
# Export the whole assembly as a STEP file
export_assembly_step_scene(spec, scene, output_path=Path("out/assembly.step"))
The AssemblySpec.kind value—either "part" or "assembly"—drives exporter selection in step_export.py and glb.py, determining whether the output contains joint annotations and hierarchical metadata or represents a single solid body.
Summary
- CAD Skills uses
AssemblySpec,AssemblyInstanceSpec, andAssemblyNodeSpecdataclasses inassembly_spec.pyto define hierarchical mechanical structures. - The
assembly_spec_from_payload()function validates user input and forbids duplicate sibling names during hierarchy normalization. assembly_flatten.pyresolves world-space transforms by multiplying cumulative transformation matrices down the hierarchy.- Joint instantiation occurs in
assembly.py, creating build123dJointobjects (Revolute, Rigid, Linear, Cylindrical, and Ball) attached to a labeledCompound. - The
AssemblySpec.kindfield controls export behavior instep_export.pyandglb.py, determining whether outputs include assembly metadata and joint definitions.
Frequently Asked Questions
What joint types does CAD Skills support?
CAD Skills supports five joint types through the build123d library: RigidJoint for fixed connections, RevoluteJoint for rotational motion, LinearJoint for sliding motion, CylindricalJoint for combined rotation and translation, and BallJoint for spherical articulation. These are instantiated in assembly.py using the add_axis_frame helper function.
How does CAD Skills validate assembly structures?
Validation occurs in assembly_spec.py via the assembly_spec_from_payload() function, which checks for duplicate sibling names within parent nodes, validates path references, and normalizes transform matrices into standard AssemblyInstanceSpec objects before pipeline processing begins.
What is the difference between native and linked composition?
Native composition (handled in assembly_composition.py) constructs the OCCT scene directly using internal geometry kernels, while linked composition delegates assembly construction to the build123d library. Native composition provides finer control over STEP metadata and joint serialization, whereas linked composition offers simplified API integration for rapid prototyping.
How are transforms handled in hierarchical assemblies?
The assembly_flatten.py module recursively traverses the AssemblyNodeSpec hierarchy and multiplies local transforms using multiply_transforms to calculate each component's final world-space position. This flattening process produces a list of leaf instances with absolute coordinates ready for export to STEP or GLB formats.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →