Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SolveCraft

SolveCraft is an open-source parametric 3D CAD application written in pure Rust. It works the way Fusion-style modellers do: sketch on a plane, constrain and dimension the sketch, turn profiles into solids with features on a timeline, and change a parameter to watch the whole timeline rebuild.

It runs as:

  • a desktop app for Windows and Linux (egui + wgpu);
  • a web app in the browser (wasm, WebGPU or WebGL2), try it here;
  • a headless CLI, solvecraft-cli, that runs command scripts, measures and exports;
  • an MCP server that AI agents model with, headless or driving the running app.

Everything the user can do is a command with JSON parameters. The toolbar, the dialogs, scripts, the CLI, the control channel and MCP all run the same commands, so anything you can click you can also script, test or hand to an agent.

SolveCraft is early. Sketching, solid modelling, parameters, components with joints, sheet metal and a plastic-enclosure subset work; surfaces, drawings, CAM and simulation are deferred. The roadmap has the details, and command parity and the Fusion oracle measure how far along it is.

This book has two halves: the user guide for people modelling with SolveCraft, and the developer guide for people working on it. The command reference is generated from the code on every build.

SolveCraft is licensed MIT OR Apache-2.0. It is a clean-room implementation and is not affiliated with Autodesk; Fusion is a trademark of Autodesk, Inc.

Getting started

Desktop

Download the latest build from the releases page, or build it yourself with stable Rust (1.90 or newer):

git clone https://github.com/bherbruck/solvecraft && cd solvecraft
cargo run --release -p solvecraft -- --sample   # opens with a sample part

solvecraft part.step opens a STEP, IGES, 3MF, STL or OBJ file as a new design, and solvecraft design.solvecraft opens a saved design. Windows builds cross-compile from Linux with cargo xwin build --release --target x86_64-pc-windows-msvc -p solvecraft.

Web

The same app runs in the browser: open SolveCraft on the web (?sample opens the sample part, ?webgl forces WebGL2 where WebGPU is missing). Designs are saved in the browser’s private file storage, which needs HTTPS or localhost. To build it yourself:

rustup target add wasm32-unknown-unknown
cd apps/solvecraft-web && trunk serve --release

CLI

solvecraft-cli runs the same commands without a window:

cargo run --release -p solvecraft-cli -- commands                       # every command, as JSON
cargo run --release -p solvecraft-cli -- run examples/bracket.json --out bracket.step
cargo run --release -p solvecraft-cli -- eval examples/bracket.json     # volume, area, faces…
cargo run --release -p solvecraft-cli -- snapshot examples/bracket.json --out bracket.png
cargo run --release -p solvecraft-cli -- exec solid.box '{"length": 20, "width": 10, "height": 5}'

A script is a list of commands:

{"commands": [
  {"command": "sketch.create", "params": {"plane": "XY"}},
  {"command": "sketch.rectangle.two_point", "params": {"p0": [0, 0], "p1": [40, 30]}},
  {"command": "sketch.finish", "params": {}},
  {"command": "solid.extrude", "params": {"distance": 20}},
  {"command": "solid.fillet", "params": {"edges": [[0, 0, 10]], "radius": 3}}
]}

Units are millimetres and degrees unless an expression says otherwise ("1 in", "30 deg"), and Z is up.

A tour of the window

  • Title bar. On Windows and Linux the application bar is the window’s title bar: File, save, undo and redo, the start page, the theme, Help, and one tab per open design (each with its own undo history, selection and camera).
  • Toolbar. The workspace switcher (DESIGN) sits on the left. The tabs (SOLID, SURFACE, SHEET METAL, PLASTIC, UTILITIES, and SKETCH while you sketch) hold panels of commands. Click a panel’s name for all its commands.
  • Browser (left). The design as a tree: document settings and units, named views, the origin, bodies, sketches, construction planes, canvases, joints and components. The eye shows or hides an item, a component’s radio button makes it the active component, and right-clicking any row gives that item’s menu. Rows drag into groups and other components.
  • Viewport. Click to select, Shift or Ctrl to add, drag for a window or crossing box. The wheel zooms at the cursor, the middle button pans and Shift+middle orbits (the mouse preset in Preferences switches to SolidWorks or Inventor style). The view cube and the navigation bar change the view. Right-click for the marking menu: eight commands around the cursor, plus a menu for whatever is under it.
  • Dialogs dock on the right with a live preview. Most features also show drag handles and a value box in the viewport.
  • Timeline (bottom). Every feature in order. Double-click to edit, drag the marker to roll back, and drag features to reorder them.
  • S box. Press S anywhere for a command search at the cursor. Pin the commands you use most to its top.

Preferences (File › Preferences) hold the theme, the units of new designs, the mouse preset, the visual style and autosave. Help › Keyboard Shortcuts lists every key, and File › Keyboard Shortcuts changes them.

Sketching and constraints

A sketch lies on a plane: an origin plane, a construction plane or a planar face. Start one with Create Sketch (sketch.create) and click a plane, or pick a face first. Finish Sketch returns to the model.

Geometry

Lines and polylines (L), rectangles (two-point R, three-point, centre), circles (centre C, two-point, three-point, tangent), arcs (three-point, centre, tangent), polygons, slots, ellipses, fit-point and control-point splines, conics, points and text. Construction (X) turns curves into reference geometry that doesn’t form profiles. Modify tools include trim (T), extend, break, fillet, chamfer, offset (O), mirror, patterns, move/copy and scale.

Project (P) and Intersect bring model edges into the sketch as linked geometry that follows later changes. Sketching on a face projects its edges automatically.

Constraints and dimensions

The solver is SolveCraft’s own (damped Gauss–Newton with degree-of-freedom analysis). Constraints: coincident, horizontal/vertical, parallel, perpendicular, tangent, equal, concentric, collinear, midpoint, symmetry, fix, curvature (G2) and polygon. Sketch Dimension (D) adds linear, aligned, horizontal, vertical, radius, diameter and angle dimensions. Each is a parameter (d1, d2, …), so it can hold an expression.

Under-constrained geometry is blue and fully constrained geometry turns dark, and the status shows the remaining degrees of freedom. A constraint or dimension that would over-constrain the sketch is refused rather than applied. Drag a point to move it; the solver keeps every constraint while you drag.

Profiles

Closed loops become profiles automatically, including regions split by crossing lines and islands inside outlines. Extrude, revolve, sweep and loft pick profiles.

Solid features

Features live on the timeline and rebuild incrementally from the first one that changed. Each one can be edited (double-click), suppressed, renamed, reordered (if nothing it depends on moves after it), rolled back to, and deleted. Deleting asks first when other features depend on it.

PanelFeatures
Createextrude (one side, two sides, symmetric, taper, start offset, to a distance or through all; new body, join, cut, intersect), revolve, sweep, loft, box, cylinder, sphere, torus, coil, pipe, rib, web, emboss, hole (simple, counterbore, countersink, tapped; on faces or at sketch points), thread, rectangular/circular/path patterns, mirror
Modifypress pull, fillet, chamfer, shell, draft, scale, combine, split body, offset face, replace face, move/copy, align, remove
Constructoffset plane, plane at angle
Inspectmeasure, section analysis, interference, curvature comb and map, zebra, draft, minimum radius, isocurves, centre of mass

Fillets and chamfers handle straight edges between planar faces, whole smooth loops, runs of neighbouring edges with mitred or spherical corners, and closed chains of curved edges (a rolling ball). See the roadmap’s known limitations for the rest.

Feature references to faces and edges are stored by name, so an upstream edit that moves geometry keeps them on the right face (see Persistent naming). When a reference can’t be found, the feature warns instead of silently picking another face.

Press Delete on anything selected (bodies, faces, sketches, features, planes, components, canvases). It runs as one undoable step and lists the features that would go with it or fail.

Parameters and expressions

Every dimension and feature input is a model parameter (d1, d2, …). User parameters are ones you add yourself, with a unit, an expression and an optional comment. Change them in Change Parameters (parameters.change). The timeline rebuilds from the first feature that uses them.

Expressions are unit-aware: 2 * width + 5 mm, angle / 2, sqrt(area), 1 in + 3 mm, using + - * / ^, parentheses, sin cos tan asin acos atan atan2 sqrt abs min max floor ceil round mod trunc and unit conversion. A length where an angle is expected (or the other way round) is an error, not a silent conversion. Cycles are reported with their path (a → b → a), renaming a parameter updates every expression, dimension and feature input that uses it, and a parameter in use can’t be deleted (the error lists its users).

The design’s units (Document Settings in the browser: mm, cm, m, in or ft) set how a bare number typed into a feature is read. In an inch design 2 means 2 in. Changing the units never moves existing geometry.

Parameters export to and import from CSV or JSON (parameters.export, parameters.import). Configurations (config.*) keep a table of rows, each with its own parameter values and suppressed features, switched with config.activate.

Components and joints

A design is a tree of components. New Component (component.create) adds one, and Create Components from Bodies (component.from_bodies) turns bodies into components. Each component has its own origin, bodies, sketches and timeline entries. The active component (the radio button in the browser) receives new sketches and features; the others are drawn faded.

A component is placed by its occurrences. Paste creates another instance of the same component, and Paste New creates an independent copy. Move an occurrence with the move triad, then Capture Position to keep it or Revert to put it back. SolveCraft asks before the next command if a move wasn’t captured. Ground fixes an occurrence in place.

Joints (joint.create, J) connect two components by joint origins that snap to faces, edges, circles and vertices: rigid, revolute, slider, cylindrical, pin-slot, planar and ball, with offsets, flip and limits. As-built Joint keeps the current position, Rigid Group locks components together, Drive Joints moves a joint through its values, and Motion Link couples two joints. Contact sets stop motion where bodies would pass through each other, Motion Study keys joint values along a timeline, and Interference shows overlapping volumes in red.

STEP export writes the component tree as an assembly.

Sheet metal

The SHEET METAL tab builds parts from a sheet of constant thickness under a sheet metal rule (thickness, bend radius, K-factor), set in Sheet Metal Rules (sheet.manage_rules).

  • Flange (sheet.flange): a base flange from a sketch profile, a contour flange from an open sketch, or an edge flange from a part edge.
  • Hem (sheet.hem) and Fold (sheet.fold) along a sketch line.
  • Unfold / Refold (sheet.unfold, sheet.refold) for working on the flat part.
  • Convert (sheet.convert) turns a solid of constant thickness into sheet metal.
  • Create Flat Pattern (sheet.flat_pattern) shows the flat part with its bend lines and a bend table, and exports it as DXF.

Bends follow the rule’s K-factor, so flat lengths match what a press brake will make. The oracle checks these against parts built in Fusion.

Plastic parts

The PLASTIC tab has a small enclosure subset:

  • Boss (plastic.boss): screw bosses with a hole, fillets and optional ribs.
  • Lip / Groove (plastic.lip): the mating lip and groove along a shell’s opening.
  • Snap Fit (plastic.snap_fit): a cantilever snap hook and its catch.
  • Rest (plastic.rest): a pad for a part to rest on.
  • Plastic Rules (plastic.manage_rules, plastic.assign_rule): wall thickness, draft and clearance per material, used as the features’ defaults.

Rib and web (on the SOLID tab) complete the set. These features are checked against hand-computed geometry rather than a Fusion oracle.

Import and export

FormatReadWrite
.solvecraft (JSON design, versioned)yesyes
STEP (AP203, AP214, AP242)solids, assemblies, units, names, coloursAP242 with names, colours and the component tree
IGES 5.3manifold solids, or trimmed surfaces sewn into solidsmanifold solids with names and colours
3MF, STL, OBJmesh bodiesyes
DXF, SVGinto a sketchsketches and flat patterns as DXF
PNG, JPEGcanvases and decals

Opening a STEP or IGES file creates a design with the file’s bodies as a base feature, and inserting one adds them to the current design. Later features build on them. Mesh bodies render, measure, move and export, but solid features need B-rep bodies.

Saves are atomic: a crash at any moment leaves either the old file or the new one, and the replaced version is kept as <file>.bak. Unsaved changes are autosaved to a recovery folder, and the next launch offers to recover them. Older design files are upgraded on open, and files from a newer version are refused with a message.

Keyboard shortcuts

Command shortcuts can be changed in File › Keyboard Shortcuts; these are the defaults.

KeyCommandKeyCommand
LLineEExtrude
RTwo-Point RectangleQPress Pull
CCenter Diameter CircleFFillet
DSketch DimensionHHole
XConstructionMMove/Copy
OOffsetJJoint
TTrimIMeasure
PProjectAAppearance
Ctrl+ZUndoCtrl+YRedo
Ctrl+NNew DesignCtrl+OOpen
Ctrl+SSaveDeleteDelete the selection

Keys handled by the window itself:

KeyAction
Sthe S box: search every command at the cursor
Vshow or hide the selected bodies, sketches and planes
F2rename the selected browser or timeline item
F6fit the view to the model
Esccancel the running command, or clear the selection

Help › Keyboard Shortcuts in the app lists the current bindings, including any you changed.

SolveCraft and AI agents (MCP)

SolveCraft speaks the Model Context Protocol over stdio (JSON-RPC 2.0, one message per line; protocol revisions 2025-06-18, 2025-03-26 and 2024-11-05). An agent models exactly like a person does: every tool runs engine commands, the same ones the toolbar, palette, scripts and control channel use, and never opens a dialog.

Start it

# Headless: an in-process design session, no window (CPU renders for screenshots).
solvecraft-cli mcp

# Headless, starting from a design file or a command script.
solvecraft-cli mcp --in part.solvecraft

# Bridged to the running app, so you can watch the agent model:
solvecraft --control 7878 &
solvecraft-cli mcp --connect 127.0.0.1:7878

stdout carries only protocol messages; errors go to stderr. Bridged screenshots are written to a temporary file by the app and read back by the server, so both must run on the same machine.

Client configuration

Claude Code (project .mcp.json, or claude mcp add solvecraft -- solvecraft-cli mcp):

{
  "mcpServers": {
    "solvecraft": { "command": "solvecraft-cli", "args": ["mcp"] }
  }
}

Claude Desktop (claude_desktop_config.json), bridged to a running app:

{
  "mcpServers": {
    "solvecraft": { "command": "/path/to/solvecraft-cli", "args": ["mcp", "--connect", "127.0.0.1:7878"] }
  }
}

Tools

