# Converting 3D STEP Geometry to 2D Cut Layouts for Laser, Plasma, and Waterjet Cutting

> Convert 3D STEP to 2D cut layouts for laser, plasma, and waterjet. Explore the text-to-cad pipeline using build123d and ezdxf for efficient production-ready DXF files.

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

---

**The text-to-cad repository provides a modular pipeline that converts 3D STEP files into production-ready DXF cut layouts through separate CAD and DXF skills, using build123d for solid modeling and ezdxf for validated 2D projection.**

This open-source toolkit from `earthtojake/text-to-cad` automates the entire workflow from design intent to manufacturing files. Its layered architecture ensures that 3D geometry and 2D cut layouts stay synchronized, eliminating manual redrawing and reducing errors for CNC cutting processes.

## How the Conversion Pipeline Works

The repository organizes functionality into three orthogonal layers that enforce clean separation between geometry creation, format conversion, and visualization.

| Layer | Purpose | Location |
|------|---------|----------|
| **Skills** | Reusable agent workflows (CAD, DXF, G-code slicing) | `skills/` |
| **Packages** | Shared runtime utilities (viewer, rendering, geometry) | `packages/` |
| **Applications** | End-user tools (CLI, web viewer, benchmarks) | `viewer/`, `scripts/`, `benchmarks/` |

### The CAD Skill: Creating Validated STEP Geometry

Located at `skills/cad/`, this skill generates STEP-first geometry using **build123d** Python sources. Running `python scripts/step <source.py>` produces:

- A validated `.step` (or `.stp`) artifact
- Optional secondary meshes (STL, 3MF, GLB)

The skill enforces unit consistency, origin placement, and assembly structure through conventions documented in [`skills/cad/SKILL.md`](https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/SKILL.md). Every CAD generator must implement `gen_step()` as its entry point.

### The DXF Skill: Projecting to 2D Cut Layouts

The `skills/dxf/` skill consumes Python sources defining `gen_dxf()` — or automatically projects from existing `gen_step()` definitions. Key capabilities:

- **Automatic face projection** from STEP topology when working with CAD-backed sources
- **Validation via ezdxf** for layer names, closed polylines, and required holes
- **Default units** and material-aware offsets for kerf compensation

This ensures your laser, plasma, or waterjet cut layout matches the true 3D geometry without manual tracing.

## Step-by-Step: From STEP to DXF

Follow this command sequence to generate a validated cut layout:

```bash

# 1. Generate STEP from build123d source

python scripts/step src/bracket.py -o models/bracket.step

# 2. Project to 2D DXF automatically

python scripts/dxf src/bracket.py -o models/bracket.dxf

# 3. Launch viewer for visual confirmation

cad-viewer launch --dir "$(pwd)/models"

```

The DXF skill validates outputs before handoff, checking that all cutting contours are closed polylines and hole layers are properly named for post-processors.

## Key Technical Implementation Details

### Generator Function Signatures

Both skills expect specific Python function signatures in source files:

```python
import build123d as bd

def gen_step():
    """Return a build123d Compound, Solid, or Assembly.
    Called by scripts/step to produce STEP output."""
    plate = bd.Box(120, 60, 5)
    # ... feature operations ...

    return plate

def gen_dxf():
    """Return an ezdxf document.
    Called by scripts/dxf for 2D cut layout generation."""
    import ezdxf
    doc = ezdxf.new(setup=True)
    msp = doc.modelspace()
    # ... projection logic ...

    return doc

```

### Handoff Policy to CAD Viewer

