# How CAD Skills Handles Assemblies and Joints in Text-to-CAD

> Discover how Text-to-CAD handles assemblies and joints, modeling various mechanical relationships including Revolute, Rigid, and Linear joints for complex structures.

- Repository: [earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad)
- Tags: how-to-guide
- Published: 2026-07-28

---

**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`](https://github.com/earthtojake/text-to-cad/blob/main/assembly_spec.py)** defines two primary dataclasses that govern how assemblies are structured:

- **`AssemblyInstanceSpec`** describes concrete STEP parts, encapsulating the instance ID, source file path, local path, 4×4 transform matrix, and color usage flags.
- **`AssemblyNodeSpec`** describes 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:

```python
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`](https://github.com/earthtojake/text-to-cad/blob/main/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`](https://github.com/earthtojake/text-to-cad/blob/main/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 `build123d` library 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`](https://github.com/earthtojake/text-to-cad/blob/main/assembly.py)**, CAD Skills supports five joint primitives from the build123d library:

1. **`RigidJoint`** – Fixes two components together with no degrees of freedom.
2. **`RevoluteJoint`** – Allows rotation around a single axis.
3. **`LinearJoint`** – Permits translation along an axis.
4. **`CylindricalJoint`** – Combines rotation and translation along the same axis.
5. **`BallJoint`** – Provides spherical rotation with multiple degrees of freedom.

### Creating Joints in assembly.py

The **[`assembly.py`](https://github.com/earthtojake/text-to-cad/blob/main/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:

```python
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`](https://github.com/earthtojake/text-to-cad/blob/main/generation.py)** module orchestrates the workflow:

```python
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`](https://github.com/earthtojake/text-to-cad/blob/main/step_export.py)** and **[`glb.py`](https://github.com/earthtojake/text-to-cad/blob/main/glb.py)**, determining whether the output contains joint annotations and hierarchical metadata or represents a single solid body.

## Summary

- **CAD Skills** uses `AssemblySpec`, `AssemblyInstanceSpec`, and `AssemblyNodeSpec` dataclasses in [`assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/assembly_spec.py) to define hierarchical mechanical structures.
- The **`assembly_spec_from_payload()`** function validates user input and forbids duplicate sibling names during hierarchy normalization.
- **[`assembly_flatten.py`](https://github.com/earthtojake/text-to-cad/blob/main/assembly_flatten.py)** resolves world-space transforms by multiplying cumulative transformation matrices down the hierarchy.
- Joint instantiation occurs in **[`assembly.py`](https://github.com/earthtojake/text-to-cad/blob/main/assembly.py)**, creating build123d `Joint` objects (Revolute, Rigid, Linear, Cylindrical, and Ball) attached to a labeled `Compound`.
- The `AssemblySpec.kind` field controls export behavior in [`step_export.py`](https://github.com/earthtojake/text-to-cad/blob/main/step_export.py) and [`glb.py`](https://github.com/earthtojake/text-to-cad/blob/main/glb.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`](https://github.com/earthtojake/text-to-cad/blob/main/assembly.py) using the `add_axis_frame` helper function.

### How does CAD Skills validate assembly structures?

Validation occurs in [`assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/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`](https://github.com/earthtojake/text-to-cad/blob/main/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`](https://github.com/earthtojake/text-to-cad/blob/main/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.