ToolArgumentsWhat it does
list_commandsfilter?The command catalog: id, label, tab/panel, shortcut, parameter docs, enabled now.
executecommand, params?Run any command with JSON parameters (list_commands shows them). Failures leave the design unchanged.
batchcommands: [{command, params}], rollback? (default true)Run steps in order, stop at the first error and undo the completed steps. The reply names the failing step (failed.index, failed.command, failed.error) and every result before it.
inspect_designmeasure? (default true), sketches? (default true)Parameters, timeline (type, suppressed, rolled back, error, warning, timing), bodies (volume, area, centre of mass, bbox, face/edge/vertex counts), sketches (curves, points, constraints, profiles, DOF, solve status), selection, undo depth.
measurebody?Volume (mm³), area (mm²), centre of mass, bbox and topology counts per body, with totals.
body_topologybodyEdges (index, midpoint, length, ends) and faces (index, area, centroid, normal). An edge midpoint is the [x,y,z] that picks that edge for fillet and chamfer.
set_parametername, value (number or expression), unit?, comment?Create or change a user parameter and recompute; reports features that now fail.
screenshotview? (iso, top, front, back, bottom, left, right, home, fit), path?, width?, height?, source? (window, model)A PNG image content block. Headless: CPU render fitted to the model. Bridged: the app window (default) or a model render with the app’s camera.
exportpath, format? (step, 3mf, stl, stla, obj), bodies?STEP, 3MF or mesh export.
undo, redo—Undo / redo one change.
new_designname?Start an empty design.
openpathOpen a .solvecraft design, a STEP file (.step/.stp) as a new design holding its bodies (an Import base feature), or a 3MF/STL file as mesh bodies.
savepath?Save the design (to its current file without path).

Resources: solvecraft://design (inspect JSON with measurements) and solvecraft://commands (the catalog).

Errors: an unknown tool or arguments that don’t match the tool’s schema are JSON-RPC -32602 errors; an engine failure (bad command parameters, a feature that fails) is a tool result with isError: true and the engine’s message, which the model can read and correct.

batch rollback undoes as many steps as the batch added to the undo history; batches that themselves call edit.undo, file.new or doc.open are not rolled back exactly.

Demo: an enclosure built over MCP

examples/demo/enclosure_mcp.py is a small MCP client. It drives a running app (solvecraft --control PORT) through solvecraft-cli mcp --connect and builds an electronics enclosure one execute call at a time (enclosure_steps.json): a rounded box, shelled open at the top, a USB port cut through a side wall, four screw bosses, and then a change of the width parameter that the timeline rebuilds. examples/demo/record.sh records the run with ffmpeg, with each step’s caption burnt in.

examples/demo/enclosure_ui_mcp.py (v2) builds the same part the way a person would: the modelling goes through the app’s UI over its control channel. The pointer glides to toolbar buttons and faces and clicks them, the real dialogs open, the sketches go on the model’s faces, and the camera follows. The parameters, the sketch dimensions bound to them and two extrudes go over MCP. With --story v3 (the default) one standoff is patterned 2 × 2 in the Rectangular Pattern dialog with spacings from the parameters, and width and wall are then changed in the Parameters dialog. record_ui.sh records it and draws the pointer and the captions.

A typical session

→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"me","version":"1"}}}
→ {"jsonrpc":"2.0","method":"notifications/initialized"}
→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"batch","arguments":{"commands":[
     {"command":"sketch.create","params":{"plane":"XY"}},
     {"command":"sketch.rectangle.two_point","params":{"p0":[0,0],"p1":[40,30]}},
     {"command":"sketch.finish"},
     {"command":"solid.extrude","params":{"distance":20}},
     {"command":"solid.fillet","params":{"edges":[[0,0,10]],"radius":3}},
     {"command":"sketch.create","params":{"plane":"XY"}},
     {"command":"sketch.circle.center","params":{"center":[20,15],"radius":5}},
     {"command":"solid.extrude","params":{"distance":20,"operation":"cut"}}]}}}
← {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"ok\": true, \"completed\": 8, \"results\": [...]}"}]}}
→ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"measure","arguments":{}}}
← … "total": {"volume_mm3": 22390.6, …}
→ {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"screenshot","arguments":{"view":"iso"}}}
← {"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"image","mimeType":"image/png","data":"iVBORw0…"},{"type":"text","text":"{\"source\":\"model\",…}"}]}}
→ {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"export","arguments":{"path":"/tmp/part.step"}}}

The tests in crates/mcp/src/tests.rs run sessions like this (box → fillet → hole, parameters, batch rollback, hostile arguments) and check the results only through MCP.

Contributing to SolveCraft

SolveCraft is an open-source parametric 3D CAD app written in pure Rust, in the style of Autodesk Fusion. You sketch on a plane, constrain the sketch, turn profiles into solids, and change a parameter to watch the timeline rebuild. It runs as a desktop app (Windows, Linux), in the browser (wasm + WebGPU/WebGL2), headless from a CLI, and as an MCP server that AI agents can model with.

Help is welcome, whether that’s a one-line fix or owning a whole area. This page covers how to build it, how the code is laid out, the few rules that aren’t negotiable, and where help is needed most.

Get it running (about 10 minutes on a fast machine)

You need stable Rust 1.90 or newer. For the web build you also need rustup target add wasm32-unknown-unknown and trunk.

git clone https://github.com/bherbruck/solvecraft && cd solvecraft
cargo run --release -p solvecraft -- --sample      # desktop app with a sample part
cargo test --workspace --release                   # unit, engine and UI-scenario tests
cargo xtask ci                                     # everything a PR must pass (see below)

Web: cd apps/solvecraft-web && trunk serve --release, then open the printed URL (?sample opens the sample part, ?webgl forces WebGL2). Saving and opening files in the browser needs HTTPS or localhost, because it uses the Origin Private File System.

Windows from Linux: cargo xwin build --release --target x86_64-pc-windows-msvc -p solvecraft.

How the code is laid out

The README has the layer table. The short version:

  • crates/geom: vectors, planes, meshes, measures.
  • crates/sketch: our own constraint solver, plus profile finding.
  • crates/kernel: the B-rep kernel boundary. It wraps truck, and it’s the only crate allowed to touch truck. Fillets, chamfers, shell/offset, Delete Face, Split Face, STEP/IGES read and write, and face provenance are our own code here. Patched copies of three truck crates live in vendor/ and are excluded from our fmt/clippy rules.
  • crates/doc: the document. It holds parameters and expressions, the feature timeline and its incremental rebuild, components and occurrences, joints, appearances, and persistent naming.
  • crates/io: design files and mesh formats (3MF, STL, OBJ).
  • crates/engine: the session and the command registry. Everything the user can do is a command, and the toolbar, dialogs, scripts, CLI, control channel and MCP all run the same commands.
  • crates/ui-egui: the desktop/web front end (egui + wgpu). It’s swappable; nothing below it depends on egui.
  • crates/mcp, apps/solvecraft, apps/solvecraft-cli, apps/solvecraft-web.

cargo xtask layers fails the build if a lower layer depends on a higher one.

Rules that aren’t negotiable

  1. Clean-room. We observe Fusion as a black box: its UI, how parts it builds measure, and what it does with a given input. We never copy Autodesk code, icons, artwork, help text, tooltips, templates or materials, and we never commit files Fusion produced. Command ids are our own (solid.fillet, not Fusion’s internal names).
  2. No GPL/LGPL/AGPL code, not even reading it. That rules out OpenCASCADE, FreeCAD (including planegcs), SolveSpace, CadQuery/OCP, LibreCAD and QCAD. SolveCraft is MIT OR Apache-2.0, and its solver and blends are written from the maths. Permissive dependencies are fine. Note which one you’re adding and why in the PR, and run cargo xtask licences so THIRD-PARTY-LICENSES.txt lists it (CI checks that it is current).
  3. Every asset is original or openly licensed and has a row in ATTRIBUTION.md. Icons are drawn in code (crates/ui-egui/src/icons.rs). cargo xtask assets enforces this.
  4. Never crash. People trust a CAD app with their work.
    • Non-test code never uses unwrap, expect, panic! or indexing on input-derived data. There’s no unsafe, and clippy denies it.
    • An unsupported case returns an error (“not supported yet: …”); it never panics.
    • Every call into truck goes through solvecraft_kernel::guard.
    • Every crash fix comes with a regression test.
  5. Everything is a command, with a test. A new feature means a CommandSpec in crates/engine/src/cmd/, engine tests that measure the result (volume, area, face/edge/vertex counts against a value you can derive), and for UI work a scenario (below).

AGENTS.md has the long form of these rules. It’s written for AI agents, but it’s the most complete description of the house style.

Tests