Every skill must hand artifacts to `$cad-viewer` per [`SKILL.md`](https://github.com/earthtojake/text-to-cad/blob/main/SKILL.md) specifications. This launches the web-based viewer at `viewer/vite.config.mjs` entry points, returning a shareable URL for stakeholder review. The policy is enforced across all skills — CAD, DXF, and future formats like G-code.

### Shared Package Architecture

Runtime code in `packages/` prevents cross-skill imports (enforced by tests):

- **`packages/cadjs/`** — JavaScript viewer utilities for scene scaling, edge display, and screenshot capture
- **`packages/implicitjs/`** — Experimental GLSL-based implicit CAD engine for browser-native rendering

Both are bundled via [`scripts/bundle/bundle.sh`](https://github.com/earthtojake/text-to-cad/blob/main/scripts/bundle/bundle.sh) before release.

## Validating Output for Manufacturing

The pipeline includes deterministic checks that downstream CAM systems can trust:

| Validation | Tool | Location |
|-----------|------|----------|
| STEP geometry inspection | `scripts/inspect` | `skills/cad/` |
| DXF closed polyline check | `ezdxf` query | `skills/dxf/` |
| Layer naming conventions | `ezdxf` layer API | `skills/dxf/` |

For laser/plasma/waterjet cutting, the DXF skill specifically verifies that outer contours and inner holes are on separate layers with consistent color codes — a requirement for many nesting software packages.

## Complete Working Example

This end-to-end example creates a plate with mounting holes, exports STEP, and generates a matching DXF cut layout:

```bash

# Install skills library

npx skills install earthtojake/text-to-cad

# Create generator source

cat > src/mounting_plate.py <<'PY'
import build123d as bd

def gen_step():
    # 200mm x 100mm x 6mm aluminum plate

    plate = bd.Box(200, 100, 6)
    
    # Four M8 clearance holes

    hole_pattern = [
        (-80, -35), (80, -35),
        (-80, 35), (80, 35)
    ]
    for x, y in hole_pattern:
        hole = bd.Cylinder(r=4.5, h=10).translate((x, y, 0))
        plate = plate.cut(hole)
    
    return plate

def gen_dxf():
    """Generate 2D cut layout from projected faces."""
    import ezdxf
    from ezdxf import units
    
    doc = ezdxf.new(setup=True)
    doc.units = units.MM
    msp = doc.modelspace()
    
    # Cutting contour on standardized layer

    msp.add_lwpolyline(
        [(-100, -50), (100, -50), (100, 50), (-100, 50)],
        close=True,
        dxfattribs={"layer": "CUT_CONTOUR", "color": 1}
    )
    
    # Holes on separate layer

    for x, y in [(-80, -35), (80, -35), (-80, 35), (80, 35)]:
        msp.add_circle(
            (x, y),
            radius=4.5,
            dxfattribs={"layer": "CUT_HOLES", "color": 3}
        )
    
    return doc
PY

# Execute pipeline

python scripts/step src/mounting_plate.py -o models/mounting_plate.step
python scripts/dxf src/mounting_plate.py -o models/mounting_plate.dxf

# Validate DXF structure

python -c "
import ezdxf
doc = ezdxf.readfile('models/mounting_plate.dxf')
print('Units:', doc.units)
print('Layers:', list(doc.layers.names()))
print('Contours:', len(doc.modelspace().query('LWPOLYLINE')))
print('Holes:', len(doc.modelspace().query('CIRCLE')))
"

# View results

cad-viewer launch --dir "$(pwd)/models"

```

## Why This Architecture Matters for Manufacturing

**No geometry duplication** — The DXF skill projects from the same STEP source rather than maintaining parallel 2D drawings. When the 3D model changes, re-running `scripts/dxf` guarantees the cut layout synchronizes automatically.

**Format extensibility** — Adding waterjet-specific lead-in geometries or plasma kerf tables only requires a new skill. The core CAD and viewer packages remain unchanged.

**Deterministic validation** — Each skill implements its own quality gates. The `scripts/inspect` tool for STEP and ezdxf-based checks for DXF ensure that SendCutSend, OMAX, or Hypertherm post-processors receive valid inputs.

## Summary

- **`skills/cad/`** generates validated STEP geometry via `gen_step()` and build123d
- **`skills/dxf/`** projects to 2D cut layouts via `gen_dxf()` with ezdxf validation
- **`$cad-viewer`** handoff policy provides immediate visual confirmation for every artifact
- **Shared packages** in `packages/cadjs/` and `packages/implicitjs/` power the web viewer without cross-skill dependencies
- **CLI entry points** at `scripts/step` and `scripts/dxf` wrap Python generators for shell and agent integration

## Frequently Asked Questions

### Can I convert existing STEP files without rewriting them in Python?

Yes. The DXF skill can consume any STEP file through a generator that imports external geometry. Create a minimal `gen_step()` that loads your existing file with `build123d.import_step()`, then define `gen_dxf()` to project the faces you need for cutting. The skill architecture treats file-based and code-based sources identically.

### What kerf compensation does the DXF skill apply?

Kerf width compensation is configurable per [`skills/dxf/SKILL.md`](https://github.com/earthtojake/text-to-cad/blob/main/skills/dxf/SKILL.md) defaults. The skill applies material-and-process-specific offsets (typically 0.1mm for laser, 0.5–1.5mm for plasma) through `ezdxf` geometry transformation before layer assignment. You override defaults via generator arguments or CLI flags.

### How do I ensure hole positions match between STEP and DXF?

When both `gen_step()` and `gen_dxf()` exist in the same source file, derive hole coordinates from shared constants or helper functions. The DXF skill validates against the STEP geometry when CAD-backed projection is available, flagging deviations exceeding tolerance thresholds.

### Does the viewer support DXF files directly?

Yes. `skills/cad-viewer` renders DXF alongside STEP, STL, and GLB formats. The Vite-based viewer at `viewer/vite.config.mjs` handles `LWPOLYLINE`, `CIRCLE`, and `ARC` entities common to laser cutting workflows, with layer toggle controls for contour visualization.