A from-scratch 3D CAD application
- Based on the original KodaCAD
- Built on OCP (Python bindings for OpenCascade's OCCT) -- see below for how this differs from other Python CAD libraries
- Patterned after HP's dynamic modeling lineage (ME-30 → SolidDesigner)
- Part creation workflow: workplane → sketch → extrude/revolve → modify
- Intended to meet the basic requirements in a typical CAD workflow:
- Start a new session or load a previous one (saved as a
.kcadfile: the STEP model plus your workplanes) - Import components in STEP format
- Position them within an assembly structure
- Create simple mounting plates & brackets
- Modify parts directly, even ones you didn't build: remove features (Defeaturing) and push or pull faces (Move Face) with a live preview
- Save session as
.kcad, or export the 3D model as plain STEP
- Start a new session or load a previous one (saved as a
Designed for users who want to assemble, modify, and create 3D mechanical parts and assemblies using a scriptable, open-source toolchain -- with no commercial dependencies and no Conda environment.
A STEP file arrives with no feature tree. KodaCAD lets you edit it directly, the way the HP/CoCreate lineage did:
- Move Face -- pick a face (or a box of faces), type a distance, and watch a live ghost of the result before you commit. A positive distance adds material (thickens a wall, narrows a slot); a negative one removes it (opens a hole up for a clearance fit). Faces that must move with the one you picked show in yellow, and heavy green or magenta lines mark exactly what changes.
- Defeaturing -- remove a hole, a pocket, a blend or a whole isolated feature, and the surrounding faces heal over the gap.
- They work together. When OpenCascade cannot build an offset, Move Face marks the faces that stand in its way in red and tells you what to remove first; Defeaturing takes them out, and the move goes through.
Try it on a real bracket in the Moving Faces tutorial, and see Defeaturing a Part for the other half.
Most Python CAD libraries (build123d, CadQuery) represent assemblies as copies of shapes organized in a Python object tree. This means modifying one instance of a repeated part only affects that copy.
KodaCAD uses OCCT's XDE document model directly. In XDE, repeated parts are stored once as a prototype shape and placed multiple times as instances (references with location transforms). Modifying the prototype automatically updates all placements -- exactly the behavior you expect from professional CAD software like Creo or CoCreate.
Example: the as1-oc-214.stp test assembly has two L-bracket
assemblies, each containing the same L-bracket part. Applying a fillet
to one L-bracket updates both simultaneously, because they share the
same XDE root label.
- Python programmers who want to work with real STEP assemblies
- Users coming from CoCreate / Creo who want familiar assembly workflow
- Developers who want to understand OCCT's XDE document architecture
- Anyone who needs to load, modify, and save STEP files with full assembly structure preserved
KodaCAD uses uv for dependency management. No Conda required.
-
Install uv if you don't have it
-
Clone this repo
git clone https://github.com/dblanding/kodacad2
cd kodacad2
- Run directly -- uv handles all dependencies automatically
uv run kodacad.py
Dependencies (managed automatically by uv):
ocp-- OCCT Python bindings (replaces PythonOCC)pyside6-- Qt6 Python bindings (replaces PyQt5)build123d-- used for geometry utilities
KodaCAD's native session file is a .kcad file. It is an ordinary
zip archive holding two things side by side, in the spirit of NumPy's
.npz:
mypart.kcad
mypart.stp the 3D model -- a standard STEP file
workplanes.json the workplanes, plain text
The STEP half carries everything a STEP file can: the assembly
structure, part names, colors, shared instances. The JSON half carries
what STEP has no place for: each workplane's position in 3D, its
sketched profile geometry (lines, circles, arcs), its construction
geometry, which workplanes are hidden, and which one was active. The
file is plain data -- nothing in it is executed when it loads -- and
because it is a zip, you can always open it with any archive tool and
pull out the .stp by hand.
File → Save Session
Writes a .kcad file holding the model and your workplanes. The file
is written to a temporary name and swapped into place only when
complete, so a failed save never damages an existing file.
File → Load Session
Opens a .kcad file -- or any plain .stp / .step file with
assembly structure. Replaces the entire 3D document -- whatever was
open before is gone. The loaded file's own content becomes the session,
with no wrapping or nesting involved.
What happens to workplanes depends on the file. A .kcad file brings
its own workplanes and replaces the ones on screen. A plain STEP file
has no workplanes to bring, so it leaves your current workplanes
alone -- handy for loading a part or assembly and sketching on it
with the workplanes you already have set up.
File → Export STEP Writes the 3D model alone, as a plain STEP file, for opening in other CAD applications or sharing with people who don't use KodaCAD. No workplanes are included.
File → Import STEP
Adds a STEP file as a new component under the current /, alongside
whatever's already open, ready to be positioned and dragged into an
assembly. This is the one operation that nests a file's content under
an existing root, so it's the one that needs to guard against a saved
session's own / wrapper getting nested under the current one --
which it does automatically (see below).
- Undo/Redo history. Loading starts you with a clean history.
- Display-only face colors (Color Faces...), which are an illustration aid, not part of the model.
- A workplane's link to the face it was placed on. The workplane keeps its position in space; it just no longer remembers which face it came from.
Undo (Ctrl+Z) and Redo (Ctrl+Y) step through changes to the 3D model -- extrudes, pulls, fillets, shells, positioning, renaming and so on. Workplanes and their sketch geometry are not part of that history. Drawing a line, deleting an element, or repositioning a workplane cannot be undone with Ctrl+Z, and undoing a 3D operation will not rewind your sketches. Workplanes are your construction scaffolding rather than the model itself, and they live outside the document that Undo/Redo manages.
In practice this means: Undo is safe to use freely on the model without
disturbing your sketches, but a workplane edit is final until you make
the opposite edit yourself. Since a workplane's state is only stored
when you save, Save Session before experimenting with sketch work
you might want back -- the .kcad file is your safety net for
workplanes.
Save strips any
redundant / wrapper before writing, so a saved file never contains
one to begin with. Load Session replaces the whole document rather
than nesting anything, so there's nothing for a wrapper to accumulate
into. Import STEP is the one path that adds content under an existing
root, so it explicitly unwraps any /-named root in the file being
imported before adding its children -- composing locations through any
already-nested legacy wrapper levels in one pass, so even an older
file saved before this existed unwraps completely on import.
Note on the tree's own / label: the tree view always shows a /
under the 3D header, whether or not anything is loaded -- that's
just the tree's own organizing scaffolding, not part of any document.
The document's actual root assembly label is also named / by
convention (visible in the tree as its child), which is why a
freshly-built, never-saved session can show what looks like / nested
under / -- two different things that happen to share a name, not an
accumulation bug.
The tree on the left shows the assembly hierarchy:
WP ← workplanes (2D construction geometry)
wp1
3D
/ ← root of 3D assembly
as1 ← loaded assembly
rod-assembly_1
l-bracket-assembly_1
...
button ← newly created part (ready to drag)
- Checkbox — show/hide part or assembly. Parent checkbox propagates to all children.
- Left-click — select item
- Right-click — context menu: Item Info, Set Active, Rename, Create New Assembly, Create Shared Instance, Set Transparent, Set Opaque, Delete
Full, step-by-step walkthroughs live under tutorials/ --
each one doubles as a regression test, with every step annotated with
what it exercises in KodaCAD's own code.
- The OCC Bottle -- builds the classic OpenCascade tutorial bottle entirely from scratch: sketching, Extrude, Fillet, Mill/Pull, Shell, and Undo/Redo, all on a single part.
- Assembly Structure with
as1-oc-214.stp-- loading a pre-built, multi-part assembly two different ways, the XDE hierarchy viewer, and fillet propagation across a pre-existing shared instance. - Build Assembly
as1from a Kit of Parts -- the reverse of the tutorial above: starting fromas1-oc-214's five loose, un-positioned prototype parts and rebuilding the real assembly from scratch, exercising nearly every Position dialog method along the way. - Building Quaoar's Chassis -- building a wheel-and-axle-in-a-chassis assembly from nothing: sibling-assembly creation, reparenting, shared instances (both a part and an assembly), the Position dialog's full surface, and Undo/Redo across a long, mixed operation history.
- Organizing an Assembly -- organizing a flat file in which all parts are a direct child of the root (often with cryptic names), into a hierarchical structure of functional subassemblies, making it easier to navigate.
- Sketching a Jack-o'-Lantern -- a full workout of the 2D sketch toolbar building a jack-o'-lantern face, then reusing the OCC Bottle's extrude-then-heavily-fillet technique to carve that same face into a pumpkin with a real, multi-profile Pull operation.
- Defeaturing a Part -- demonstrating the ability to remove holes and faces and to extend adjacent faces to heal the gap.
- Lofting a Hollow Part Through a Workplane Set -- building a simple lofted part then working it over: subtracting one lofted part from another, a transverse hole, fillets, and finally defeaturing to take the fillets back off.
- Moving Faces --
pushing and pulling the faces of an imported L-bracket (
L-brkt.stp) with a live preview: thickening a wall, opening up a slot, widening holes (box select and Ctrl+click to unpick), and what to do when OpenCascade cannot build an offset -- the red "suspect" faces, and Defeaturing to fix them.
- Load
as1-oc-214.stp - Set
l-bracket_1as active part - Apply fillet -- both L-brackets update simultaneously
- Rename a bolt -- all shared instances update
- Save session
- Reload -- modifications preserved
- Load
as1-oc-214.stp - Place workplane on top face of right L-bracket
- Create a new part
- RMB click on '/' iin tree; select "Create Empty Part"; name "button"
- Draw 5mm circle on workplane
- Creare/Modify -> Pull; Distance: 3mm
- Button appears under
/alongsideas1 - Drag button to
l-bracket-assembly_2 - Button appears in BOTH L-bracket assemblies (shared instance)
- Save and verify in CAD Assistant -- color and name preserved
kodacad2/
kodacad.py main entry point
mainwindow.py MainWindow, TreeView
docmodel.py XDE document model (core -- read this first)
workplane.py 2D workplane geometry
wp_io.py .kcad session container + workplane save/restore
m2d.py 2D sketch toolbar
koda_viewport.py OCP 3D viewport (AIS_ViewController)
stepanalyzer.py STEP structure analysis utility
face_offset.py Move Face geometry (per-face offset, no GUI)
face_offset_server.py persistent worker that computes the live preview
face_offset_worker.py one-shot worker used by Apply
offset_preview.py Qt client for the preview worker
rpnCalculator.py RPN calculator widget
version.py version string
OCCUtils/ OCC geometry utilities (ported from PythonOCC)
icons/ toolbar icons
step/ sample STEP files
docs/
DEVELOPMENT_LOG.md session-by-session development history
TODO.md broken items + future ideas
tutorials/
occ-bottle/ build the OCC bottle from scratch
as1-oc-214/ load and explore a pre-built assembly
build-as1-oc-214/ rebuild that same assembly from loose parts
chassis/ build an assembly from scratch (Quaoar's chassis)
lathe/ organizing and navigating a large assembly
jack/ sketch a jack-o'-lantern face, then carve a pumpkin
defeaturing/ remove holes and features, heal the gap
lofting/ loft through a workplane set, then work it over
move-face/ push/pull faces of an L-bracket, with live preview
pyproject.toml uv project file
docmodel.py is the heart of KodaCAD. It wraps OCCT's XDE
(XCAFDoc_DocumentTool, XCAFDoc_ShapeTool, XCAFDoc_ColorTool) and
provides parse_doc() which builds part_dict and label_dict from
the live document. Everything else in the application reads from these
two dicts.
The XDE label hierarchy:
0:1:1:1 as1 (assembly) <- GetFreeShapes returns this
0:1:1:2 rod-assembly <- prototype shapes at root
0:1:1:3 nut
...
0:1:1:1:1 rod-assembly_1 => 0:1:1:2 <- component (instance)
0:1:1:1:2 l-bracket-assembly_1 => 0:1:1:5
OCP vs PythonOCC: Static methods get _s suffix in OCP.
GetApplication() → GetApplication_s(). See docs/DEVELOPMENT_LOG.md
for the complete list of API changes.
Mouse picking has two separate pipelines -- native OCCT 3D
selection and the 2D sketch engine's own pixel-to-workplane math --
that converge on one callback dispatcher. See
docs/PICKING_ARCHITECTURE.md for
the full picture, including a trap worth knowing about before editing
selection-mode code.
