- C++ 99%
- CMake 0.8%
- Shell 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The CONSTRUCT panel's first three commands are live: Offset Plane, Plane at Angle and Midplane. Built so a loft can be tried on something other than the three origin planes. DatumPlaneFeature (datum_plane) resolves to a SketchPlane and builds to no shape. Offset moves the base along its normal by a distance; angle turns it about a world axis through its origin or about a straight edge of the base's body; midplane sits halfway between two parallel faces of one body or two datums. The base is an origin plane, a planar face, or another datum, and each fails with ErrorCode::Unresolved rather than a guess when what it references is gone. An axis perpendicular to the plane, non-parallel faces for a midplane, and a curved edge as an axis are refused with the reason. Enum-like settings now live on Feature as named text options with a declared value list, serialised generically under "options", cloned with the rest of the state, and refused on load when out of range. showsInput() lets a feature hide inputs and options that do not apply to how it is configured, so an offset plane's dialog offers a distance and nothing else. BuildContext::planeOf and Feature::datumPlane() are how a sketch on a datum finds its plane (SketchFeature::PlaneKind::Datum, serialised as "datum"); Feature::isConstruction() keeps datums out of Document::isBody, the body list, the scene and the boolean host search. The face-plane resolution a face-mounted sketch used is now planeOfReferencedFace, shared with datums. kernel::edgeEndpoints gives a plane at an angle its hinge. The sketch plane picker doubles as the datum base picker: it draws the origin planes (when there are no bodies, or the browser shows them) and every built datum as targets, and answers whichever asked -- Create Sketch or the dialog's From field. Datums draw as translucent labelled quads sized to the model, the dialog's draft in the accent colour, and the browser's Construction node lists them with an eye, a Create Sketch item and the usual context menu. The dialog gains SelectionKind::Plane, options drawn as dropdowns, and OK keyed off the build succeeding rather than a preview shape existing, since construction has none. Not built: Tangent Plane, the two-edge and three-point planes, and the axis and point constructors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Jaxmn8nH523Vpz4S9iadP |
||
| core | ||
| docs | ||
| packaging | ||
| scripts | ||
| tests | ||
| third_party | ||
| ui | ||
| .clang-format | ||
| .editorconfig | ||
| .gitignore | ||
| .gitmodules | ||
| ANVIL_PROFILE_LEAK_BUG.md | ||
| CLAUDE.md | ||
| CMakeLists.txt | ||
| CMakePresets.json | ||
| imgui.ini | ||
| LICENSE | ||
| PLAN.md | ||
| PROGRESS.md | ||
| README.md | ||
Anvil
A lightweight parametric CAD application for Linux, built for 3D printing.
Anvil is a native parametric solid modeller aimed at people who design parts for their own printer: brackets, enclosures, adapters, jigs, replacement parts. The reference experience is Fusion 360's sketch → extrude → modify workflow, without the cloud, the licensing or the bloat.
It is not a mesh editor, a slicer, or a general-purpose FreeCAD clone.
Status: M0–M5, plus the interface described in CLAUDE.md §10. Constraint-solved sketches drawn in the viewport with inferred constraints, extrude and revolve, fillet, chamfer, shell, patterns and mirror, a persistent topological naming scheme that survives parametric edits, and import/export for 3MF, STEP and STL.
Of the 159 commands the toolbar lists, 63 do something; the rest are greyed with a tooltip saying where they stand. The largest gaps are construction geometry (datum planes, axes, points), the hole feature, and the sketch modify tools — trim, extend and offset. PROGRESS.md lists exactly what is and is not built.
Building
Arch Linux (or CachyOS):
sudo pacman -S --needed cmake ninja gcc git clang doctest \
opencascade eigen nlohmann-json minizip-ng pugixml zlib
git clone --recurse-submodules <url> anvil && cd anvil
cmake --preset release
cmake --build --preset release
ctest --preset release
./build/release/bin/anvil
If you cloned without --recurse-submodules:
git submodule update --init --recursive
Presets
| Preset | Purpose |
|---|---|
debug-asan |
Debug with ASan + UBSan. What you develop against. |
debug |
Debug without sanitizers, for when you need the speed. |
relwithdebinfo |
Optimised with symbols, for profiling. |
release |
Optimised. The configuration the performance budgets are measured against. |
scripts/ci.sh runs the full gate: formatting check, then debug-asan and release both
built with -Werror and both running the test suite.
Usage
anvil [options] [FILE.anvil]
FILE.anvil Open a document on startup.
--inspect FILE Dump a document's JSON to stdout and exit.
--startup-timing Print milliseconds from launch to the first drawn frame.
--log-level LEVEL trace, debug, info, warn, error or off.
--width / --height Initial window size.
--no-vsync Disable vertical sync.
--gl-debug Request a debug context and log driver diagnostics.
--inspect reads one entry straight out of the container without building the document,
so it still works on a file this build cannot fully open:
anvil --inspect bracket.anvil | jq .parameters
Modelling
The parametric workflow:
- Create → Create Sketch (on XY, or on a selected planar face)
- Draw with
L,R,C,A. The tool stays armed for the next shape;Escputs it down. Constraints are inferred as you go — the glyph at the cursor is what you will get — and typing a number while dragging locks it as a driving dimension. The sketch palette counts the remaining degrees of freedom. - Finish Sketch, then Create → Extrude (
E) - Switch to edge selection (
R), click edges, Modify → Fillet (F) - Change a parameter — everything downstream rebuilds, and the fillet stays on its edge
Every 3D feature opens a dialog at the right of the viewport with a live preview. Fields
take expressions, so wall_thickness * 2 + 0.4 is a valid size; define named parameters in
the Parameters panel and reference them from any field. Dragging the arrow in the
viewport edits the same value. Enter commits, Esc cancels, and nothing reaches the
document until you accept.
Selecting first pre-fills the dialog; running a command with nothing selected opens it waiting for a pick. Shift-click adds to a selection. Resting the cursor over crowded geometry offers a list of what is under it.
Right-click opens a marking menu — flick in a direction and release to fire that wedge
without the menu ever drawing. S opens a fuzzy command search where any command can be
pinned.
Everything goes through the undo stack — Ctrl+Z and Ctrl+Shift+Z, unlimited.
| Shortcut | |
|---|---|
Ctrl+O / Ctrl+S |
Open / Save |
Ctrl+Z / Ctrl+Shift+Z |
Undo / Redo |
Ctrl+Q |
Quit |
S |
Command search |
Esc |
Back out one level |
Del |
Delete the selected feature |
E / F / Q |
Extrude / Fillet / Press Pull |
B / G / R |
Select bodies / faces / edges |
L / R / C / A |
In a sketch: line / rectangle / circle / arc |
D |
In a sketch: dimension |
Command keys come from the toolbar layout, so the key a tooltip shows is the key that
fires. R is the edge filter outside a sketch and the rectangle tool inside one.
Viewport navigation
Matching Fusion 360's defaults, and rebindable under Preferences → Navigation if you prefer middle-drag to orbit.
| Input | Action |
|---|---|
| Middle drag | Pan |
| Shift + middle drag | Orbit |
| Scroll | Zoom toward the cursor |
H |
Home view |
1–6 |
Front, back, left, right, top, bottom |
F6 |
Fit to the build volume |
O |
Toggle orthographic / perspective |
Layout
core/
base/ math, Result, logging, ids
document/ expressions, parameters, features, commands, undo
sketch/ 2D entities, constraints, ISketchSolver
features/ sketch, extrude, revolve, fillet, chamfer, shell, patterns
kernel/ the ONLY place OpenCASCADE headers are included
io/ .anvil container, JSON, STL, 3MF, STEP, PNG
ui/
app/ window, main loop, session, navigation
commands/ the command registry and the toolbar layout, which is data
render/ GL loader, camera, grid, build plate, mesh and picking
panels/ theme, docked panels, dialogs, sketch view, file dialog
tests/ doctest suites. anvil_core_tests links core alone, which keeps it headless;
anvil_ui_tests drives panels through real ImGui frames with no window.
docs/ file format and the topological naming scheme.
core/ is testable without a GPU or a window, and OpenCASCADE types never escape
core/kernel/ — the core test target compiles without OCCT's include path at all, which is
what proves it. Both rules exist so the geometry kernel stays replaceable
(CLAUDE.md §4).
The sketch solver sits behind ISketchSolver for the same reason. It is SolveSpace's,
vendored under third_party/solvespace — see PROGRESS.md for why, and what
that cost.
How a fillet survives a parametric edit
Change an extrude's height and OpenCASCADE renumbers the faces. A fillet storing "edge 7" would silently round a different corner. Anvil names a face after what produced it — an extrude's wall is seeded by the sketch entity that generated it, and sketch ids do not move when numbers do — and names an edge by the two faces meeting along it.
When a reference genuinely cannot be found, the feature errors rather than guessing. docs/topological-naming.md explains the whole scheme.
File format
.anvil is a ZIP with every entry stored uncompressed, so a saved model is greppable
and diffable:
document.json parameters, feature tree
metadata.json app version, format version, timestamps
grep -a wall_thickness bracket.anvil # works, by design
Licence
GPL-3.0-or-later. See LICENSE.