Dxf
Generate, regenerate, and validate 2D DXF drawings from Python build123d sources. Use for DXF files, `.py` drawing scripts, @dxf models, 2D profiles, outlines, templates, gaskets, panels, flat patterns, laser/plasma/waterjet cut layouts, and 2D drawing exports of CAD geometry.
- Skill ID
- earthtojake/text-to-cad/dxf
- Publisher
- earthtojake
- Repository
- text-to-cad
- Installs
- 0
- Files
- 5
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
earthtojake/text-to-cad/dxfInstalls these files- LICENSE
- SKILL.md
- agents/openai.yaml
- references/generator-templates.md
- requirements.txt
What this skill tells the agent
DXF generation and validation
Provenance: maintained in earthtojake/text-to-cad. Use the installed local skill files as the runtime source of truth; the repository link is only for provenance and release review.
Setup
This skill's commands are thin entrypoints over the cadgen distribution, which carries the Python build runtime and the JavaScript it executes. Install it once:
python -m pip install -r requirements.txtDrawings are build123d geometry, so a drawing build loads the CAD kernel like a STEP build does (~2.5s cold; the warm daemon absorbs it on re-runs). Only cadgen dxf snapshot additionally needs Node 20 or newer on `PATH` — it meshes the flat pattern on demand through a bundled Node one-shot; a missing node is reported at render time.
Purpose
Create or modify 2D DXF drawings from natural-language requirements or from CAD geometry, generate validated drawing artifacts, and return checked outputs. A DXF drawing's source of truth is a Python file named <name>.py defining one parameterless @dxf model function.
A drawing is a model. It has the same wrapper, record, freshness gate and build job a @step part has; its one output is the .dxf file; it has no geometry tree (nothing links to a drawing). Every run writes the sibling <name>.dxf (or the out= the decorator names); an unchanged source is a no-op; a drawing that calls a part model — bracket() inside its body — is stale whenever that part's GEOMETRY changes and current when it does not; cadgen store why <drawing>.py explains the verdict; --force rebuilds it anyway. The CAD Viewer and dxf snapshot read the .dxf file itself, so the file you hand a cutting service and the file the viewer renders are one and the same.
The contract
A `@dxf` function takes no parameters and returns build123d 2D geometry. The engine writes the DXF. You never construct a document, name a file, or place an entity — the same division of labor @step has.
from cadgen import build123d as bd
from cadgen import dxf
HOLE_D = 4.5
@dxf
def gasket():
with bd.BuildSketch() as cut:
bd.Rectangle(60, 40)
bd.Circle(HOLE_D / 2, mode=bd.Mode.SUBTRACT)
return cut.sketch # bare shape -> the CUT layer
if __name__ == "__main__":
gasket()- Bare shape → one
CUTlayer. That is the whole contract for most drawings. - `{layer: shape}` → named layers, when the drawing genuinely has more than one CAM operation (
CUT/ENGRAVE/SCORE). ACompoundwhose children are all labelled means the same thing. - No parameters. Dimensions are module constants (
HOLE_D = 4.5) or constants imported from the part the drawing derives from; a different drawing is a different file. - Text is
bd.Text(...)engraved OUTLINES on a marking layer, never a DXFTEXTentity: cut and marking toolchains consume geometry, and font rendering inside CAM is unreliable. - Geometry must lie in the XY plane. A face taken from a solid sits at that solid's height; relocate it (
flatten.flatten_face(face), orbd.Location((0, 0, -z)) * face). The engine REFUSES off-plane geometry rather than silently writing its XY shadow. - Output bytes are a function of the geometry. Layers are sorted by name and entities by geometric content, so an unchanged drawing rebuilds to an identical file, cold or warm, on any machine.
The three DXF workflows
Copy the full template for the applicable workflow from references/generator-templates.md when creating a new drawing.
- Drafted from scratch (gaskets, panels, templates, cut layouts with no 3D model behind them): a
<name>.pythat builds sketches and returns them.
- Flat pattern of a generated STEP part: a drawing script beside the model it derives from, with its OWN stem (one model per file —
bracket_drawing.pybesidebracket.py). Import the model and call it, exactly as an assembly composes a child: importing never builds, and inside the drawing's build the call returns the part's geometry (building the part first if it is stale).
from cadgen import dxf, flatten
from bracket import bracket # a child: tracked by its RESULT
KERF = 0.15
@dxf
def bracket_drawing():
return flatten.flat_pattern(bracket(), coordinate=3.0, kerf=KERF)
if __name__ == "__main__":
bracket_drawing()The drawing's record pins the part's tree, so a part edit that changes its geometry makes the drawing stale, and one that does not (a comment, a refactor, a colour) leaves it current. Constants imported from the part (from bracket import THICKNESS) are tracked by value the same way.
- Flat pattern of an imported STEP (a
.step/.stpwith no Python source): read it withcadgen.read_step, notbuild123d.import_step. It records the file's content hash as a build INPUT, so replacing the vendor STEP makes the drawing stale on its own, with no--force; read it through build123d and the drawing stays "current" against a file that changed underneath it.
from pathlib import Path
from cadgen import dxf, flatten, read_step
_HERE = Path(__file__).resolve().parent
KERF = 0.15
@dxf
def panel_flat():
panel = read_step(_HERE / "imported" / "vendor_panel.step") # recorded input
return flatten.flat_pattern(panel, coordinate=3.0, kerf=KERF)
if __name__ == "__main__":
panel_flat()Never read a STEP this project generates. Reading the .step a @step model writes is not a loop, it is a drawing whose input changes on every run of the model: the freshness gate can never say "current", every build is a full rebuild, and the flat pattern depends on what the last run left on disk. Keep source STEPs in an imported/ directory beside the drawing, committed like any other input — input path and output path being different files is the whole rule. For a STEP this project DOES generate, use workflow 2 instead: import the model script and call it, which is tracked by result and never touches an artifact.
One model per file is the recommendation, and a drawing gets its own script: a file MAY declare several models — two @dxf drawings, or a @dxf beside a @step — and each is its own record, output and job (a sole model writes <file>.dxf; models sharing a file write <function>.dxf), but they share the file's closure, so editing one rebuilds them all. A drawing composes models, never the reverse: calling a @dxf function from a @step body is just its 2D geometry and links nothing. The viewer catalog is artifacts-only: scripts never list; the .dxf the run writes is the entry the viewer renders.
Use this skill when
Use this skill when the user asks for DXF files, 2D drawings, profiles, outlines, templates, gaskets, panels, flat patterns, or cut layouts for laser, plasma, waterjet, or CNC routing.
Use $cad for the 3D part or assembly a DXF derives from. Use $sendcutsend for SendCutSend-specific upload preflight.
Defaults
Use these defaults unless the user specifies otherwise:
- Units: millimeters. The engine sets them; a drawing never declares units.
- Geometry lives at 1:1 scale in the XY plane.
- Cut profiles close. Open contours belong on bend/engrave/reference layers — generation validation enforces this (see Validation).
- For CAD-backed parts, derive contours from the real topology with
cadgen.flattenrather than redrawing them:planar_facesselects,flatten_facelays a face into XY exactly,union_facesfuses, andflat_patterndoes all of it in one call. Hand-drawn parametric outlines only when there is no reliable 3D topology. - Kerf / tool-radius compensation is
flatten.offset_profile(shape, amount)orflat_pattern(..., kerf=...); never hand-offset coordinates.
More from earthtojake
Use this skill in a project
Open any RiverX project, search the Skills panel for earthtojake/text-to-cad/dxf, and install it in one click.