WhatWhereRun
Unit and engine testsnext to the code, or *_tests.rs filescargo test --workspace --release
UI scenarioscrates/ui-egui/tests/scenarios/*.jsonpart of cargo test
Fusion oraclecargo xtask oracleneeds the local oracle data (see below)

UI scenarios drive the real app headless: start a tool, click a plane or a sketch point, press keys, then assert on the result.

[
  {"start": "sketch.create"},
  {"click": {"plane": "XY"}},
  {"start": "sketch.rectangle.two_point"},
  {"click": {"sketch": [0, 0]}},
  {"click": {"sketch": [40, 30]}},
  {"expect": {"errors": 0}}
]

A new .json file in that folder is picked up automatically. To record a bug before it’s fixed, put {"pending": "why"} in the scenario, and remove it in the PR that fixes the bug.

Release mode matters. CI runs the tests with --release. truck has debug-only assertions that can fail on valid geometry, so if a test fails only in debug, say so in the PR.

The Fusion oracle is a set of 76 reference parts, each built in Fusion from a recipe. SolveCraft rebuilds the same recipe and compares volume, area and topology counts with Fusion’s measurements; docs/oracle.md has the current results. The recipes and measurements live outside the repo (plan/, gitignored), because they come from a Fusion install. If you work on geometry and want to check against it, ask in an issue.

Sending a change

  1. Work is tracked in GitHub issues, labelled by area (area:kernel, area:sketch, area:doc, area:ui, area:infra). Before starting, comment on the issue so it gets the in progress label and two people don’t build the same thing. For anything bigger than a bug fix that has no issue yet, open one first.
  2. Branch from main and keep the PR to one topic.
  3. Run cargo xtask ci before pushing. It runs fmt, clippy (-D warnings), the tests in release mode, the asset and layer checks, and the wasm build check. A PR has to pass it.
  4. Reference the issue (Fixes #12). In the PR, say what you tested and how. For geometry work, include the measured numbers (and the expected ones).

Much of SolveCraft so far was written by AI coding agents (Claude) working in parallel, each owning one area, with a human setting direction and reporting bugs. Their working notes are in AGENTS.md. Human contributors are just as welcome. Use whatever tools you like, as long as the change meets the rules above.

Where help is wanted most

See the help wanted and good first issue labels. In order of how much they’d help users:

  1. Fillet and blend robustness. Fillets are our own code (truck has no fillet operation). Constant, variable-radius and chord fillets work, and so do corners where fillets of different radii meet, including three rounds meeting at a vertex. Next are setback corners, fillets along curved edges between curved faces, and fillets that consume neighbouring faces.
  2. Boolean robustness. A random-design fuzzer currently gets about half of its designs through without a kernel error. Each failing seed is a reproducible bug.
  3. Y-up / Z-up. SolveCraft is Z-up only, so most STEP files (from SolidWorks, Onshape, or Fusion’s default) open lying on their backs. Fusion solves this with a “default modeling orientation” preference.
  4. Imported appearances. Colours from STEP files are lost when a feature changes the imported body. They should become design appearances on import.
  5. Drawings, CAM and mesh tools. These are out of scope for now (see ROADMAP.md), but anyone who wants to own one is welcome.

Licence

By contributing you agree that your contribution is licensed as MIT OR Apache-2.0, like the rest of SolveCraft. SolveCraft isn’t affiliated with Autodesk. Fusion is a trademark of Autodesk, Inc.

Architecture

SolveCraft is a Cargo workspace in layers. A crate only depends on crates in lower layers, and cargo xtask layers fails the build otherwise.

LayerCratesRole
L0geomvectors, planes, profiles, meshes and their measures
L1sketch, kernel, renderconstraint solver and profiles; the B-rep kernel boundary (truck); view math and CPU rasterizer
L2docparameters, expressions, the feature timeline and its incremental evaluation
L3iodesign files, STL/OBJ/STEP/3MF export, STEP and 3MF/STL import features
L4enginethe session and the command registry (everything is a command)
L5ui-egui, mcpthe swappable desktop front end; the MCP server (headless or bridged to the app)
appssolvecraft, solvecraft-clidesktop app, headless CLI

The web app, apps/solvecraft-web, is the desktop front end compiled to wasm with trunk. xtask holds the repository’s own tooling: cargo xtask ci, layers, assets, oracle, parity, step-corpus and book.

A few rules follow from the layering:

  • Only crates/kernel touches truck. Everything above it sees SolveCraft’s own types: bodies, faces, edges, meshes and measures. See The kernel boundary.
  • The document knows nothing about commands or the UI. crates/doc holds the parameters, the feature timeline and the component tree, and evaluates them into a model.
  • Everything the user can do is a command in crates/engine. See The command engine.
  • The front end is swappable. Nothing below ui-egui depends on egui. The CLI and the MCP server run the same engine without a window, and crates/render has a CPU rasterizer so headless screenshots work anywhere.

The command engine

Every user-visible action is a command: a CommandSpec registered in crates/engine/src/cmd/, with a dotted, lower-case id (solid.extrude, sketch.create, timeline.rollback). The toolbar, dialogs, marking menu, S box, scripts, the CLI, the control channel and MCP all run commands through Session::execute(id, params). Commands take JSON parameters and never open dialogs; a dialog is a front-end form that builds the parameters.

#![allow(unused)]
fn main() {
CommandSpec::new("solid.fillet", "Fillet", fillet)
    .at("SOLID", "MODIFY")      // toolbar tab and panel ("" = not on the toolbar)
    .icon("fillet")             // drawn in code, crates/ui-egui/src/icons.rs
    .key("F")                   // default shortcut
    .params("edges: [[x,y,z] | {body, index}]; radius: expr; …"),
}

run is fn(&mut Session, &Value) -> Result<Value>, and enabled is fn(&Session) -> Result<(), String>; the error string is the disabled-button tooltip. The params string is the one-line documentation shown by solvecraft-cli commands, MCP’s list_commands and the command reference.

A command that changes the design is undoable by default: the session snapshots the document before it runs, and a failing command leaves the document unchanged. Feature commands add a feature to the timeline and let the document evaluate it; they validate their inputs first (units, ranges, references) and return BadParams instead of building a broken feature.

Ids used before the 2026-10 rename still resolve through crates/engine/src/legacy_ids.rs, so old scripts, recipes, saved shortcuts and toolbar layouts keep working.

Adding a command

  1. Add a CommandSpec to the right file in crates/engine/src/cmd/ and implement run.
  2. Write engine tests that measure the result: volume, area, and face/edge/vertex counts against values you can derive by hand.
  3. The hostile-input test (hostile_params_never_panic in crates/engine/src/tests.rs) runs your command automatically with every documented parameter set to junk. It must return errors, never panic.
  4. For UI work, add a scenario (see Test harnesses).

The kernel boundary and truck

crates/kernel is SolveCraft’s B-rep kernel. It wraps truck (Apache-2.0) for topology, NURBS geometry, booleans, tessellation and STEP parsing, and it is the only crate allowed to use truck. Callers get SolveCraft types (Body, face and edge indices, meshes, measures) and a KernelError instead of truck types and panics.

guard

truck can panic on geometry it doesn’t handle. Every call into it goes through solvecraft_kernel::guard, which catches the panic and turns it into KernelError::Internal, leaving the model unchanged:

#![allow(unused)]
fn main() {
pub fn guard<T>(what: &str, f: impl FnOnce() -> Result<T>) -> Result<T>
}

A failing feature shows an error on the timeline; it never takes the app down.

What is our own

truck has no fillets, chamfers, shells or face edits, so these are SolveCraft code in the kernel: constant, chord and variable-radius fillets with corner blends, chamfers, shell and offset, Delete Face, Split Face, Replace Face, sewing, STEP and IGES reading and writing beyond truck’s parser, and face provenance (which input face each output face came from, for persistent naming).

Vendored truck crates

Patched copies of truck-shapeops, truck-stepio and truck-meshalgo live in vendor/, wired in with [patch.crates-io]. The patches fix robustness bugs found by our tests and the STEP corpus. vendor/ is excluded from our fmt and clippy rules, and each patch should go upstream when it can.

Some truck assertions only run in debug builds and can fail on valid geometry, so tests and CI run with --release.

Persistent naming

A fillet stores the edges it rounds, and a sketch stores the face it sits on. If those were face and edge indices, an upstream edit that adds a face would move the fillet to the wrong edge. SolveCraft stores names instead (crates/doc/src/naming.rs):

  • A face of an evaluated body is named after how it was made. A face lying on a face of the feature’s input keeps that face’s name; a face new to the feature is named by its role: F<feature>:side:<sketch curve>, F<feature>:start and :end, F<feature>:blend:<k> and so on.
  • Pieces of a split face get #1, #2, … in order along the longest axis.
  • An edge is named by the two faces it separates.
  • Pattern and mirror copies are named after their sources plus the instance.

Names are computed lazily, the first time something asks, from the body, the feature that made it and the model just before that feature. Face provenance from the kernel (which input face an output face came from) does most of the work.

When a feature evaluates, each stored reference is resolved by name. If no face has the name any more, the reference falls back to the geometry it recorded (a point on the face, a normal), and the feature shows a warning so the user can check it. A reference that matches nothing is an error, never a guess.

Test harnesses

cargo xtask ci runs everything a pull request must pass: fmt, clippy with -D warnings, the tests in release mode, xtask layers, xtask assets and the wasm build check.

HarnessWhereWhat it catches
Unit and engine testsnext to the code, *_tests.rsmeasured results: volume, area, topology counts against derived values
Hostile inputhostile_params_never_panic, crates/engine/src/tests.rsevery command × every documented parameter × junk values: errors, never panics
Sketch fuzzcrates/engine/src/cmd/sketch_fuzz_tests.rsrandom sketches through the solver and profile finder
UI scenarioscrates/ui-egui/tests/scenarios/*.jsonthe real app, headless: clicks, keys, drags, assertions
Layout testscrates/ui-egui/tests/ui_layout.rs, window_widths.rspanels and dialogs at every window width
Design-file corpusapps/solvecraft-cli/tests/corpusevery saved file version still opens and evaluates the same
Kill mid-saveapps/solvecraft-cli/tests/kill_mid_save.rsa save killed at any moment leaves a readable file
Fusion oraclecargo xtask oracle → docs/oracle.mdparts rebuilt from recipes, compared with Fusion’s measurements
STEP corpuscargo xtask step-corpus → docs/step-import.mdSTEP files from Fusion opened and measured

UI scenarios

A scenario is a JSON list of steps run against the real app with a headless egui context:

[
  {"start": "sketch.create"},
  {"click": {"plane": "XY"}},
  {"start": "sketch.rectangle.two_point"},
  {"click": {"sketch": [0, 0]}},
  {"click": {"sketch": [40, 30]}},
  {"expect": {"errors": 0}}
]

A new file in crates/ui-egui/tests/scenarios/ is picked up automatically. To record a bug before it’s fixed, add a {"pending": "why"} step; its test is then ignored with that reason, and the fix removes the step.

Release mode

CI runs the tests with --release, because some truck assertions only exist in debug builds and fail on valid geometry. If a test fails only in debug, say so in the PR.

Control protocol

solvecraft --control PORT (or SOLVECRAFT_CONTROL_PORT=PORT) listens on 127.0.0.1:PORT for JSON lines. Each request is one line {"id": …, "method": "…", "params": {…}}; each reply is one line {"id": …, "ok": true, "result": …} or {"id": …, "ok": false, "error": "…"}. Requests are handled on the UI thread between frames; pointer and keyboard input is injected as real egui events, one pointer event per frame.

Engine

MethodParamsResult
engine.executecommand: command id, params: JSON objectthe command’s result
engine.scriptcommands: [{command, params}, …]results, stops at the first error
engine.commands—every command: id, label, tab, panel, icon, shortcut, params doc, enabled
document.inspectmeasure?: boolparameters, timeline (errors, timings), bodies, sketches, selection

Commands never open dialogs when run this way. solvecraft-cli commands prints the same registry.

UI

MethodParamsNotes
ui.inspect—UI state, viewport rect, camera, active tool/dialog, hover, renderer, timings, live preview (active, busy, error, ms, bodies it stands in for)
ui.setany UiState field (tab, perspective, showGrid, hiddenBodies, dark, …)dark: false switches to the light theme
ui.viewview: front, back, top, bottom, left, right, iso, home, fit; animate (bool, default false: snap)
ui.startcommandlike a toolbar click: starts the sketch tool or dialog for the command
ui.clickx, y (screen points), button?: left/right/middle, shift?, ctrl?, double?real pointer input
ui.movex, y
ui.dragx0, y0, x1, y1, button?, shift?, ctrl?, steps?, hold?, hold_end?press, move, release; a left drag on the model is a box selection (left to right: window, right to left: crossing); hold keeps the button down that many frames first (a right press-and-hold opens the marking menu, releasing over a direction picks it), hold_end that many frames at the end (a sketch drag shows its snap)
ui.menux?, y?, target?, close?with a point: open a context menu there as a right-click would (the viewport’s marking menu, or a browser menu with target: {"type": "body", "name"} | {"type": "sketch", "id"} | {"type": "component", "id"}); without: the open menu (radial and items: id, label, shortcut, enabled)
ui.menuPickitem: id or labelruns an item of the open menu, as clicking it does
ui.renametext?, commit? (default true)finishes the rename box a Rename item opened
ui.selectionthe selection, the open dialog’s inputs, the hovered item, and the active sketch’s drawn dimensions (dimensions: id and text centre, dimension_selected, dimension_editing)
ui.worldToScreenpoints: [[x, y, z]…] world pointstheir screen points
ui.triadscreen points of the 3D sketch move triad’s handles (x, y, z, plane)
ui.sketchToScreenpoints: [[x, y]…] in active-sketch coordinatestheir screen points (null when behind the camera)
ui.dialogInputindexwhat clicking an input’s Select chip does: picks go to that input
ui.atworld: [x,y,z] | sketch: [x,y] | plane: "XY" | axis: "Z" | dimension: "d1" (its value text) | handle: "arrow" (a manipulator handle, see scenario::publish_handle)the screen point (a scenario step’s target; see crates/ui-egui/src/scenario.rs)
ui.handlesprefix?the handles drawn last frame, [{name, x, y}]: toolbar buttons and drop-down items toolbar:<command id>, panel labels panel:<tab>:<panel>, browser rows row:<label> and fold arrows fold:<label>, manipulator handles
ui.editFeaturefeature (id or name)what double-clicking a timeline item does: rolls back to it and opens its dialog filled in (sketches open in sketch mode)
ui.scrollx, y, delta?wheel zoom at the cursor
ui.keykey (egui key name, Enter, Escape…), cmd?, shift?
ui.texttexttyped text
ui.screenshotpath?PNG of the window (needs a presented frame)
ui.renderpath, width?, height?CPU render of the model with the current camera (no window needed)
ui.confirmaccept?: boolanswers the Delete confirmation (shown when a delete takes other features with it or makes some fail); without accept, what it lists
ui.documentsaction?: new, switch, close, quit; index?; force? (close without asking); answer?: save, dont_save, cancel (answers “save changes?”)the open designs (tabs): names, unsaved flags, the active one, a pending “save changes?” prompt, whether a quit is asking
ui.homeopen?: bool; sample?: indexthe start page: show or hide it, open a built-in sample (once built); returns open and the recent designs
ui.shortcutcommand, key?, replace?a command’s key, or rebinds it ("" clears; a key another command uses is refused unless replace)
ui.helpitem?: about, shortcuts (read only), report (copies diagnostics)the version, commit, build date and diagnostic text
ui.prefsany of default_units, nav (fusion, solidworks, inventor), zoom_reverse, orbit_cursor, msaa, length_decimals, angle_decimalssets preferences (kept between runs), returns them all
ui.captureaction?: capture, revert, cancelanswers the Capture Position question (asked when a command starts or the design is saved while components were moved without capturing); without action, what waits
ui.windowaction: minimize, maximize, restore, toggle, closewhat the title bar’s caption buttons do (on Windows and Linux the application bar is the title bar); close asks about unsaved designs first
ui.agent_cursorshow (bool), speed: instant, normal, slow, follow_camera (bool); all optionalthe settings. While the cursor is shown, engine.execute first glides to the command’s toolbar button and to each pick, then runs and replies; later requests wait their turn. "animate": false on a call skips it
ui.resizewidth, height
app.quitforce? (quit without asking about unsaved designs)quitting (the window closes), asking (a “save changes?” prompt is open)

MCP

solvecraft-cli mcp --connect 127.0.0.1:PORT bridges an MCP client (an AI agent) to this channel; see mcp.md.

Example

{"id":1,"method":"engine.execute","params":{"command":"sketch.create","params":{"plane":"XY"}}}
{"id":2,"method":"engine.execute","params":{"command":"sketch.rectangle.two_point","params":{"p0":[0,0],"p1":[40,30]}}}
{"id":3,"method":"engine.execute","params":{"command":"solid.extrude","params":{"distance":20}}}
{"id":4,"method":"engine.execute","params":{"command":"solid.fillet","params":{"edges":[[0,0,10]],"radius":3}}}
{"id":5,"method":"ui.screenshot","params":{"path":"/tmp/part.png"}}

Fusion oracle

Generated by cargo xtask oracle. Each case is a part built in Fusion (recipe and measurements kept locally in plan/fusion/oracle/, not committed). SolveCraft replays the recipe through its command engine and compares body count, volume and area (relative tolerance 1e-3) and face/edge/vertex counts (exact). SolveCraft counts each closed-surface patch once and ignores seam edges and vertices; when body count, volume and area pass but the raw counts differ, Fusion’s counts are normalised the same way from its face, edge and vertex lists (faces cut from one cylinder, cone, sphere or torus and separated only by seams count once; an edge on that surface and no other is a seam and is dropped; a vertex left with two edges joins them) and compared exactly; the note gives both sides’ raw counts.

76 / 76 cases pass (0 need features not implemented yet).

CaseResultNotes
01-boxpass
02-cylinderpass
03-box-through-holepass
04-revolve-vasepass
05-taper-extrudepass
06-symmetric-extrudepass
07-fillet-all-edgespass
08-chamferpass
09-shellpass
10-rect-pattern-holespass
11-circular-pattern-flangepass
12-mirror-featurepass
13-combine-joinpass
14-combine-cutpass
15-combine-intersectpass
16-sweeppass
17-loftpass
18-hole-featurespass
19-l-bracket-constrainedpass
20-slot-constrainedpass
21-draftpass
22-split-bodypass
23-bracket-multifeaturepass
24-user-parameterspass
25-sketch-trianglepass
26-sketch-beltpass
27-sketch-trapezoidpass
28-blind-pocket-filletspass
29-two-sided-and-partial-revolvepass
30-fillet-after-shellpass
31-fillet-l-all-edgespass
32-boss-plate-filletspass
33-fillet-tangent-chainpass
34-chamfer-then-filletpass
35-fillet-curved-curvedpasstopology compared seam-normalised: Fusion’s 6/6/5 faces/edges/vertices become 6/5/5 with closed-surface patches counted once and seam edges and vertices dropped (raw SolveCraft/Fusion: faces 9/6, edges 17/6, vertices 10/5)
36-corner-blend-two-radiipass
37-pyramid-apex-blendpass
38-coincident-cylinderspass
39-tee-pipepass
40-box-sphere-toruspass
41-tube-radial-holespass
42-overlapping-patternspass
43-cut-splits-bodypass
44-shell-l-bracketpass
45-shell-boss-platepass
46-shell-two-open-facespass
47-draft-curved-pocketpass
48-pulleypass
49-helical-sweeppassFusion sweeps along a spline fitted through helix points; SolveCraft builds the true helix (a coil), so the size is compared within 0.5% and the topology not at all
50-sweep-tight-bendpassarea not compared: Fusion reports this tight sweep’s horn torus face at three times its area (π²·R·r quarter-turn value; 740 mm² against 246.7 by Pappus); volume and topology are compared
51-loft-rect-circle-offsetpass
52-circular-pattern-filletpass
53-path-patternpass
54-mirror-body-filletspass
55-tapped-hole-cosmeticpass
56-holes-on-curved-facepasstopology compared seam-normalised: Fusion’s 7/10/10 faces/edges/vertices become 7/8/8 with closed-surface patches counted once and seam edges and vertices dropped (raw SolveCraft/Fusion: faces 16/7, edges 32/10, vertices 19/10)
57-hole-pattern-sketch-pointspass
58-phone-standpass
59-enclosure-basepass
60-pipe-flangepass
61-shaft-keywaypasstopology compared seam-normalised: Fusion’s 14/22/18 faces/edges/vertices become 14/20/16 with closed-surface patches counted once and seam edges and vertices dropped (raw SolveCraft/Fusion: faces 24/14, edges 54/22, vertices 34/18)
62-heat-sinkpass
63-hinge-knucklepassknown Fusion difference: Gap replayed with the extent Fusion actually used (its through-all is sized as 1.1 × the farthest vertex, so curved tops stop it short; SolveCraft’s through-all goes truly through)
64-sketch-tangent-arc-chainpass
65-sketch-gear-toothpass
66-sketch-linkagepass
67-sm-base-flangepass
68-sm-edge-flangepass
69-sm-traypass
70-sm-flange-45pass
71-sm-custom-rulepass
72-sm-contour-flangepass
73-sm-hempass
74-sm-unfold-refoldpass
75-sm-bracket-holespass
76-sm-thickness-variantpass

No oracle

FeatureStatusTested by
Boss (draft, root fillet, hole, ribs)no oracle (licence)post, hole and rib volumes (boss_with_hole_fillet_and_ribs)
Lip / Grooveno oracle (licence)band offsets and groove clearance on a shelled box (lip_and_groove_on_a_shelled_box, groove_takes_the_rule_clearance)
Snap Fitno oracle (licence)arm and catch volume, hook direction (snap_fit_arm_and_catch)
Restno oracle (licence)rectangle, round, hollow and drafted pads (rest_pads)
Plastic Rules (manage, assign)no oracle (licence)library values, Thickness-based values, rule draft as the boss default (plastic_rules_library_edit_and_assign)

Fusion builds these only with its Design Extension, which the oracle account lacks, so there is no Fusion part to compare with. They follow Autodesk’s public descriptions of the features and are checked against hand-computed geometry; the plastic rule library values were read from Fusion’s API.

Fusion command parity

Generated by cargo xtask parity from xtask/data/fusion-catalog.tsv (the toolbar commands of Fusion’s Design workspace, ids and names only) and the live command registry (solvecraft-cli commands). A command counts as live when SolveCraft registers the same command id. This measures breadth, not depth; see ROADMAP.md and docs/oracle.md for depth.

In scope (SOLID, SKETCH, ASSEMBLY, SHEET METAL, PLASTIC enclosure subset): 328 / 541 commands live (61%). SOLID + SKETCH: 185 / 291 (64%). Registered commands: 300.

Deferred until further notice (not in the in-scope figure): MESH, FORM, SURFACE beyond what solids need, PCB, render, animation, simulation, manufacture, drawings, MANAGE, UTILITIES and the rest of PLASTIC. All Design tabs together: 616 / 1175 (52%).

TabPanelLiveTotal%
SOLIDCREATE233566%
SOLIDMODIFY193259%
SOLIDASSEMBLE122157%
SOLIDCONFIGURE2367%
SOLIDCONSTRUCT182090%
SOLIDINSPECT122548%
SOLIDINSERT61155%
SOLIDSELECT0150%
SOLIDPOSITION22100%
SKETCHCREATE4141100%
SKETCHMODIFY1212100%
SKETCHCONSTRAINTS1515100%
SKETCHCONFIGURE2367%
SKETCHINSPECT122548%
SKETCHINSERT71354%
SKETCHASSEMBLE010%
SKETCHSELECT0150%
SKETCHFINISH SKETCH22100%
SHEET METALCREATE151979%
SHEET METALMODIFY122157%
SHEET METALASSEMBLE112055%
SHEET METALCONFIGURE2367%
SHEET METALCONSTRUCT182090%
SHEET METALINSPECT122548%
SHEET METALINSERT61155%
SHEET METALSELECT0150%
SHEET METALREFOLD010%
SHEET METALPOSITION22100%
ASSEMBLYINSERT4850%
ASSEMBLYCREATE5862%
ASSEMBLYRELATIONSHIPS41136%
ASSEMBLYMOTION33100%
ASSEMBLYMODIFY81173%
ASSEMBLYCONTACT22100%
ASSEMBLYCONFIGURE2367%
ASSEMBLYCONSTRUCT182090%
ASSEMBLYINSPECT122548%
ASSEMBLYSELECT0150%
ASSEMBLYPOSITION22100%
PLASTICenclosure subset55100%

Deferred tabs

TabPanelLiveTotal%
SURFACECREATE92536%
SURFACEMODIFY172665%
SURFACEASSEMBLE112055%
SURFACECONFIGURE2367%
SURFACECONSTRUCT182090%
SURFACEINSPECT122548%
SURFACEINSERT61155%
SURFACESELECT0150%
SURFACEPOSITION22100%
MESHCREATE2540%
MESHPREPARE040%
MESHMODIFY63020%
MESHASSEMBLE122157%
MESHCONFIGURE2367%
MESHCONSTRUCT182090%
MESHINSPECT122548%
MESHINSERT51050%
MESHSELECT0100%
MESHEXPORT010%
MESHPOSITION22100%
FORMCREATE21414%
FORMMODIFY53116%
FORMSYMMETRY060%
FORMUTILITIES050%
FORMCONSTRUCT182090%
FORMINSPECT122548%
FORMINSERT71354%
FORMASSEMBLE010%
FORMSELECT0150%
FORMFINISH FORM010%
PLASTICSETUP22100%
PLASTICCREATE273577%
PLASTICMODIFY193259%
PLASTICASSEMBLE112055%
PLASTICCONFIGURE2367%
PLASTICCONSTRUCT182090%
PLASTICINSPECT122646%
PLASTICINSERT61155%
PLASTICSELECT0150%
PLASTICPOSITION22100%
MANAGEASSIGN010%
MANAGERELEASE020%
MANAGEBOM020%
MANAGEPROCESS MANAGEMENT030%
MANAGETASK MANAGEMENT030%
UTILITIESMODELING TOOLS010%
UTILITIESMAKE010%
UTILITIESNEST010%
UTILITIESADD-INS020%
UTILITIESUTILITY1333%
UTILITIESINSPECT112446%
UTILITIESCREATE PMI040%
UTILITIESSELECT0150%
UTILITIESPOSITION22100%

Live (SOLID, SKETCH)

  • SOLID › CREATE › Create Sketch
  • SOLID › CREATE › Extrude
  • SOLID › CREATE › Revolve
  • SOLID › CREATE › Sweep
  • SOLID › CREATE › Loft
  • SOLID › CREATE › Rib
  • SOLID › CREATE › Web
  • SOLID › CREATE › Emboss
  • SOLID › CREATE › Hole
  • SOLID › CREATE › Thread
  • SOLID › CREATE › Box
  • SOLID › CREATE › Cylinder
  • SOLID › CREATE › Sphere
  • SOLID › CREATE › Torus
  • SOLID › CREATE › Coil
  • SOLID › CREATE › Pipe
  • SOLID › CREATE › Bounding Solid
  • SOLID › CREATE › Rectangular Pattern
  • SOLID › CREATE › Circular Pattern
  • SOLID › CREATE › Pattern on Path
  • SOLID › CREATE › Mirror
  • SOLID › CREATE › Thicken
  • SOLID › CREATE › Joint Origin
  • SOLID › MODIFY › Press Pull
  • SOLID › MODIFY › Fillet
  • SOLID › MODIFY › Chamfer
  • SOLID › MODIFY › Shell
  • SOLID › MODIFY › Draft
  • SOLID › MODIFY › Scale
  • SOLID › MODIFY › Combine
  • SOLID › MODIFY › Offset Face
  • SOLID › MODIFY › Replace Face
  • SOLID › MODIFY › Split Face
  • SOLID › MODIFY › Split Body
  • SOLID › MODIFY › Move/Copy
  • SOLID › MODIFY › Align
  • SOLID › MODIFY › Delete
  • SOLID › MODIFY › Remove
  • SOLID › MODIFY › Physical Material
  • SOLID › MODIFY › Appearance
  • SOLID › MODIFY › Change Parameters
  • SOLID › MODIFY › Compute All
  • SOLID › ASSEMBLE › Insert Component
  • SOLID › ASSEMBLE › New Component
  • SOLID › ASSEMBLE › Joint
  • SOLID › ASSEMBLE › As-Built Joint
  • SOLID › ASSEMBLE › Rigid Group
  • SOLID › ASSEMBLE › Drive Joints
  • SOLID › ASSEMBLE › Motion Link
  • SOLID › ASSEMBLE › Motion Study
  • SOLID › ASSEMBLE › Enable Contact Sets
  • SOLID › ASSEMBLE › Enable All Contact
  • SOLID › ASSEMBLE › Disable Contact
  • SOLID › ASSEMBLE › New Contact Set
  • SOLID › CONFIGURE › Configure
  • SOLID › CONFIGURE › Display Configuration Table
  • SOLID › CONSTRUCT › Offset Plane
  • SOLID › CONSTRUCT › Plane at Angle
  • SOLID › CONSTRUCT › Tangent Plane
  • SOLID › CONSTRUCT › Midplane
  • SOLID › CONSTRUCT › Plane Through Two Edges
  • SOLID › CONSTRUCT › Plane Through Three Points
  • SOLID › CONSTRUCT › Plane Along Path
  • SOLID › CONSTRUCT › Axis Through Cylinder/Cone/Torus
  • SOLID › CONSTRUCT › Axis Perpendicular To Face
  • SOLID › CONSTRUCT › Axis Through Two Planes
  • SOLID › CONSTRUCT › Axis Through Two Points
  • SOLID › CONSTRUCT › Axis Through Edge
  • SOLID › CONSTRUCT › Point At Vertex
  • SOLID › CONSTRUCT › Point Through Two Edges
  • SOLID › CONSTRUCT › Point Through Three Planes
  • SOLID › CONSTRUCT › Point At Center Of Circle/Sphere/Torus
  • SOLID › CONSTRUCT › Point At Edge And Plane
  • SOLID › CONSTRUCT › Point Along Path
  • SOLID › INSPECT › Measure
  • SOLID › INSPECT › Interference
  • SOLID › INSPECT › Section Analysis
  • SOLID › INSPECT › Curvature Comb Analysis
  • SOLID › INSPECT › Curvature Map Analysis
  • SOLID › INSPECT › Draft Analysis
  • SOLID › INSPECT › Environment Map Analysis
  • SOLID › INSPECT › Isocurve Analysis
  • SOLID › INSPECT › Zebra Analysis
  • SOLID › INSPECT › Accessibility Analysis
  • SOLID › INSPECT › Center of Mass
  • SOLID › INSPECT › Minimum Radius Analysis
  • SOLID › INSERT › Decal
  • SOLID › INSERT › Canvas
  • SOLID › INSERT › Insert SVG
  • SOLID › INSERT › Insert DXF
  • SOLID › INSERT › Insert Mesh
  • SOLID › INSERT › Insert Fastener
  • SOLID › POSITION › Capture Position
  • SOLID › POSITION › Revert Position
  • SKETCH › CREATE › Line
  • SKETCH › CREATE › Midpoint Line
  • SKETCH › CREATE › SketchConstrainer
  • SKETCH › CREATE › 2-Point Rectangle
  • SKETCH › CREATE › 3-Point Rectangle
  • SKETCH › CREATE › Center Rectangle
  • SKETCH › CREATE › Center Diameter Circle
  • SKETCH › CREATE › 2-Point Circle
  • SKETCH › CREATE › 3-Point Circle
  • SKETCH › CREATE › 2-Tangent Circle
  • SKETCH › CREATE › 3-Tangent Circle
  • SKETCH › CREATE › 3-Point Arc
  • SKETCH › CREATE › Center Point Arc
  • SKETCH › CREATE › Tangent Arc
  • SKETCH › CREATE › Circumscribed Polygon
  • SKETCH › CREATE › Inscribed Polygon
  • SKETCH › CREATE › Edge Polygon
  • SKETCH › CREATE › Ellipse
  • SKETCH › CREATE › Center to Center Slot
  • SKETCH › CREATE › Overall Slot
  • SKETCH › CREATE › Center Point Slot
  • SKETCH › CREATE › Three Point Arc Slot
  • SKETCH › CREATE › Center Point Arc Slot
  • SKETCH › CREATE › Fit Point Spline
  • SKETCH › CREATE › Control Point Spline
  • SKETCH › CREATE › Control Point Spline
  • SKETCH › CREATE › Conic Curve
  • SKETCH › CREATE › Point
  • SKETCH › CREATE › Text
  • SKETCH › CREATE › Fit Curves to Mesh Section
  • SKETCH › CREATE › Mirror
  • SKETCH › CREATE › Circular Pattern
  • SKETCH › CREATE › Rectangular Pattern
  • SKETCH › CREATE › Project
  • SKETCH › CREATE › Intersect
  • SKETCH › CREATE › Spun Profile
  • SKETCH › CREATE › Include 3D Geometry
  • SKETCH › CREATE › Project To Surface
  • SKETCH › CREATE › Intersection Curve
  • SKETCH › CREATE › Isoparametric Curve
  • SKETCH › CREATE › Sketch Dimension
  • SKETCH › MODIFY › Fillet
  • SKETCH › MODIFY › Equal Distance Chamfer
  • SKETCH › MODIFY › Distance and Angle Chamfer
  • SKETCH › MODIFY › Two Distance Chamfer
  • SKETCH › MODIFY › Blend Curve
  • SKETCH › MODIFY › Offset
  • SKETCH › MODIFY › Trim
  • SKETCH › MODIFY › Extend
  • SKETCH › MODIFY › Break
  • SKETCH › MODIFY › Sketch Scale
  • SKETCH › MODIFY › Move/Copy
  • SKETCH › MODIFY › Change Parameters
  • SKETCH › CONSTRAINTS › AutoConstrain
  • SKETCH › CONSTRAINTS › AutoConstrain from datum
  • SKETCH › CONSTRAINTS › Horizontal/Vertical
  • SKETCH › CONSTRAINTS › Coincident
  • SKETCH › CONSTRAINTS › Tangent
  • SKETCH › CONSTRAINTS › Equal
  • SKETCH › CONSTRAINTS › Parallel
  • SKETCH › CONSTRAINTS › Perpendicular
  • SKETCH › CONSTRAINTS › Fix/UnFix
  • SKETCH › CONSTRAINTS › MidPoint
  • SKETCH › CONSTRAINTS › Concentric
  • SKETCH › CONSTRAINTS › Collinear
  • SKETCH › CONSTRAINTS › Symmetry
  • SKETCH › CONSTRAINTS › Curvature
  • SKETCH › CONSTRAINTS › Polygon
  • SKETCH › CONFIGURE › Configure
  • SKETCH › CONFIGURE › Display Configuration Table
  • SKETCH › INSPECT › Measure
  • SKETCH › INSPECT › Interference
  • SKETCH › INSPECT › Section Analysis
  • SKETCH › INSPECT › Curvature Comb Analysis
  • SKETCH › INSPECT › Curvature Map Analysis
  • SKETCH › INSPECT › Draft Analysis
  • SKETCH › INSPECT › Environment Map Analysis
  • SKETCH › INSPECT › Isocurve Analysis
  • SKETCH › INSPECT › Zebra Analysis
  • SKETCH › INSPECT › Accessibility Analysis
  • SKETCH › INSPECT › Center of Mass
  • SKETCH › INSPECT › Minimum Radius Analysis
  • SKETCH › INSERT › Decal
  • SKETCH › INSERT › Canvas
  • SKETCH › INSERT › Insert SVG
  • SKETCH › INSERT › Insert DXF
  • SKETCH › INSERT › Insert Mesh
  • SKETCH › INSERT › Insert Component
  • SKETCH › INSERT › Insert Fastener
  • SKETCH › FINISH SKETCH › Finish Sketch
  • SKETCH › FINISH SKETCH › Finish with AutoConstrain

Not yet (SOLID, SKETCH)

  • SOLID › CREATE › Create Form
  • SOLID › CREATE › Derive
  • SOLID › CREATE › Automated Modeling
  • SOLID › CREATE › Geometric Pattern
  • SOLID › CREATE › Boundary Fill
  • SOLID › CREATE › Find Features
  • SOLID › CREATE › Fluid Volume
  • SOLID › CREATE › Create Base Feature
  • SOLID › CREATE › Create Linked PCB
  • SOLID › CREATE › Derive PCB
  • SOLID › CREATE › Create Unlinked PCB
  • SOLID › CREATE › Generate Timeline
  • SOLID › MODIFY › Edit Face
  • SOLID › MODIFY › Silhouette Split
  • SOLID › MODIFY › Arrange
  • SOLID › MODIFY › Remove Features
  • SOLID › MODIFY › Remove Faces
  • SOLID › MODIFY › Replace with Primitives
  • SOLID › MODIFY › Volumetric Lattice
  • SOLID › MODIFY › Volumetric Texture
  • SOLID › MODIFY › VolFieldModifyModelCommand
  • SOLID › MODIFY › Manage Materials
  • SOLID › MODIFY › Compute Unresolved
  • SOLID › MODIFY › Convert
  • SOLID › MODIFY › Bill of Materials
  • SOLID › ASSEMBLE › Duplicate With Joints
  • SOLID › ASSEMBLE › Constrain Components
  • SOLID › ASSEMBLE › Align Constraint
  • SOLID › ASSEMBLE › Angle Constraint
  • SOLID › ASSEMBLE › Center Constraint
  • SOLID › ASSEMBLE › Tangent Constraint
  • SOLID › ASSEMBLE › Joint Constraint
  • SOLID › ASSEMBLE › As-Built Constraint
  • SOLID › CONFIGURE › Create Configuration Rules
  • SOLID › CONSTRUCT › User Coordinate System (UCS)
  • SOLID › CONSTRUCT › Perpendicular Plane
  • SOLID › INSPECT › Validate
  • SOLID › INSPECT › Component Connection Analysis
  • SOLID › INSPECT › DOF Analysis
  • SOLID › INSPECT › Edit Component Connection Analysis
  • SOLID › INSPECT › Fastener Stack Analysis
  • SOLID › INSPECT › Find Similar Components
  • SOLID › INSPECT › Navigator
  • SOLID › INSPECT › Display Component Colors
  • SOLID › INSPECT › Display Mesh Face Groups
  • SOLID › INSPECT › Assembly Statistics
  • SOLID › INSPECT › Change Summary
  • SOLID › INSPECT › History
  • SOLID › INSPECT › Sustainability Insights
  • SOLID › INSERT › Insert T-Spline
  • SOLID › INSERT › Insert Derive
  • SOLID › INSERT › Insert McMaster-Carr Component
  • SOLID › INSERT › Insert a manufacturer part
  • SOLID › INSERT › Insert TraceParts Supplier Components
  • SOLID › ASSEMBLE › Add To Assembly
  • SOLID › SELECT › Select
  • SOLID › SELECT › Window Selection
  • SOLID › SELECT › Freeform Selection
  • SOLID › SELECT › Paint Selection
  • SOLID › SELECT › Select By Name
  • SOLID › SELECT › Select By Boundary
  • SOLID › SELECT › Select By Size
  • SOLID › SELECT › Invert Selection
  • SOLID › SELECT › Seed And Boundary
  • SOLID › SELECT › Component Drag
  • SOLID › SELECT › Select Body Priority
  • SOLID › SELECT › Select Face Priority
  • SOLID › SELECT › Select Edge Priority
  • SOLID › SELECT › Select Component Priority
  • SOLID › SELECT › Selection Filters
  • SKETCH › CONFIGURE › Create Configuration Rules
  • SKETCH › INSPECT › Validate
  • SKETCH › INSPECT › Component Connection Analysis
  • SKETCH › INSPECT › DOF Analysis
  • SKETCH › INSPECT › Edit Component Connection Analysis
  • SKETCH › INSPECT › Fastener Stack Analysis
  • SKETCH › INSPECT › Find Similar Components
  • SKETCH › INSPECT › Navigator
  • SKETCH › INSPECT › Display Component Colors
  • SKETCH › INSPECT › Display Mesh Face Groups
  • SKETCH › INSPECT › Assembly Statistics
  • SKETCH › INSPECT › Change Summary
  • SKETCH › INSPECT › History
  • SKETCH › INSPECT › Sustainability Insights
  • SKETCH › INSERT › Insert T-Spline
  • SKETCH › INSERT › Duplicate With Joints
  • SKETCH › INSERT › Insert Derive
  • SKETCH › INSERT › Insert McMaster-Carr Component
  • SKETCH › INSERT › Insert a manufacturer part
  • SKETCH › INSERT › Insert TraceParts Supplier Components
  • SKETCH › ASSEMBLE › Add To Assembly
  • SKETCH › SELECT › Select
  • SKETCH › SELECT › Window Selection
  • SKETCH › SELECT › Freeform Selection
  • SKETCH › SELECT › Paint Selection
  • SKETCH › SELECT › Select By Name
  • SKETCH › SELECT › Select By Boundary
  • SKETCH › SELECT › Select By Size
  • SKETCH › SELECT › Invert Selection
  • SKETCH › SELECT › Seed And Boundary
  • SKETCH › SELECT › Component Drag
  • SKETCH › SELECT › Select Body Priority
  • SKETCH › SELECT › Select Face Priority
  • SKETCH › SELECT › Select Edge Priority
  • SKETCH › SELECT › Select Component Priority
  • SKETCH › SELECT › Selection Filters

STEP import

SolveCraft reads STEP (ISO 10303-21; AP203, AP214 and AP242 geometry) with its own reader in crates/kernel/src/step_in (written from the public standards; no third-party STEP code). A STEP file becomes an Import base feature on the timeline: it holds the file’s B-rep (the STEP text is kept in the design, so the design reopens without the file) and later features — fillets, holes, cuts sketched on its faces, shells — build on its bodies.

  • Open: File → Open (doc.open) accepts .step/.stp in any case and starts a new design; solvecraft part.step; solvecraft-cli eval part.step, solvecraft-cli run script.json --in part.step; the MCP open tool.
  • Insert into the current design: File → Insert STEP… or SOLID → INSERT (file.insert_step {path}).
  • Drag and drop a STEP file on the window: inserted when the design has features, opened otherwise.

Read: manifold solid breps (with voids), faceted breps, shell-based surface models; planes, cylinders, cones, spheres, tori, B-spline and rational B-spline surfaces (with knots, Bézier, uniform, quasi-uniform), surfaces of revolution and linear extrusion, trimmed surfaces; lines, circles, ellipses, B-spline curves, polylines, trimmed/surface/seam curves; length and angle units (SI prefixes, conversion-based units such as inches); product names (body names come from the solid’s name, else the product’s); surface colours; assemblies (next-assembly-usage occurrences placed by item-defined transformations, and mapped items) — the product tree is kept in the feature for the components milestone. Files are untrusted: sizes, entity counts and nesting are capped and every kernel call is guarded. What cannot be read becomes a warning on the feature (a face left out gives an open body); not read yet: offset surfaces, pcurve-only edges.

IGES

IGES 5.3 files (.igs/.iges) open, insert and drop the same way: crates/kernel/src/iges_in.rs (written from the published specification) restates the file as STEP and the STEP reader reads it, so the feature holds STEP text. Read: manifold solid B-reps (186) with their shells, faces, loops, edge and vertex lists; trimmed (144) and bounded (143) surfaces, and bare B-spline patches, sewn into a solid by their shared edges (an open body when they do not close); lines, circular arcs, ellipses, composite curves, rational B-spline curves; planes (108, 190), cylinders, cones, spheres, tori, rational B-spline surfaces, surfaces of revolution and tabulated cylinders; transformation matrices; units; colours (314 and the predefined numbers); body names (name properties, else labels). Not read yet: ruled and offset surfaces, curves on surfaces given only in parameter space. Export (file.export to .igs) writes each body as a manifold solid B-rep on the same surfaces a STEP export uses, with its name and colour.

Fusion corpus

Generated by cargo xtask step-corpus. Each case is a part exported from Fusion as STEP (AP214; kept locally in plan/fusion/oracle/, never committed). SolveCraft opens part.step exactly as File → Open does (an Import base feature) and compares with Fusion’s own measurements: body count, volume and area (relative tolerance 1e-3) and the number of faces read from the file.

76 / 76 files pass.

CaseResultBodiesVolume mm³ (got / Fusion)Area mm² (got / Fusion)Faces (got / Fusion)msNotes
01-boxpassBox24000.000 / 24000.0005200.000 / 5200.0006 / 63
02-cylinderpassCylinder35339.429 / 35342.9176125.845 / 6126.1063 / 387
03-box-through-holepassBox45738.283 / 45738.0539327.809 / 9327.7887 / 762
04-revolve-vasepassVase112488.847 / 112499.47913156.093 / 13156.6155 / 5511
05-taper-extrudepassFrustum61814.826 / 61814.8269676.915 / 9676.9156 / 62
06-symmetric-extrudepassBlock30000.000 / 30000.0006200.000 / 6200.0006 / 61
07-fillet-all-edgespassBox23340.621 / 23340.8494726.220 / 4726.26526 / 26532
08-chamferpassBox23730.667 / 23730.6675029.352 / 5029.35210 / 103
09-shellpassBox13632.000 / 13632.00013992.000 / 13992.00011 / 112
10-rect-pattern-holespassPlate29803.713 / 29803.54014652.481 / 14652.38918 / 18252
11-circular-pattern-flangepassFlange66758.144 / 66758.84419320.617 / 19320.79510 / 10168
12-mirror-featurepassBase21392.575 / 21392.9206530.944 / 6530.97310 / 1080
13-combine-joinpassA45570.295 / 45571.6808634.196 / 8634.3369 / 943
14-combine-cutpassA42933.034 / 42931.4179706.783 / 9706.8587 / 7402
15-combine-intersectpassA54452.172 / 54454.2737382.532 / 7382.7437 / 71022
16-sweeppassTube2584.428 / 2584.7241779.638 / 1779.6985 / 5612
17-loftpassLoft35121.595 / 35125.4986495.373 / 6495.0006 / 6191
18-hole-featurespassPlate33890.908 / 33890.7108568.695 / 8568.69613 / 13184
19-l-bracket-constrainedpassLBracket14720.000 / 14720.0005472.000 / 5472.0008 / 82
20-slot-constrainedpassSlot2965.428 / 2965.4871774.644 / 1774.6906 / 615
21-draftpassBox54574.737 / 54574.7378781.998 / 8781.9986 / 61
22-split-bodypassCyl, Cyl (1)75390.813 / 75398.22412954.440 / 12955.1756 / 6109
23-bracket-multifeaturepassBracket59331.477 / 59331.12918290.767 / 18290.73719 / 19117
24-user-parameterspassBlock34822.022 / 34821.9038114.175 / 8114.1597 / 758
25-sketch-trianglepassTriangle4032.155 / 4032.1552274.099 / 2274.0995 / 51
26-sketch-beltpassBelt11326.042 / 11326.1705637.347 / 5637.4047 / 742
27-sketch-trapezoidpassTrapezoid5342.286 / 5342.2573066.961 / 3066.9468 / 853
28-blind-pocket-filletspassBlock84299.345 / 84299.24916279.560 / 16279.57223 / 23320
29-two-sided-and-partial-revolvepassBlock25731.356 / 25731.3076312.589 / 6312.60212 / 1226
30-fillet-after-shellpassBox21745.915 / 21745.87614968.284 / 14968.33227 / 27464
31-fillet-l-all-edgespassL14321.483 / 14321.6265024.004 / 5024.04538 / 38747
32-boss-plate-filletspassPart33590.273 / 33590.3509929.854 / 9929.86410 / 101214
33-fillet-tangent-chainpassSlot8237.037 / 8237.1222793.541 / 2793.56914 / 14555
34-chamfer-then-filletpassBox22892.383 / 22892.4214753.677 / 4753.68818 / 1854
35-fillet-curved-curvedpassTee63169.685 / 63171.44310269.168 / 10269.3486 / 6904
36-corner-blend-two-radiipassBox57799.504 / 57800.2408585.437 / 8585.52418 / 18519
37-pyramid-apex-blendpassPyramid15465.376 / 15465.5753914.529 / 3914.58118 / 18312
38-coincident-cylinderspassA15706.406 / 15707.9633769.760 / 3769.9113 / 367
39-tee-pipepassTee24095.763 / 24096.23716709.772 / 16710.0487 / 7182
40-box-sphere-toruspassA36287.558 / 36287.43310696.143 / 10696.16510 / 102393
41-tube-radial-holespassTube20243.046 / 20243.98114873.689 / 14873.77322 / 221409
42-overlapping-patternspassPlate38488.411 / 38487.06421687.215 / 21686.60343 / 43533solid ‘Plate’: imported as an open body (the shell is not closed)
43-cut-splits-bodypassBar, Bar (1)28000.000 / 28000.0007200.000 / 7200.00012 / 125
44-shell-l-bracketpassL8384.000 / 8384.0008784.000 / 8784.00015 / 153
45-shell-boss-platepassPart13676.354 / 13676.60214156.531 / 14156.60215 / 1569
46-shell-two-open-facespassBox16044.000 / 16044.00011398.000 / 11398.00010 / 103
47-draft-curved-pocketpassBlock111387.998 / 111387.71217700.336 / 17700.36111 / 1127
48-pulleypassPulley168508.160 / 168510.84123718.215 / 23718.64412 / 12321
49-helical-sweeppassSpring4733.931 / 4738.0994765.903 / 4767.6523 / 310552solid ‘Spring’: face wrapping around a closed surface kept whole: split line does not reach the rings
50-sweep-tight-bendpassBend3758.025 / 3758.443– / –5 / 5405
51-loft-rect-circle-offsetpassLoft61129.957 / 61133.1169412.473 / 9411.6556 / 6251
52-circular-pattern-filletpassHub47206.182 / 47206.85014141.988 / 14142.17121 / 21820
53-path-patternpassPlate35010.499 / 35010.39816463.947 / 16463.89413 / 13130
54-mirror-body-filletspassBracket22780.113 / 22780.0896972.831 / 6972.83212 / 1298
55-tapped-hole-cosmeticpassPlate17542.360 / 17542.3144761.590 / 4761.5968 / 884
56-holes-on-curved-facepassCyl77941.171 / 77942.38810592.951 / 10593.1397 / 7378
57-hole-pattern-sketch-pointspassPlate36767.029 / 36766.71211818.165 / 11818.15324 / 24197
58-phone-standpassStand74559.442 / 74559.17729148.741 / 29148.78519 / 1924
59-enclosure-basepassEnclosure34309.038 / 34309.48633795.405 / 33795.61039 / 39286
60-pipe-flangepassFlange323661.027 / 323660.97956984.270 / 56984.56416 / 16993
61-shaft-keywaypassShaft47521.504 / 47525.9469676.781 / 9677.09614 / 14182
62-heat-sinkpassHeatSink43125.000 / 43125.00045725.000 / 45725.00066 / 668
63-hinge-knucklepassHinge4783.029 / 4783.1174110.255 / 4110.28714 / 14118
64-sketch-tangent-arc-chainpassWave4800.000 / 4800.0003843.701 / 3843.7129 / 910
65-sketch-gear-toothpassTooth3987.831 / 3988.0841945.416 / 1945.4859 / 98
66-sketch-linkagepassLink8045.410 / 8045.3864308.172 / 4308.17510 / 10129
67-sm-base-flangepassBody115000.000 / 15000.00012800.000 / 12800.0006 / 62
68-sm-edge-flangepassBody118972.476 / 18972.62216057.518 / 16057.55014 / 1426
69-sm-traypassBody129665.828 / 29666.23424979.993 / 24980.08646 / 4666
70-sm-flange-45passBody118450.704 / 18450.77715629.621 / 15629.63714 / 1414
71-sm-custom-rulepassBody110053.579 / 10053.71713896.249 / 13896.29922 / 2238
72-sm-contour-flangepassBody112097.478 / 12097.62210411.968 / 10412.00222 / 2236
73-sm-hempassBody121334.478 / 21334.72317994.397 / 17994.47322 / 2260
74-sm-unfold-refoldpassBody115800.086 / 15800.30413610.275 / 13610.32632 / 3284
75-sm-bracket-holespassBody113915.866 / 13916.01212035.934 / 12035.96225 / 25100
76-sm-thickness-variantpassBody122520.368 / 22520.57516064.089 / 16064.12814 / 1431

Command reference

Generated by cargo xtask book from solvecraft-cli commands (300 commands). Every command runs the same way from the toolbar, a script, solvecraft-cli exec <id> '<json>', the control channel and MCP. Parameters marked ? are optional; expr is a number or an expression with units.

SOLID › CREATE

  • sketch.create Create Sketch
    plane: XY|XZ|YZ | {origin, x_dir, y_dir} | {face: [x,y,z]} (planar face at point), offset?: expr, name?
  • solid.extrude Extrude (E)
    distance: expr (or through_all: true); taper?: angle expr; sketch?: id|name (default: active or last sketch); profiles?: all | [index] | [[curve ids]] | [{point:[x,y]}]; face?: [x,y,z] (extrude a planar body face instead of a sketch profile); direction?: positive|negative|symmetric; distance2?; start_offset?; to?: [x,y,z] (To Object: a face, vertex or point; to_offset?: expr past it); from?: [x,y,z] (From Object: start at that face or point); operation?: new|join|cut|intersect|auto (cut into a body, join out of one, else new); targets?: [body]; name?; body_name?
  • solid.revolve Revolve
    axis: sketch line id | x | y (sketch axes) | X|Y|Z (world); angle?: expr (default 360 deg); angle2?: expr (Two Sides: the other side's angle); direction?: symmetric (angle split either side); to?: [x,y,z] (To Object: turn until that face, vertex or point); sketch?, profiles? | face: [x,y,z] (a planar body face as the profile), operation?, targets?, name?, body_name?
  • solid.sweep Sweep
    sketch: profile sketch; profiles?; path_sketch: sketch; path: [curve ids in order]; operation?
  • solid.loft Loft
    sections: [{sketch, profiles?} | {sketch, point: id} (an apex)] (2 or more, in order); operation?
  • solid.box Box
    length, width, height: expr; corner?: [x,y,z] | center?: [x,y,z]; operation?
  • solid.cylinder Cylinder
    radius | diameter, height: expr; base?: [x,y,z]; axis?: [x,y,z]; operation?
  • solid.sphere Sphere
    radius | diameter: expr; center?: [x,y,z]; operation?
  • solid.torus Torus
    major, minor: expr (radii); center?; operation?
  • solid.pattern.rectangular Rectangular Pattern
    features: [names] | bodies: [names] | components: [names]; dir1: [x,y,z]; count1; spacing1; dir2?, count2?, spacing2?
  • solid.pattern.circular Circular Pattern
    features: [names] | bodies: [names] | components: [names]; axis: X|Y|Z | {origin, dir}; count; angle? (default 360 deg)
  • solid.pattern.path Pattern on Path
    features: [names] | bodies: [names]; path_sketch: sketch; path: [curve ids in order]; count; spacing (between instances) | distance (first to last); orientation?: identical|path; flip?: bool
  • solid.mirror Mirror
    features: [names] | bodies: [names] (combine?: join with the original); plane: XY|XZ|YZ | {origin, x_dir, y_dir}
  • solid.hole Hole (H)
    position: [x,y,z] on a face | sketch + points: [sketch point ids] (one hole each, perpendicular to the sketch) | sketch alone (every sketch point: standalone points and circle centres); direction?: [x,y,z] (default: into the face); diameter | thread (a tapped hole: the tap drill, major − pitch) | clearance: "M6" with fit?: close|normal|loose (ISO 273); depth? (default through all) | to?: [x,y,z] (To Object: drill to that face or point); type?: simple|drilled|counterbore|countersink; tip_angle?; cb_diameter?, cb_depth?; cs_diameter?, cs_angle?; thread?: "M6" (cosmetic)
  • solid.pipe Pipe
    path_sketch: sketch; path: [curve ids in order]; diameter: expr; wall?: expr (hollow); operation?, targets?, name?, body_name?
  • solid.bounding_solid Bounding Solid
    bodies?: [names] (default: all); margin?: expr; name?, body_name?
  • solid.thread Thread
    face: [x,y,z] on a cylindrical face; designation?: ISO metric ("M8", "M8x1"; default: the size that fits); length?: expr (default: the whole face). Cosmetic: the model is unchanged, model.threads lists it
  • SurfaceSculpt Boundary Fill
    tools: [bodies]; cells: [[x,y,z] a point inside each cell to fill (a cell: inside some tools, outside the rest)]; operation?: new|join|cut|intersect; remove_tools?: bool
  • solid.coil Coil
    diameter; two of revolutions (or turns), height, pitch; section_size; section?: circular|square; section_position?: inside|center|outside; base?: [x,y,z]; axis?: X|Y|Z|[x,y,z] (default Z); start_angle?; clockwise?: bool; operation?, targets?, name?, body_name?
  • solid.rib Rib
    sketch, curves: [open curve ids, in order]; thickness (across the sketch plane); depth? (default: to the next face); flip?: bool (fill to the other side)
  • solid.web Web
    sketch, curves: [open curve ids] (one wall each); thickness; depth? (default: to the next face); flip?: bool
  • solid.emboss Emboss
    sketch (on a planar face), profiles?; depth; mode?: emboss|deboss (default emboss); targets?
  • joint.origin Joint Origin
    name?, occurrence?: id|name (default: from the pick), face: [x,y,z] (centre of a planar face) | circle: [x,y,z] (centre of a round end) | point: [x,y,z], z?: [x,y,z]

SOLID › MODIFY

  • solid.fillet Fillet (F)
    edges: [[x,y,z] point on edge | {body, index}]; radius: expr; body?; type?: constant|chord (radius is the width across)|variable (radius at the end nearest start, radius2 at the other); radius2?; start?: [x,y,z]
  • solid.chamfer Chamfer
    edges: [[x,y,z] | {body, index}]; distance: expr; distance2?: expr (along the second face) | angle?: expr (from the first face); flip?: bool (which face is first: by default the one facing up most); body?
  • solid.combine Combine
    target: body; tools: [body]; operation?: join|cut|intersect; keep_tools?: bool; new_component?: bool (the result goes into a new component; component_name?)
  • solid.scale Scale
    bodies: [names]; factor: expr | factors: [x, y, z] exprs; origin?: [x,y,z]
  • face.delete Delete Face
    faces: [[x,y,z] points on the faces]; body?. The neighbouring planes close the gap, as when a rounded edge, hole or boss is taken off
  • solid.offset_face Offset Face
    faces: [[x,y,z] points on planar faces]; distance: expr (positive: outward); body?
  • solid.shell Shell
    faces: [[x,y,z] points on the faces to remove]; thickness: expr; direction?: inside|outside|both; tangent_chain?: bool (also open faces joined smoothly to the picked ones); body?
  • solid.draft Draft
    faces: [[x,y,z]]; angle: expr; neutral: XY|XZ|YZ|plane name|{origin, normal}; pull?: [x,y,z] (default: neutral plane normal); body?
  • solid.split_face Split Face
    faces: [[x,y,z] points on the faces to split]; body?; and the splitting tool: plane: XY|XZ|YZ|plane name|{origin, normal} — or tool: {body, point} (a face of a body, its surface extended) — or sketch: id or name (its curves, along the sketch's normal)
  • solid.split_body Split Body
    body: name; plane: XY|XZ|YZ|plane name|{origin, normal} — or tool: {body, point} (a face of a body, its surface extended)
  • solid.press_pull Press Pull (Q)
    face: [x,y,z] and distance: expr (positive adds material, negative cuts), or edges: [[x,y,z]…] and distance (a fillet)
  • solid.move Move/Copy (M)
    bodies: [names]; translate?: [x,y,z] (exprs or numbers); axis?: [x,y,z]; angle?: expr
  • solid.replace_face Replace Face
    faces: [[x,y,z] points on planar faces]; target: plane (XY|XZ|YZ|construction plane|{origin, normal}) | target_face: [x,y,z] on a parallel planar face; body?
  • solid.align Align
    bodies: [names]; from: [x,y,z] | {snap: vertex|edge_mid|face_center|circle_center, at: [x,y,z]} | from_face: [x,y,z]; to: (the same) | to_face: [x,y,z] (faces also turn to meet); flip?: bool
  • solid.remove Remove
    bodies: [names] (removed from here on in the timeline)
  • timeline.compute_all Compute All
  • timeline.delete Delete (Delete)
    features: [id or name] (a sketch takes its dependent features with it)
  • parameters.change Change Parameters
    name, expression (number or text), unit?: mm|cm|m|in|ft|deg|rad|"" (new parameters default to their value's unit), comment?, favorite?: bool, new_name?: rename (references follow); or delete: name
  • material.assign Physical Material
    bodies: [names]; material: name (Steel, Aluminum, ABS Plastic, …; `material.list`)
  • appearance.assign Appearance (A)
    bodies?: [names]; faces?: [[x,y,z] on faces] (body?: which body, default the nearest); components?: [names or ids, or occurrence names]; appearance?: library name (appearance.library) | color: "#rrggbb" or [r, g, b] (0–255), opacity? (0–1, default 1); neither (or color: null): clear, so the face follows its body, the body its component, the component its material

SOLID › CONSTRUCT

  • construct.plane.offset Offset Plane
    base: XY|XZ|YZ|plane name; offset: expr; name?
  • construct.plane.at_angle Plane at Angle
    base: XY|XZ|YZ|plane name; axis: X|Y|Z|{origin, dir}; angle: expr; name?
  • construct.axis.through_cylinder Axis Through Cylinder/Cone/Torus
    face: ref (a cylinder, cone or torus face, or a circular edge); name?
  • construct.axis.normal_to_face Axis Perpendicular To Face
    face: ref; point: ref (the axis goes through it, square to the face); name?
  • construct.axis.two_planes Axis Through Two Planes
    a, b: plane or planar face refs; name?
  • construct.axis.two_points Axis Through Two Points
    a, b: point refs (vertices, sketch points, points); name?
  • construct.axis.edge Axis Through Edge
    edge: ref (a straight edge or sketch line); name?
  • construct.point.vertex Point At Vertex
    point: ref (vertex, sketch point, point); name?
  • construct.point.two_edges Point Through Two Edges
    a, b: edge or line refs (where they meet, or come closest); name?
  • construct.point.three_planes Point Through Three Planes
    a, b, c: plane or planar face refs; name?
  • construct.point.center Point At Center Of Circle/Sphere/Torus
    of: ref (circular edge, sketch circle or arc, sphere or torus face); name?
  • construct.point.edge_plane Point At Edge And Plane
    edge: edge or line ref; plane: plane or planar face ref; name?
  • construct.point.along_path Point Along Path
    path: edge or sketch curve ref; t: expr, 0…1 along it (default 0.5); name?
  • construct.plane.midplane Midplane
    a, b: plane or planar face refs (halfway between; bisecting when they meet at an angle); name?
  • construct.plane.two_edges Plane Through Two Edges
    a, b: straight edge or line refs in one plane; name?
  • construct.plane.three_points Plane Through Three Points
    a, b, c: point refs; name?
  • construct.plane.tangent Tangent Plane
    face: ref (cylinder, cone, sphere or torus); at?: [x,y,z] where it touches (default: the face pick); name?
  • construct.plane.along_path Plane Along Path
    path: edge or sketch curve ref; t: expr, 0…1 along it (default 0.5); name? The plane is square to the path there

SOLID › ASSEMBLE

  • component.create New Component
    name?, parent?: component id or name (default: the active one); activate?: bool (default true)
  • joint.create Joint (J)
    type: rigid|revolute|slider|cylindrical|pin_slot|planar|ball; a, b: joint origins ({occurrence, face|circle|point: [x,y,z] in world} | origin name); values?: [angles in deg or expressions, distances]; limits?: [[min, max] | null…]; flip?: bool; offset?; angle?; name?
  • joint.as_built As-Built Joint
    type, a: occurrence, b: occurrence (stay where they are); at?: joint origin (default: b's origin) — the joint's frame
  • joint.rigid_group Rigid Group
    occurrences: [ids or names] (move together, as they are)
  • joint.drive Drive Joints
    joint: id|name; value | values: [angles in deg or expressions, distances] (clamped to the limits)
  • joint.motion_link Motion Link
    a, b: joints; ratio (b = ratio · a + offset); offset?; ia?, ib?: value indices (default 0)
  • contact.enable_sets Enable Contact Sets
    enabled?: bool (default true) — driving joints and motion studies stop where bodies in one contact set would pass through each other
  • contact.enable_all Enable All Contact
    every occurrence takes part in contact (one set of all)
  • contact.disable_all Disable Contact
    contact off
  • contact.create New Contact Set
    occurrences: [ids or names] (two or more); name?
  • motion.study Motion Study
    name?; study? (edit an existing one); steps? (timeline length, default 100); keys: [{joint, index?, points: [[step, value], …]}] (values: degrees for angles, mm for distances, or expressions)
  • explode.create Exploded View
    name?; scale? (automatic: each occurrence pushed away from the assembly's centre by scale × its offset, default 1); moves?: [{occurrence, translate: [x,y,z]}] (instead of automatic)

SOLID › POSITION

  • component.capture_position Capture Position
    keeps pending occurrence moves
  • component.revert_position Revert Position
    drops pending occurrence moves

SOLID › INSPECT

  • inspect.interference Interference
    bodies?: [names] (default: all, in world placement) → pairs that overlap with the overlap volume, area, bounding box and centre
  • inspect.measure Measure (I)
    bodies?: [names] (default all); or items: [1-2 selections] → length/area/volume, minimum distance (from, to) and angle
  • inspect.section Section Analysis
    plane: XY|XZ|YZ | {origin, normal}; offset?: expr along the normal; flip?: bool; or clear: true

SOLID › CONFIGURE

  • config.configure Configure
    params?: [parameter names]; features?: [feature names] (columns to add; with no rows yet, a "Default" row takes the current values)
  • config.table Display Configuration Table
    → columns (param:<name> or suppress:<feature>), rows, the active row and whether the design still matches it

SOLID › INSERT

  • parts.fastener Insert Fastener
    the Insert Part dialog's command (same as parts.insert): family, size, length?, at?|point?+direction?, name?
  • file.insert_step Insert STEP
    path: .step/.stp file (its bodies join the design as an Import base feature); name?
  • file.insert_mesh Insert Mesh
    path: .3mf or .stl file (its meshes join the design as mesh bodies); name?

SKETCH › FINISH SKETCH

  • sketch.finish Finish Sketch
  • sketch.finish_auto_constrain Finish with AutoConstrain
    like AutoConstrain, then Finish Sketch

SKETCH › CREATE

  • sketch.line Line (L)
    points: [[x,y] | "l1.end" | {at, ref}…], closed?: bool, construction?: bool, ids?: [..], infer?: bool (add horizontal/vertical)
  • sketch.rectangle.two_point 2-Point Rectangle (R)
    p0, p1: opposite corners [x,y]; construction?
  • sketch.rectangle.three_point 3-Point Rectangle
    p0, p1: first edge; p2: point on the opposite edge
  • sketch.rectangle.center Center Rectangle
    center, corner
  • sketch.circle.center Center Diameter Circle (C)
    center: [x,y] or point ref, radius | diameter: number
  • sketch.circle.two_point 2-Point Circle
    p0, p1: diameter ends
  • sketch.circle.three_point 3-Point Circle
    p0, p1, p2
  • sketch.arc.three_point 3-Point Arc
    start, end, through
  • sketch.arc.center_point Center Point Arc
    center, start, end (counter-clockwise from start) | center, start, sweep: signed degrees
  • sketch.polygon.inscribed Inscribed Polygon
    center, radius (to vertices), sides, angle?: deg
  • sketch.polygon.circumscribed Circumscribed Polygon
    center, radius (to edge midpoints), sides, angle?: deg
  • sketch.polygon.edge Edge Polygon
    p0, p1: one edge, sides
  • sketch.slot.center_to_center Center to Center Slot
    p0, p1: arc centres, width
  • sketch.slot.overall Overall Slot
    p0, p1: overall ends, width
  • sketch.point Point
    point: [x,y], id?
  • sketch.dimension Sketch Dimension (D)
    entities: [refs], type?: auto|distance|horizontal|vertical|length|radius|diameter|angle, value?: number or expression (default: current)
  • sketch.construction Normal/Construction (X)
    curves: [ids], value?: bool (default: construction unless all of them already are)
  • sketch.line.midpoint Midpoint Line
    mid: [x,y] or point ref, end: [x,y]; construction?
  • sketch.arc.tangent Tangent Arc
    start: end point of a line or arc ("l1.end"), end: [x,y]; curve?: the curve to be tangent to (default: the one ending at start)
  • sketch.circle.two_tangent 2-Tangent Circle
    curves: [two lines/circles/arcs], radius | diameter: expr, near: [x,y] (which of the solutions)
  • sketch.circle.three_tangent 3-Tangent Circle
    curves: [three lines/circles/arcs], near: [x,y] (which of the solutions)
  • sketch.slot.center_point Center Point Slot
    center: slot centre, end: one arc centre, width
  • sketch.slot.arc_three_point Three Point Arc Slot
    start, through, end: points on the centre arc, width
  • sketch.slot.arc_center Center Point Arc Slot
    center, start: centre arc start, end: direction of the centre arc end (counter-clockwise), width
  • sketch.ellipse Ellipse
    center, major: end of the major axis [x,y], minor: a point the ellipse passes through on the minor side [x,y] | minor_radius
  • sketch.spline.fit_point Fit Point Spline
    points: [[x,y] or point refs…] (2…500): the spline passes through them
  • sketch.spline.control_point Control Point Spline
    points: control points (2…500), cubic
  • sketch.spline.control_point_5 Control Point Spline (Degree 5)
    points: control points (2…500), degree 5
  • sketch.conic Conic Curve
    start, end, apex: [x,y] or point refs; rho?: 0…1 (default 0.5: parabola)
  • sketch.text Text
    text: string (\n breaks lines), at: baseline start [x,y], height?: capital height mm (default 5), angle?: deg
  • sketch.constrainer Sketch Constrainer
    entities: [one or two sketch entity ids]: applies the constraint the geometry nearly has (horizontal/vertical, parallel, perpendicular, collinear, coincident, concentric, equal, tangent)
  • sketch.mirror Mirror
    entities: [curve/point ids], line: mirror line id (symmetry constraints are added)
  • sketch.pattern.circular Circular Pattern
    entities: [ids], center: [x,y] or point ref, count: 2…500, angle?: total deg (default 360)
  • sketch.pattern.rectangular Rectangular Pattern
    entities: [ids], dir?: [x,y] (default x), count, spacing; count2?, spacing2? (perpendicular direction)
  • sketch.project Project (P)
    refs: [{edge: [x,y,z], body?} | {face: [x,y,z], body?} (its edge loops) | {body: name} (silhouette) | {vertex: [x,y,z], body?} | {sketch, curve} | {sketch, point} | "origin" | {axis: X|Y|Z} | {plane: XY|XZ|YZ|name}]
  • sketch.intersect Intersect
    refs: [{body}, {face: [x,y,z], body?}, {edge: [x,y,z], body?}, {sketch, curve}]: section curves (or points) with the sketch plane, linked
  • sketch.include_3d Include 3D Geometry
    refs: like Project (edges, vertices, sketch curves and points); curves out of the sketch plane are flattened onto it
  • sketch.project_to_surface Project To Surface
    curves: [{sketch, curve}…] (curves of other sketches), face: [x,y,z] point on the target face, body?: projected along the active sketch's normal onto the face, as linked 3D curves
  • sketch.intersection_curve Intersection Curve
    a, b: {body} | {face: [x,y,z], body?}: where they meet, as linked 3D curves
  • sketch.spun_profile Spun Profile
    body: name, axis: a line of the active sketch (curve id) | X|Y|Z: the body's outline revolved about the axis, in the sketch plane
  • sketch.isoparametric_curve Isoparametric Curve
    face: [x,y,z] point on the face (the curve passes through it), body?, direction?: u|v|[x,y,z] (default u): linked 3D curve
  • sketch.fit_curves_to_section Fit Curves to Mesh Section
    body: name (a mesh or solid body): its section with the sketch plane, fitted with lines and arcs, as normal (unlinked) geometry

SKETCH › CONSTRAINTS

  • sketch.constraint.horizontal_vertical Horizontal/Vertical
    line: id | points: [a, b]; mode?: horizontal|vertical
  • sketch.constraint.coincident Coincident
    a: point, b: point or curve
  • sketch.constraint.tangent Tangent
    a, b: curves
  • sketch.constraint.equal Equal
    a, b: lines or circles/arcs
  • sketch.constraint.parallel Parallel
    a, b: lines
  • sketch.constraint.perpendicular Perpendicular
    a, b: lines
  • sketch.constraint.fix Fix/UnFix
    entity: point or curve | entities: [points and curves]; fixed?: bool (default: fix them all unless all are fixed, then unfix)
  • sketch.constraint.midpoint MidPoint
    point, line
  • sketch.constraint.concentric Concentric
    a, b: circles/arcs
  • sketch.constraint.collinear Collinear
    a, b: lines
  • sketch.constraint.symmetry Symmetry
    a, b: points, line: symmetry line
  • sketch.constraint.curvature Curvature
    a, b: two curves sharing an end point (curvature continuous, G2)
  • sketch.constraint.polygon Polygon
    lines: [line ids] of a closed chain: equal sides with every corner on one circle (a regular polygon)
  • sketch.auto_constrain AutoConstrain
    tolerance?: mm (default 0.01), angle_tolerance?: deg (default 1), dimensions?: bool (default true): infer constraints, then dimension until fully constrained
  • sketch.auto_constrain_from_datum AutoConstrain from datum
    datum: point ref (default origin); like AutoConstrain with positions measured from the datum

SKETCH › MODIFY

  • sketch.blend_curve Blend Curve
    a, b: end points of two curves ("l1.end", "a2.start"): a smooth (tangent) spline joining them
  • sketch.fillet Fillet
    point: corner shared by two curves ("l1.end") | a, b: two lines or arcs meeting at a corner; radius: expr
  • sketch.chamfer.equal_distance Equal Distance Chamfer
    point | a, b: a corner of two lines; distance
  • sketch.chamfer.distance_angle Distance and Angle Chamfer
    point | a, b: a corner of two lines; distance: along a, angle: deg from a
  • sketch.chamfer.two_distance Two Distance Chamfer
    point | a, b: a corner of two lines; distance: along a, distance2: along b
  • sketch.offset Offset (O)
    curves: [ids], distance: number (positive = left of the chain, or toward `side`), side?: [x,y], chain?: bool (default true: the connected chain)
  • sketch.trim Trim (T)
    curve: id, at: [x,y] (the piece to remove: between the nearest intersections around it)
  • sketch.extend Extend
    curve: id, at: [x,y] near the end to extend (to the nearest curve)
  • sketch.break Break
    curve: id, at: [x,y] (split at the nearest intersections around it) | params: split at points: [[x,y]…]
  • sketch.scale Sketch Scale
    entities: [curve/point ids], base: [x,y] or point ref, factor: number

SKETCH › INSERT

  • sketch.insert_dxf Insert DXF
    path | text: ASCII DXF (blocks expanded); at?: [x,y] offset, scale?, tolerance?: mm within which ends join (default 0.0001); plane?: (when no sketch is active, a new sketch on it; default XY)
  • sketch.insert_svg Insert SVG
    path | text: SVG; at?: [x,y] offset, scale?, tolerance?: mm within which ends join (default 0.0001); plane?: (when no sketch is active, a new sketch on it; default XY)
  • canvas.insert Canvas
    path | data (base64 PNG or JPEG); plane?: XY|XZ|YZ|{face: [x,y,z]}|… (default XY); center?: [x,y] on the plane, or at?: [x,y,z]; width?: mm (default 100); angle?: deg; opacity?: 0…1 (default 0.5); flip?; name?
  • canvas.decal Decal
    path | data; face: [x,y,z] on a planar face (the image is centred there); width?: mm; angle?: deg; opacity?: (default 1)
  • canvas.edit Edit Canvas
    canvas: id or name; center?, width?, angle?: deg, opacity?, flip?, visible?, name?

SKETCH › INSPECT

  • inspect.curvature_comb Curvature Comb Analysis
    curves?: [sketch curve ids] (sketch?: id|name, default active) | edges?: [[x,y,z] points on body edges]; density?: teeth per curve (default 40), scale?: comb length per unit curvature (default auto)
  • inspect.minimum_radius Minimum Radius Analysis
    curves?: [sketch curve ids] (sketch?) | edges?: [[x,y,z]…] | faces?: [[x,y,z]…] (default: every face of every body); the smallest radius of curvature of each and where it is
  • inspect.isocurve Isocurve Analysis
    faces: [[x,y,z]…] points on faces; count?: curves each way (default 8): the faces' isoparametric curves
  • inspect.center_of_mass Center of Mass
    bodies?: [names] (default all): the centre of mass (uniform density) of each and of all together
  • inspect.zebra Zebra Analysis
    stripes?: number across the view (default 12); clear?: true turns the analysis off
  • inspect.draft Draft Analysis
    pull?: [x,y,z] direction (default Z); angle?: degrees (default 1); clear?: true turns the analysis off
  • inspect.environment_map Environment Map Analysis
    clear?: true turns the analysis off
  • inspect.accessibility Accessibility Analysis
    direction?: [x,y,z] the tool comes from (default Z, from above); clear?: true turns the analysis off
  • inspect.curvature_map Curvature Map Analysis
    radius?: mm shown red (default: a tenth of the model's size); clear?: true turns the analysis off

SURFACE › CREATE

  • surface.patch Patch
    sketch?: id|name and profiles? (planar patches of sketch regions) | edges: [[x,y,z]] (points on a closed loop of a body's edges) and body?; name?, body_name?
  • surface.thicken Thicken
    bodies: [surface names]; thickness: expr (negative: against the normal); symmetric?: bool; operation?: new|join|cut|intersect; targets?

SURFACE › MODIFY

  • surface.stitch Stitch
    bodies: [names] (surfaces sewn into the first; a solid when they close); tolerance?: expr (default: tight)
  • surface.extend Extend
    body: planar surface name; edges: [[x,y,z]] (points on straight edges to push out); distance: expr
  • surface.trim Trim
    body: surface name; plane: XY|XZ|YZ|plane name|{origin, normal} or tool: {body, point} (a face, its surface extended); keep: [x,y,z] (a point on the side to keep)

SHEET METAL › CREATE

  • sheet.flange Flange
    base: sketch + profiles? (rule?, flip?) | contour: sketch + curves (open chain of lines) + distance (rule?, flip?, reverse?) | edge: edges: [[x,y,z] on top/bottom edges] + height (to the outer face), angle? (default 90 deg), radius? (default: rule), position?: inside|outside|middle, flip?, body?; type?: base|edge|contour (default from the inputs)
  • sheet.hem Hem
    edges: [[x,y,z] on sheet edges]; length (to the outer face); gap? (inner radius, default: rule); flip?; body?
  • sheet.fold Fold
    line: sketch + curve (a sketch line on the sheet's base face; followed when the sketch changes) or points: [[x,y,z], [x,y,z]]; angle? (default 90 deg); radius? (default: rule); position?: centerline|start|end|mould (where the line sits in the bend, default centerline); fixed?: [x,y,z] on the side that stays (default: the larger side); flip? (fold the other way); body?
  • sheet.convert Convert to Sheet Metal
    body; face: [x,y,z] on its large flat face (a plate of even thickness); rule?
  • sheet.flat_pattern Create Flat Pattern
    body? → flat size, outline loops and cut-outs (flat coordinates), bend lines and the bend table

SHEET METAL › MODIFY

  • sheet.unfold Unfold
    body? (default: the last sheet body) — all bends
  • sheet.refold Refold
    body? (default: the last unfolded sheet)
  • sheet.manage_rules Sheet Metal Rules
    name (new or existing); thickness?, k_factor?, bend_radius?, relief_width?, relief_depth?, corner_relief?, hem_gap?, gap? (expressions; `Thickness` is the rule's); active?: bool; delete?: bool

PLASTIC › CREATE

  • plastic.boss Boss
    position: [x,y,z] on a face; direction? (default: out of the face); diameter, height; hole_diameter?, hole_depth? (default: to the face); draft? (angle), fillet? (root radius); ribs? (count), rib_thickness?, rib_length? (out from the wall), rib_offset? (below the top); body?
  • plastic.lip Lip
    face: [x,y,z] on the rim face of a shelled body; width, height; type?: lip|groove; gap? (groove clearance); side?: inside|outside (which rim edge); body?
  • plastic.snap_fit Snap Fit
    position: [x,y,z] on a face; direction? (default: out of the face); hook: [x,y,z] (the catch's direction); length, thickness, width, catch_depth, catch_length; body?
  • plastic.rest Rest
    position: [x,y,z] on a face (the rest's centre); direction? (default: out of the face); width (round: the diameter); length? (a rectangle, along `along`); along?: [x,y,z]; height; draft? (angle; default: the body's plastic rule); thickness? (wall of a hollow rest); body?

PLASTIC › SETUP

  • plastic.manage_rules Manage Plastic Rules
    name (library or new rule; a new one starts from `from`, default ABS (1.5mm)); from?; material?; thickness?, nominal_radius?, clearance?, knife_edge?, reveal_height?, thickness_variation?, draft?, max_thickness?, min_thickness?, min_draft? (expressions; `Thickness` is the rule's); active?: bool (rule for bodies without one); delete?: bool (a design rule; a library rule goes back to the library values)
  • plastic.assign_rule Assign Plastic Rule
    bodies: [names]; rule: name (empty: no rule) — plastic features on these bodies take its draft and clearance as defaults

Other: sketch.*

  • sketch.edit Edit Sketch
    sketch: id or name
  • sketch.delete Delete Sketch Entities
    entities: [curve, point or constraint ids]
  • sketch.move_point Drag Sketch Point
    point: ref, to: [x,y]
  • sketch.drag Drag Sketch Geometry
    entity: point or curve, from: [x,y] where it was grabbed, to: [x,y]: a point goes to `to`, a line (spline, conic) slides by to - from, a circle or arc grabbed on its edge takes the radius to `to`; the rest moves as little as it can
  • sketch.dimension_text Move Dimension Text
    dimension: constraint id or parameter name; at: [x,y] text centre (sketch coordinates) | reset: true (default place)
  • sketch.options Sketch Palette Options
    sketch?: id|name (default active); any of show_profile, show_points, show_dimensions, show_constraints, show_projected, grid, snap, slice, sketch_3d: bool. Returns them all
  • sketch.chain Chain
    curve: id: the curves joined to it end to end (what a double-click selects)
  • sketch.solve Solve Sketch
    reports the solver status and degrees of freedom
  • sketch.centerline Centerline
    curves: [line ids], value?: bool (default toggles): centerline line type (not in profiles; an axis)
  • sketch.edit_text Edit Text
    link: the text's link id (or entity: one of its curves), text?, at?, height?, angle?: deg
  • sketch.toggle_driven Toggle Driven Dimension
    constraint: dimension id, driven?: bool (default toggles); a driving dimension gets a parameter with its current value
  • sketch.glyphs Constraint Glyphs
    sketch?: id|name: every constraint with where to draw its glyph (sketch coordinates)
  • sketch.snap Snap Inference
    at: [x,y], from?: [x,y] (previous click), radius?: mm (snap distance, default 1): what a click there would snap to and infer
  • sketch.export_dxf Save As DXF
    sketch?: id|name (default active); path?: file to write (else the DXF text is returned)
  • sketch.line3d 3D Line
    points: [[x,y,z]…] world points (2 or more): one 3D line per segment, joined end to end (needs 3D Sketch on)
  • sketch.spline3d 3D Spline
    points: [[x,y,z]…] world fit points (2 or more): a smooth 3D curve through them (needs 3D Sketch on)
  • sketch.point3d 3D Point
    point: [x,y,z] world point (needs 3D Sketch on)
  • sketch.move3d Move 3D Point
    wire: drawn 3D curve or point id, index?: fit point (default 0), to: [x,y,z] | by: [dx,dy,dz]; points joined to it move too
  • sketch.move Move/Copy Sketch Entities
    entities: [ids], translate?: [dx,dy], angle?: deg, center?: [x,y], copy?: bool
  • sketch.break_link Break Link
    link?: link id (j1) | entities?: [curve or point ids]: linked geometry becomes normal sketch geometry
  • sketch.auto_project Auto-Project Edges on Reference
    value?: bool (default toggles): project a face's edges when a sketch is created on it
  • sketch.redefine Redefine Sketch Plane
    sketch: id or name; plane: XY|XZ|YZ | construction plane name | {face: [x,y,z]} | {origin, normal} (the sketch keeps its geometry)
  • sketch.inspect Inspect Sketch
    sketch?: id|name (default active)

Other: canvas.*

  • canvas.calibrate Calibrate Canvas
    canvas: id or name; a, b: two points on the canvas plane [x,y]; distance: their true distance (mm): scales the image about a
  • canvas.delete Delete Canvas
    canvas: id or name

Other: sheet.*

  • sheet.rules List Sheet Metal Rules
    → rules with their expressions and values, and the active one
  • sheet.export_dxf Export Flat Pattern as DXF
    body?, path? → DXF (mm): OUTLINE, BEND_UP and BEND_DOWN (centre lines) layers

Other: plastic.*

  • plastic.rules List Plastic Rules
    → rules with their expressions and values, the active rule and the rules assigned to bodies

Other: component.*

  • component.from_bodies Create Components from Bodies
    bodies: [names] → one new component per body, named after it
  • component.activate Activate Component
    component: id or name (0 or "root" for the design)
  • component.list List Components
  • component.rename Rename Component
    component, name
  • component.delete Delete Component
    component: deletes it, its subcomponents, occurrences and features
  • component.move_bodies Move Bodies to Component
    bodies: [names], component
  • component.move_sketches Move Sketches to Component
    sketches: [ids or names], component
  • component.paste_new Paste New
    component: copied with its features into a new, independent component; translate?: [x,y,z]

Other: occurrence.*

  • occurrence.copy Paste (Instance)
    component (or occurrence): what to place again; parent?: component (default: the same parent); translate?: [x,y,z]
  • occurrence.move Move Occurrence
    occurrence; translate?: [x,y,z]; axis?: [x,y,z], angle?: radians or "30 deg", origin?: [x,y,z]; capture?: bool (default true; false keeps it pending until Capture Position)
  • occurrence.ground Ground
    occurrence; grounded?: bool (default toggles)

Other: joint.*

  • joint.limits Joint Limits
    joint; index?: value index (default 0); min?, max? (omit both to clear)
  • joint.edit Edit Joint
    joint; type?, flip?, offset?, angle?, suppressed?, name?
  • joint.delete Delete Joint
    joint: id|name (or origin: name, link: index)
  • joint.list List Joints
    → joints (type, occurrences, values, limits), origins, links, degrees of freedom, conflicts
  • joint.solve Solve Joints
    re-place the occurrences from the joints → conflicts, errors, dof

Other: contact.*

  • contact.edit Edit Contact Set
    set: id|name; occurrences?; name?; suppressed?: bool; delete?: bool
  • contact.list List Contact Sets
    → sets, enabled, all
  • contact.check Check Contact
    → pairs in contact sets that pass through each other now

Other: motion.*

  • motion.play Play Motion Study
    study: id|name; step (0…steps) — sets the joints to the study's values there (contact sets stop them) → positions
  • motion.export Export Motion Study
    study; samples? (default steps + 1); path? (.json or .csv) → occurrence positions per step (the design is not changed)
  • motion.list List Motion Studies
  • motion.delete Delete Motion Study
    study

Other: explode.*

  • explode.show Show Exploded View
    view: id|name (or none: back to the assembled placement) — shown only; the design keeps its placements
  • explode.list List Exploded Views
  • explode.delete Delete Exploded View
    view

Other: config.*

  • config.column Configuration Column
    param? | feature? (name); delete?: bool
  • config.row Configuration Row
    name; from? (row to copy; default the current values); values?: {param name or feature name: expression or true/false}; rename?; delete?: bool
  • config.activate Activate Configuration
    row: name — sets its parameter values and suppressions

Other: parts.*

  • parts.library Standard Parts
    family? → families (name, standard, sizes, lengths) and, with family, each size's dimensions (mm)
  • parts.insert Insert Part
    family (socket_head_cap_screw | hex_bolt | hex_nut | washer | set_screw | dowel_pin | bearing); size ("M6", or "608" for bearings); length? (mm; screws, bolts, set screws, pins; default a standard length near 2.5 d); at?: [x,y,z] on a hole's rim or wall (the part seats on the hole's face with a rigid joint) | point?: [x,y,z] with direction?: [x,y,z] (no joint); name?
  • parts.resize Change Part Size
    component: id|name of an inserted part; size?; length? — rebuilds it (joints keep it seated)

Other: body.*

  • body.rename Rename Body
    body: its name, name: the new name (features that use the body follow)

Other: browser.*

  • browser.group New Group
    folder: bodies|sketches|construction; component?: id (default 0); name?; items?: [keys] (they leave other groups)
  • browser.ungroup Ungroup
    group: id (its items go back to the folder)
  • browser.rename_group Rename Group
    group: id, name
  • browser.move Move Browser Items
    folder, component?, items: [keys]; group?: id (into it; omit to take them out of groups); before?: key (position in the group)
  • browser.order Order Browser Items
    folder, component?, order: [keys] (the folder's items in display order)

Other: document.*

  • document.units Change Design Units
    units: mm|cm|m|in|ft (display and unit-less parameters; values keep their meaning)
  • document.inspect Inspect Design
    measure?: bool (include body measurements)

Other: view.*

  • view.save New Named View
    name?; camera: {target: [x,y,z], yaw, pitch, distance, fov?} (replaces a view of the same name)
  • view.rename Rename Named View
    view: name, name
  • view.delete Delete Named View
    view: name
  • view.list List Named Views

Other: selection.*

  • selection.delete Delete
    items?: [selection items]; components?, occurrences?, canvases?: [ids or names]; dry_run?: bool → deleted, would_fail, skipped (one undo step)

Other: edit.*

  • edit.undo Undo (Ctrl+Z)
  • edit.redo Redo (Ctrl+Y)

Other: timeline.*

  • timeline.rename Rename
    feature: id|name, name
  • timeline.rollback Move Timeline Marker
    position: number of features to keep active (omit = end)
  • timeline.roll_to Roll History Marker
    position: number of features to keep active (omit = end) | feature: id|name (the marker goes right after it)
  • timeline.reorder Reorder Feature
    feature: id|name, position: its new index; refused when a feature that worked would fail
  • timeline.redefine Edit Feature
    feature: id|name, command: the feature's command id, params: its parameters (the feature is rebuilt in place)
  • timeline.dependents Feature Dependents
    feature: id|name → features deleted with it, and features that would fail without it
  • timeline.suppress Suppress Feature
    feature: id|name, suppressed?: bool (default toggles)
  • timeline.edit Edit Feature
    feature: id|name, set: {fields to change, e.g. {"extent": {"distance": "30"}}}
  • timeline.copy Copy Features
    features: [ids or names] (in timeline order on paste)
  • timeline.paste Paste Features
    translate?: [x,y,z] (moves the copies' geometry); the copies go at the marker

Other: select.*

  • select.set Select
    items: [{type: body|edge|face|feature|sketch_curve|sketch_point|profile, …}], add?: bool
  • select.clear Clear Selection

Other: parameters.*

  • parameters.list List Parameters
    favorites?: bool (only favourites), filter?: all|user|sketch|feature|favorites → parameters (name, expression, unit, value in its unit, source, feature/input, comment, favorite, uses, used_by, error) and cycles
  • parameters.add Add User Parameter
    name, expression, unit?, comment?, favorite?: bool
  • parameters.rename Rename Parameter
    name, new_name (expressions, feature inputs and dimensions that use it follow)
  • parameters.delete Delete Parameter
    name (refused while something uses it; the error lists the users)
  • parameters.favorite Favourite Parameter
    name, favorite?: bool (default true)
  • parameters.comment Comment Parameter
    name, comment
  • parameters.users Parameter Users
    name → users (parameters, feature inputs, dimensions) and the features it drives
  • parameters.graph Parameter Graph
    → nodes, edges [user, used] (users include Feature:<name>), cycles, errors
  • parameters.export Export Parameters
    format?: csv|json (default csv, or from the path), path?: write to a file → text
  • parameters.import Import Parameters
    text: CSV (header with name, expression, unit?, comment?) or JSON [{name, expression, unit?, comment?}] | path; all or nothing
  • parameters.complete Complete Expression
    prefix?: the text before the cursor → word, suggestions [{text, kind: parameter|function|unit|constant, detail}]

Other: expr.*

  • expr.evaluate Evaluate Expression
    expression, kind?: length|angle|unitless (or unit?: mm|in|deg…) → ok, value (mm/rad), display_value, references, error

Other: file.*

  • file.new New Design (Ctrl+N)
    name?
  • file.save Save (Ctrl+S)
    path? (default: current file)
  • file.save_as Save As
    path
  • file.export Export
    path; format?: stl|stla|obj|step|3mf (default from extension); bodies?: [names]
  • file.save_mesh Save As Mesh
    path; bodies?: [names]; ascii?: bool

Other: doc.*

  • doc.open Open (Ctrl+O)
    path: .solvecraft design, or a .step/.stp file (opens as a new design)
  • doc.recovery_list Recoverable Designs
    dir? (default: the recovery folder) → designs autosaved by apps that crashed or closed with unsaved changes
  • doc.recover Recover Design
    id (from doc.recovery_list); dir?; discard?: bool (delete the entry once open) — opens it with its file path, unsaved
  • doc.recovery_discard Discard Recovered Design
    id, or all: true; dir?

Other: model.*

  • model.edges List Edges
    body: name
  • model.faces List Faces
    body: name
  • model.threads List Threads

Other: appearance.*

  • appearance.library Appearance Library
    → the built-in appearances (name, colour, opacity)
  • appearance.list List Appearances
    body?: name → assigned appearances; with body, its resolved look and its faces' (face index, look)

Other: material.*

  • material.list List Materials

Other: engine.*

  • engine.commands List Commands