VernierCAD
A parametric solid modeller for Linux: sketch on a plane or a face, constrain the sketch, extrude it, and edit any of it afterwards without the model falling apart.
GPL-3.0-or-later. Rust workspace over an OCCT kernel reached through a thin C++ façade.
Status, 2026-09-06. Executing milestone M0 of
docs/PLAN_2026-09-06_shapr3d-competitor.md. The core loop works end to end from the GUI — draw with a mouse, constrain, extrude, save, export — and this file says exactly what that does and does not include. It is written to be believed: where something is missing it says so rather than leaving you to find out.PROJECT_BRIEF.mdis the design record and the authority on intent;NAMING.mdis the topological-naming rules;MISTAKES.mdis the ledger of what has already gone wrong;SOLVER.mdcovers the constraint solver;FUSION_LOG.mdtracks what Fusion 360 and Shapr3D express that this cannot.
Running it
WINIT_UNIX_BACKEND=x11 cargo run --release -p vernier-app
The WINIT_UNIX_BACKEND=x11 is not optional if anything other than a human
will touch the window. Under a native Wayland backend the window is
invisible to xdotool — no window id, no synthetic clicks, no screenshots —
so every automated or scripted GUI check silently has nothing to drive. It
looks exactly like the app failing to start. Set it and the window is an X11
client under XWayland, which those tools can see. Launching by hand for your own
use works either way; set it anyway so the two cases do not diverge.
Headless checks need no display at all:
cargo test --workspace # every crate
cargo run --release -p vernier-cli -- --selftest # 43 end-to-end checks, JSON
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
Prerequisites
- Rust 1.96+ (edition 2024).
- OCCT development headers and libraries, plus a C++ toolchain — the
vernier-occt-syscrate compiles a façade against them. - A Vulkan driver. The viewport is
wgpu; the target is AMD RDNA3 via Mesa/RADV, with lavapipe as the software rasterizer for CI.
What you can actually do today
Everything below is reachable from the GUI, with no CLI and no hand-edited
files. The step-by-step walkthrough lives on kanban card 67509e31; this is the
summary.
Sketching. Pick a planar face (or nothing, for world XY) and press Sketch. A banner tells you that you are sketching, which tool is armed, and that Escape leaves. Five tools:
| Tool | Clicks |
|---|---|
| Select | click a curve or a point to select it — without leaving the sketch |
| Line | click, click, click; end on the first point to close the outline |
| Circle | centre, then a point on the rim |
| Arc | centre, start, end — counter-clockwise; start on the chain to join it |
| Point | one click, one point |
Sketch geometry is drawn in the viewport and clickable there. Ctrl extends a selection, exactly as it does for faces.
Constraints — all seventeen are reachable. Horizontal and Vertical on one line; Parallel, Perpendicular, Equal and an Angle dimension on two; a Length dimension on one line; Coincident, a Distance dimension and Point-on-line using picked points; Lock and Unlock to pin a point at typed coordinates; Radius on a circle; Midpoint, Collinear, Concentric and Symmetric; and tangency between a line and a circle or between two circles/arcs. Dimensions commit when you press the button, not on Enter or on blur — the same rule as every other numeric field in the app.
Solids. A closed outline — a circle, a rectangle, or a mixed line-and-arc shape with rounded ends — reports itself closed and can be extruded. Then push/pull a face, fillet or chamfer an edge, shell, draft, pattern, mirror or section, each on a plane you choose.
Documents. Save and open the native JSON format; export binary STL or STEP. Undo and redo, on buttons and on Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y. The title bar shows an asterisk while there is unsaved work, and Open asks before it discards any.
Navigation
Right-drag orbits, middle-drag pans, the wheel zooms, and left-click only ever selects. A left-drag does nothing to the camera; that is deliberate, not a hang.
What you cannot do yet
Stated plainly, because finding these by trial costs more than reading them:
- No file browser. Save, Open and the exports take a path typed into a text field. A native dialog is a dependency decision nobody has made yet.
- No Ctrl+S. File actions are buttons only; undo is the one thing with key bindings.
- Undo never clears the unsaved marker, even if you step back to exactly the state you saved. Knowing it had would need a marker on the undo stack; erring toward "unsaved" is the safe direction.
- Arcs cannot be dimensioned, and an arc's own radius is derived from its points rather than being a constraint you can type.
- Over-constraint reporting names the conflict, not the intent. An
impossible constraint is refused whole, nothing changes, and the message now
lists the dependent constraints the solver found — by their position in the
sketch's constraint list, not by a friendly label
(
crates/vernier-doc/src/sketch.rs:2805-2814). What it cannot tell you is which of them you meant: the set is mutually inconsistent and the solver has no way to rank one member of it as the mistake. - The dogfooding milestones (L0–L2) have not been driven end to end by a person. Automated coverage is good; that is a different claim.
Repository map
| Crate | What it owns |
|---|---|
vernier-occt-sys |
The C++ façade over OCCT and its cxx bridge. Opaque integer handles, never Handle<T>. |
vernier-kernel |
Safe Rust over the façade: feature evaluation, booleans, the topological-naming tables. |
vernier-doc |
The document — features, the sketch model, commands, undo/redo. No kernel, no GPU. |
vernier-solver |
The constraint solver: sparse Jacobian, Levenberg–Marquardt, seventeen constraints. |
vernier-ui |
The recompute orchestrator, the egui shell, the sketch canvas, picking. Sees doc and kernel; owns no window. |
vernier-render |
wgpu viewport: grid, shaded part, sketch overlay, ID-buffer picking, headless render. |
vernier-tess |
Tessellation and STL writing. |
vernier-app |
The winit window, the document server thread, and the event loop that ties it together. |
vernier-cli |
Headless entry points, including --selftest. |
The dependency arrows point downward: vernier-ui may see both vernier-doc
and vernier-kernel; neither may see the other.
The two ideas worth knowing before reading the code
Topological naming is the whole point. Entity ids are allocated by the
document and persisted in naming tables, so a fillet still names the same edge
after the sketch beneath it is edited. NAMING.md is the rulebook. The tables
are keyed by feature identity and are purely additive, which is why undo needs
no separate rollback of them — measured, not assumed: a recompile against a
fresh store renumbers every face.
The kernel never blocks the render thread. Edits go to a document server on its own thread and come back as a scene snapshot. A slow boolean cannot freeze the viewport.
Tests and gates
cargo test --workspace runs everything; --selftest runs 43 end-to-end
checks with a JSON report, including golden geometry and a recompute-cost
gauge. Screenshot and picking tests render through the production pipeline and
read the framebuffer back.
Two conventions this project has paid for and keeps:
- Assert the geometry, not that a command returned
Ok. A constraint that is stored and ignored returnsOk, adds a row, grows the document and leaves the sketch unchanged. Only numbers tell those apart. - Every volume assertion measures the UNION, not the profile. An extrude fuses with the existing body, so a profile drawn over it contributes only the parts sticking out. Put test geometry clear of the starter block, or expect a plausible wrong number.