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

SolidCraft

SolidCraft is an open-source parametric 3D CAD application written in pure Rust. 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.

SolidCraft 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.

Existing .solvecraft designs and solvecraft-cli commands keep their names and behavior.

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

SolidCraft 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

SolidCraft keeps its compatibility names: the desktop executable is solvecraft, the CLI is solvecraft-cli, and saved designs remain .solvecraft. Published 0.2.0 and 0.2.1 packages were released under the SolveCraft name.

Desktop

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

git clone https://github.com/bherbruck/solidcraft && cd solidcraft
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.

Linux Flatpak

Install Flatpak using your distribution’s package manager. For an installation that receives updates, add Flathub for the required Freedesktop runtime and the SolidCraft application feed to the same user installation:

flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --user --if-not-exists solvecraft https://bherbruck.github.io/solidcraft/flatpak/solvecraft.flatpakrepo
flatpak install --user solvecraft ai.storyteller.solvecraft
flatpak run ai.storyteller.solvecraft

The application ID and remote name retain their compatibility names. To update an existing remote that still uses the former website address, run flatpak remote-modify --user --url=https://bherbruck.github.io/solidcraft/flatpak/repo/ solvecraft and then flatpak update --user.

Alternatively, download the release’s .flatpak file for your architecture and run flatpak install --user ./solvecraft-VERSION-linux-ARCH.flatpak. The bundle supplies the Flathub runtime repository address; installation still needs internet access to fetch any missing runtime. Launch with flatpak run ai.storyteller.solvecraft, rather than looking for a host solvecraft binary. Files chosen through Open/Save use the desktop file-chooser portal; command-line paths outside Documents may need a specific additional filesystem permission.

Web

The same app runs in the browser: open SolidCraft 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 right-drag orbits freely (the mouse preset in Preferences switches to SolidWorks or Inventor style). Shift+middle orbits freely in the default and Inventor presets. Shift+right-drag snaps to the nearest standard or diagonal view; Shift does not request snapping when the preset already requires it to orbit. The axis view shows the world’s X/Y/Z directions in SolidCraft’s colors. Click a signed axis tip to look along that axis; click the aligned tip again to look from the opposite side. Drag the axis view to orbit around the view centre, with Shift for snapping. Top and bottom keep the nearest quarter-turn heading. The house and navigation bar’s Home button go home. Right-click for the marking menu: eight commands around the cursor, plus a menu for whatever is under it. Shaded views omit certified smooth joins between spline spans on extruded walls. Real creases, cap outlines and authored face splits remain visible. Wireframe retains its edge presentation, and hiding a join does not remove its CAD identity or change the exported solid. Unsupported or uncertain wall geometry keeps its existing edge presentation.
  • 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.

Navigation › Smooth wheel zoom is on by default. Turn it off to apply each wheel or trackpad event immediately, with no smoothing tail. It is independent of Animate view changes, which controls axis views, Look At, Home and Fit. Re-enabling smoothing during a wheel gesture takes effect after that gesture ends, avoiding leftover zoom being replayed.

Shift-orbit snapping includes 45-degree edge views and isometric corner views, with a lightly damped spring transition and a small boundary margin to prevent flicker.

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.

Curves adapt to the current zoom and display scale. Picking and highlighting use the same visible boundary, including small circles at Fit and sketches in rotated components. Profile shading follows those curves while preserving holes. Zooming changes the display without changing sketch geometry, dimensions, or undo history. On short windows or enlarged displays, the Sketch Palette’s options scroll while its constraint status and Finish Sketch button remain visible.

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.

Run from a script or the control channel, every command that adds sketch geometry returns the ids of what it added (created: {points, curves, constraints, dimensions}). Ids are stable and never reused within a sketch, even after a delete, so a script can dimension or constrain the new geometry right away.

Extend finds intersections with exact ellipses, rational conics and spline spans. It keeps the original line or circular support and extends only beyond the selected endpoint; conflicting constraints or an ambiguous overlap leave the sketch unchanged.

Closed convex chains of tangent lines and circular arcs support inward offsets that consume a rounded corner. When the offset passes the corner radius, the neighbouring straight supports meet at a sharp corner. The surviving lines and endpoint IDs stay stable; the retired arcs return under their original IDs if a later edit restores their radius. Whole-boundary profile selections follow this transition through save and reopen, so an extruded lip can remain associative. Existing eligible closed analytic offsets upgrade without changing their saved geometry or IDs.

This transition requires a closed convex boundary with tangent junctions and a certified nonempty region. An extinguished arc referenced individually by a constraint or another offset cannot retire. Creating another offset from an already sharp retired boundary also requires breaking its association first. Open, nonconvex and mixed free-form chains keep their existing behavior. Break Offset Association freezes the current boundary, including private arc centres and construction flags, for independent editing.

Offsets of smooth ellipses, conics and splines follow their source after dimension changes, dragging and reopening a design. Their stored cubic spline has a whole-span normal-distance error bound of 0.002 mm. Singular tangents, curvature cusps, possible self-intersections and excessive subdivisions are refused. Offset these curves individually: mixed chain junctions currently require separate geometry. sketch.offset.break releases an offset for independent editing, and undo restores its association. Older independently sampled offsets remain independent. Smooth open and newly created smooth closed offsets can be offset again, including bounded refinement after a source edit. New closed outputs use a periodic cubic fit with continuous tangent and curvature at the seam. Break Offset Association preserves that interpolation, and its closing point remains joined when dragged. Deleting an output also removes its unused private fit points; deleting a source with a retained dependent is refused.

For explicit closed fit splines, sketch.spline.fit_point accepts periodic: true and at least three distinct cyclic samples. It closes the loop automatically. Existing calls and older saved closed splines retain natural interpolation; older associative closed offsets also retain their saved natural fit. A legacy seam without a unique normal still refuses another offset. Periodic intent is stored on each curve and survives save, reopen and Break. While drawing a fit spline, click its starting point after three distinct samples to close it smoothly. Right-click finishes an ordinary natural fit. Offset accepts both ellipse handles and dense fitted curves; the second click sets the side and distance from the curve.

Enable 3D Sketch in the Sketch Palette to draw lines, fit splines and points off the plane. New 3D geometry follows its sketch’s component placement, including the selected occurrence and pending position changes. Click a drawn fit point to show its move triad; its arrows move along world X, Y and Z. Scripts also supply world coordinates to sketch.line3d, sketch.spline3d, sketch.point3d and sketch.move3d. Activate the desired occurrence before editing an explicit sketch belonging to a component with multiple placements.

Older drawn or detached 3D wires keep their saved world positions. Included and surface-linked wires with an explicit component marker keep that meaning after the link is broken. Old files whose links became lost after an incorrectly scoped cross-component refresh may require a fresh link once the source is available; their stored point values are preserved on opening.

Constraints and dimensions

Horizontal/Vertical accepts a click on a line or clicks on two distinct sketch points, including the origin, endpoints and circle centres. For two points, it chooses horizontal or vertical alignment from their current positions. Esc cancels a pending first point.

The solver is SolidCraft’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.

For Coincident, pick two points, a point and a curve (in either order), or two distinct straight lines. Two lines join their closest pair of endpoints; the first picked line stays in place when the existing constraints allow it. Pick endpoints explicitly when you want to connect a different pair.

Horizontal/Vertical also applies immediately to one selected line or two distinct selected points. Shift-click to add the second point before choosing the constraint.

For Parallel, Perpendicular, Equal, Tangent, Concentric and Collinear, select exactly two sketch curves (Shift-click to add the second) and choose the constraint to apply it immediately. You can also start the constraint first and then pick its curves. An incomplete or mixed selection leaves the tool ready for ordinary picking.

Coincident accepts an exact selection of two points or a point and a curve. Midpoint accepts one point and one line, in either selection order. Symmetry accepts two distinct points and one symmetry line, regardless of where the line occurs in the selection. Successful constraints clear the selection; rejected constraints preserve it.

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.

A feature remembers a picked profile by the curves around it, not by where it was clicked: "profiles": [{"curves": ["l1", "l2", "a1", "l3"]}] (or [{"curves": ["c1"]}] for a circle) picks the region whose outer loop is made of exactly those curves, or the pieces of them that other curves cut off. It is found again on every recompute, so the feature keeps the same region when a parameter moves the geometry. Islands inside the region are its holes. When no region, or more than one (both halves of a circle cut by a line), is bounded by exactly those curves, the feature reports an error rather than taking another region. Profiles picked by index or by point ([{"point": [x, y]}]) are stored the same way when the feature is made.

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

Extrude automatically selects Join for additions when the active component already has a solid, including sideways elbow connections and screw bosses that meet only part of a face. Extruding into existing material selects Cut. In an empty component it selects New Body; choose New Body explicitly to keep an addition separate. Changing the default does not alter operations already saved in a design.

Editing an extrusion retains its signed distances, From/To Object inputs, taper expressions and explicit body targets. Revolve edits retain world, sketch and explicit-line axes, both angle expressions and To Object targets. An unchanged All profiles selection continues to include profiles added later. Cancel restores the complete timeline; a successful edit is one undo step, including downstream parameter changes. Editing a placed component previews the edited feature before its downstream history. Cylindrical Emboss profile guides follow the sketch’s chosen occurrence frame.

Newly selected adaptive profiles store complete outer and hole source identities when the entire boundary can be qualified. Changing an ellipse or periodic offset after extrusion can then change its spline span count while keeping the same selected material region. A partial, intersected or ambiguous boundary cannot satisfy that whole-source reference; changing the hole identities also makes it lost. Older curve-span selections retain their exact stored multisets. If an older feature has already lost its profile after subdivision changed, edit the feature and reselect the intended current region to capture the new identity.

New symmetric revolves split the entered angle evenly around the sketch profile, regardless of which way the selected axis points. Two Sides measures the second angle backward and the first angle forward along the sketch-normal sweep direction. Profiles on opposite sides of an axis each use their own sweep direction. Designs saved before format 11 keep their original angular placement when opened, edited or copied; creating another revolve uses the new behavior. To Object follows that same direction for each selected profile, including curved target faces. Pick a target away from the axis so its angular position is defined.

For cylindrical Emboss, select a supported exterior cylinder face and a tangent sketch profile. Deboss cuts radially into the wall. Orthogonal line profiles, including holes and concave outlines, wrap with exact radial walls; the profile must stay clear of seams and ends. Complete cylinders and coaxial sleeves are supported. Capture pending component positions before applying. The feature stores the selected target and author occurrence paths, so edits and reopening retain the chosen instances. Other curved surfaces and free-form profiles remain unsupported. A new Emboss without an explicit face infers a unique eligible supported cylinder; select the face when the target is ambiguous. Older saved features without a face retain their planar placement.

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.

Editing a feature from a script

timeline.inspect {feature} returns a feature’s definition: the fields timeline.edit changes, with their current values. timeline.edit {feature, set} merges set into that definition (objects are merged field by field, anything else replaces the value), rebuilds the feature and reports recomputed and restored. The feature’s type cannot change, and sketch geometry is edited with the sketch commands. Expressions are text ("30", "30 mm", "width / 2", "45 deg"); a bare number is a length in the design’s units, or degrees for an angle.

{"command": "timeline.inspect", "params": {"feature": "Slab"}}
→ {"type": "ExtrudeFeature",
   "definition": {"sketch": 1, "profiles": {"type": "all"}, "operation": "new_body",
                  "extent": {"distance": "10", "direction": "positive"}},
   "inputs": [{"label": "Distance", "param": "d1", "expression": "10", "unit": "mm"}], …}
{"command": "timeline.edit", "params": {"feature": "Slab", "set": {"extent": {"distance": "12"}}}}

inputs lists the feature’s named inputs. parameters.change {name: "d1", expression} sets one of those directly. Optional fields that are not set do not appear in the definition; they can still be added through set. The fields of the common features:

FeatureFields
Extrudeextent: {distance, direction: positive|negative|symmetric, distance2, start_offset, through_all, taper, to, to_offset, from}; operation: new_body|join|cut|intersect; targets; profiles; sketch
Revolveangle, angle2, symmetric, axis, to, operation, targets, profiles, sketch
Filletradius, edges (a point on each edge), body, style
Chamferdistance, distance2 or angle, flip, edges, body
Boxlength, width, height, corner, operation
Cylinderradius, height, base, axis, operation
Holediameter, depth, position, direction, hole (simple, drilled, counterbore, countersink), thread, to
Shellthickness, faces, direction, body, tangent_chain
Draftangle, faces, neutral, pull, body
Offset facedistance, faces, body
Patternpattern (its kind and counts/spacing), features, bodies
Mirrorplane, features, bodies, combine

For any other feature, timeline.inspect shows the fields.

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.

Face Extrude, face Revolve, and Press Pull retain linked generated profiles. When a modified planar face has no construction certificate, these internal profiles use the legacy named-face attachment and projection behavior so upstream edits still update the plane and outline. This compatibility path is private to those modeling tools: newly attached user sketches require a certified face or an explicitly fixed plane. Legacy internal attachments can follow the nearest matching face after topology changes; inspect their result after major upstream edits. Activate the intended occurrence before generating a face profile from a repeated component; capture a separately moved source position before creating a legacy profile across components.

Negative Press Pull across supported aligned prismatic faces retains round through-holes, including after a joined extrusion and in a rotated component occurrence. The cut partitions the current material into axial sections, preserving the hole through undo, redo, and reopen. Walls that cannot be qualified as a parallel extrusion still use the general Boolean path and may report an unsupported coincident-surface operation atomically.

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.

In the SOLID toolbar’s CONFIGURE panel, click Parameters to open the user and model parameter editor. The separate Configurations button opens the configuration table; Configure remains in the dropdown for configuring columns.

Press Enter in the new parameter’s name, expression or comment field to add it. Valid entries return focus to the name field for the next parameter; invalid entries remain editable. In expression fields, Up/Down selects an autocomplete suggestion, Enter/Tab accepts it, and Escape dismisses the suggestions. Accepting a suggestion does not also submit the parameter.

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, physical materials and body appearances, switched with config.activate. The table’s Library picker selects material or appearance values. Choose Follow material to clear an appearance override. Custom looks retain their colour and opacity across activation, undo and save/reopen. See configuration commands and examples.

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. SolidCraft asks before the next command if a move wasn’t captured. Ground fixes an occurrence in place.

Paste New copies nested component definitions, sketches, feature histories and generated dimension parameters. Repeated child instances remain shared within the new copy. Materials, appearances and wholly internal joints/motion records receive copied references. Design-wide user parameters remain shared; retained external links and omitted cross-boundary motion records are reported. Unsupported destructive links refuse the entire copy. See individual-instance and copy details.

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.

Right-click a component in the Browser and choose Properties to inspect material-aware mass, volume, surface area, centre of mass and world bounds. Descendants and repeated instances contribute to assembly totals; pending moves are included. A component uses its first placement; occurrence.properties selects a specific instance. Root Properties measures the complete assembly. Volumes are summed, including overlapping bodies, rather than measuring their union. The same workflow is available through component.properties and occurrence.properties; see placement and measurement details.

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 or a bend-table CSV through the save picker.

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.

For a shop drawing or spreadsheet, sheet.export_bend_table returns CSV by default; choose format: "json" for structured data. It includes the sheet rule, thickness, bend angles, radii, allowances and flat centre-line coordinates. Lengths use millimetres and angles use degrees. Supply path to save it, or omit path to receive the content through the command response. body defaults to the last sheet body. Exporting does not add a timeline entry or consume an undo step.

{"command":"sheet.export_bend_table","params":{"format":"csv","path":"bracket-bends.csv"}}
{"command":"sheet.export_dxf","params":{"path":"bracket-flat.dxf"}}

DXF cutout geometry preserves lines, circles and circular arcs; other curves are sampled adaptively. The OUTLINE, BEND_UP and BEND_DOWN layers separate cuts from bend centres.

Base-flange profiles keep their interior holes. Through cuts that cross a sheet edge become notches in the flat outline; overlapping and repeated cuts remove their combined area once. The flat preview, DXF and unfolded solid use the same material boundaries. Lines and circular arcs remain exact; other profile curves use a 0.001 mm chord tolerance.

Cuts can leave separate material pieces, including the centre of a ring cut. These pieces remain one logical sheet group through Unfold / Refold and export as separate flat outlines. Large profiles that exceed the geometry processing budget report an error instead of exporting a partial outline.

Parallel, untapered extrude cuts also preserve shallow pockets through Unfold / Refold. The pocket floor and side walls remain solid geometry; overlapping cuts from opposite sides become a through hole wherever they remove the whole thickness. Flat-pattern JSON lists pockets with their side, depth and bottom/top thickness coordinates in millimetres. DXF pocket boundaries use POCKET_BOTTOM_Z…_TO_…_MM, POCKET_TOP_Z…_TO_…_MM, or POCKET_INTERNAL_Z…_TO_…_MM layers. These are machining operations, so do not treat every pocket layer as a through-cut contour when importing the drawing into a CAM tool.

Extrude Intersect on a straight base or an unfolded sheet retains the material inside both the current tool footprint and its depth interval, including interior holes, detached islands and earlier pockets. A shallow intersection records the removed layers above and below the retained material as top and bottom pockets. The sheet rule keeps the original stock gauge; the remaining solid can be thinner. A completely empty intersection removes the sheet and its metadata together. DXF records pocket depth in its layer names; importing that DXF as ordinary sketch geometry does not reconstruct the machining depths automatically. Each positive retained or removed depth layer must meet the kernel’s 0.000001 mm extrusion resolution; smaller layers report an error rather than being rounded away. To Object can retain a tool that trims to a straight prism, using its actual trimmed footprint. A curved tool with varying depth is refused before changing sheet geometry. Pocket profiles can move with planar panels when forming, but a pocket crossing a bend zone cannot yet be refolded. Machining folded bend material, tapered sheet cuts and other unsupported 3D sheet Cut/Intersect operations report an error before changing the design; they cannot silently restore removed material during a later sheet rebuild.

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.

Current designs use format 12, including persistent analytic offset boundary roles and association profile references, durable sketch face anchors, periodic fit splines and versioned revolve conventions. It also retains recorded body transfers, explicit 3D curve coordinate spaces and certified face bindings, alongside semantic configuration styles, associative free-form offsets and cylindrical emboss selections. Older ownership maps, geometry and expressions retain their saved meaning. Legacy 3D curves with retained associations receive their producer’s coordinate-space marker without changing any saved points; detached curves retain world coordinates. A lost cross-component association refreshed by an older build may need an explicit relink or refresh when its source returns. Loading never guesses a numerical correction. Association records, coordinate arrays and placements are bounded and checked.

Saving in the web app

In the web app, Save and Save As keep designs in this browser’s private storage; they do not open a dialog for choosing a folder on your computer. Clearing the site’s data can remove those designs. Download a copy to keep it outside the browser or move it to another device.

  1. Open the File menu and choose Open to show Files in this browser.
  2. Choose Download open design for the current design, including its unsaved changes, or Download beside a stored file for its last saved contents.
  3. Find the .solvecraft file in your browser’s downloads. Whether a folder chooser appears depends on your browser’s download settings.

Use Open from computer… in the same panel to reopen a downloaded design. The web app works on a browser-stored copy; later saves do not update the original downloaded file. Use Rename beside a stored file to change its name. Exports such as STEP and STL are also downloaded.

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.

SolidCraft and AI agents (MCP)

SolidCraft 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, visible), sketches (curves, points, constraints, profiles, DOF, solve status, visible), selection, undo depth. Hide helper sketches, bodies or construction planes with execute browser.visibility ({items, visible} or {folder, visible}).
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 recomputed (features rebuilt), restored (features whose earlier result for the same inputs was reused, e.g. a value set back) and 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).

To change a feature after it is made, read its definition with execute timeline.inspect ({feature}: id or name) and change fields with timeline.edit ({feature, set}; e.g. {"extent": {"distance": "30"}}). The fields per feature type are listed in the book’s Solid features chapter.

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 goes back to an engine checkpoint (edit.checkpoint) taken before the first step: the design, the active sketch and the undo history are restored exactly as they were, however long the history is. Batches that call file.new or doc.open get the design back, not its file path. (An older app without checkpoints falls back to undoing as many steps as the batch added to the undo history.)

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 SolidCraft

SolidCraft is an open-source parametric 3D CAD app written in pure Rust. 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.95 or newer (required by egui/eframe 0.36). For the web build you also need rustup target add wasm32-unknown-unknown and trunk.

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

SolidCraft is the product and repository name. Cargo packages and executables retain their solvecraft names for compatibility with existing scripts and MCP clients. Build the desktop app with cargo build --release -p solvecraft, or the CLI with cargo build --release -p solvecraft-cli. Run the CLI with cargo run --release -p solvecraft-cli -- commands (or target/release/solvecraft-cli commands after building). There is no Cargo package named solidcraft.

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.

NixOS: nix develop

NixOS has no /usr/lib, so nothing is on LD_LIBRARY_PATH and a plain cargo run finds none of the libraries the app loads at run time: the Wayland ones winit opens the window through, the EGL loader wgpu renders through, and libdbus-1, which rfd’s file dialogs reach xdg-desktop-portal through (a dialog then never appears and the pick returns no path):

ERROR solvecraft: winit EventLoopError: ... The wayland library could not be loaded
ERROR eframe::native::run: Exiting because of error: WGPU error: Failed to create surface for any enabled backend: {}
ERROR rfd::backend::xdg_desktop_portal::portal::libdbus: Can't connect to a portal: libdbus-1.so not found

nix develop (the flake.nix in the repository root) supplies Wayland, X11, EGL, Vulkan-loader and D-Bus runtime libraries. It also exposes the NixOS driver manifests in /run/opengl-driver/share, so Vulkan can discover the system drivers. Enable hardware.graphics.enable on NixOS; other Linux distributions may need a matching nixGL wrapper. The default development shell retains the Rust/C toolchain already on PATH; nix develop .#rust supplies the pinned toolchain and builds in target/nix.

To launch, use nix run .#solidcraft -- --sample. To install the app’s terminal commands, desktop entry and generated icons, use nix profile install .#solidcraft. nix build alone creates a build result; it does not install a launcher in your desktop menu. The installed solidcraft command and compatible solvecraft command run the same wrapped app; solvecraft-cli is included as well. See Nix installation and troubleshooting for remote-flake commands, desktop integration and how to distinguish an old executable on PATH.

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. SolidCraft 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 xtask test (or cargo test --workspace --profile ci)
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.

Use the ci profile. CI runs the tests with --profile ci: light optimisation, line tables, no LTO, and debug assertions off (truck has debug-only assertions that reject valid geometry). It builds several times faster than --release. If a test fails only in a plain debug build, say so in the PR. cargo xtask test builds the tests with it and then runs every test binary at once (cargo test runs them one after another); arguments after -- go to each binary, e.g. cargo xtask test -- shell. Tests run beside each other on a busy machine, so a test must not depend on wall-clock timing.

The Fusion oracle is a set of 76 reference parts, each built in Fusion from a recipe. SolidCraft 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, the asset, licence and layer checks, clippy (-D warnings), then the tests with the wasm build check alongside. 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).

Help reviewers see what the words mean. For observable CAD, UI or model behavior, please include actual descriptive screenshots or a short recording in the issue or PR. This evidence is required for changes or bugs that can be demonstrated in the app, including kernel, geometry, assembly and backend work. Coding agents should run a small reproduction and capture the evidence themselves; please don’t ask a human for evidence the agent can produce. Show the actual failure or result, preferably before and after, with brief steps and expected/actual behavior. An arrow or annotation can help identify the detail that matters.

Flowcharts and Mermaid diagrams cannot replace evidence of visible behavior. Diagrams are useful for architectural reasoning when actual screenshots cannot illustrate the point. Changes with no useful observable behavior do not need screenshot or video evidence. Use a small example without private designs or sensitive details; attach the media or link to an accessible recording rather than committing generated review artifacts.

Keep checking upstream commits and the relevant PR’s merge status throughout the work and before handoff. Integrate upstream merges, resolve conflicts without losing others’ changes, and validate the integrated result. Before pushing, run git pull --rebase origin main, re-run CI if it brought changes, and push without force. Report the upstream/PR state you checked, any conflicts resolved and validation results. While a task remains active, follow an open PR through its status changes; at handoff, clearly identify anything still awaiting merge.

Much of SolidCraft 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. SolidCraft 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 SolidCraft. SolidCraft isn’t affiliated with Autodesk. Fusion is a trademark of Autodesk, Inc.

Architecture

SolidCraft 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 SolidCraft’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 SolidCraft’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 SolidCraft 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 SolidCraft 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. SolidCraft 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, xtask assets, xtask licences --check, xtask layers, clippy with -D warnings, the tests with the ci profile 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.

CI profile

CI runs the tests with --profile ci: light optimisation, line tables, no LTO and debug assertions off. Some truck debug assertions fail on valid geometry; the ci profile disables them while building faster than --release. If a test fails only in a plain debug build, say so in the PR.

Run cargo xtask test to build with this profile and run the test binaries concurrently, with doc tests alongside. Arguments after -- go to each test binary, for example cargo xtask test -- shell. To run the test binaries sequentially instead, use cargo test --workspace --profile ci.

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.

The channel is untrusted input. A request line may be at most 16 MiB; a longer one is answered with {"ok": false, "error": "request too large"} and the connection is closed, as are connections that send eight lines that are not JSON. At most 16 connections are served at once (further ones get {"ok": false, "error": "too many connections"}), at most 64 queued requests run per frame so a flood cannot starve drawing, and a request that the UI thread has not answered within 60 seconds is replied to with {"ok": false, "error": "timeout"}. The same 16 MiB line limit applies to the MCP server.

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.

Every sketch.* command that changes a sketch adds created: {points, curves, constraints, dimensions} to its result (and wires for 3D curves): the ids of what it added, in creation order, next to whatever else the command returns (curves, sides, corners…). Sketch ids are stable and never reused, even after a delete (l1, l2, then l3 once l2 is gone; only undo rolls the numbering back with the design), so they can go straight into later commands:

{"id":7,"method":"engine.execute","params":{"command":"sketch.line","params":{"points":[[0,0],[40,0]]}}}
→ {"curves":["l1"],"created":{"points":["l1.start","l1.end"],"curves":["l1"],"constraints":["k1"],"dimensions":[]},…}
{"id":8,"method":"engine.execute","params":{"command":"sketch.dimension","params":{"entities":["l1"],"value":50}}}

UI

MethodParamsNotes
ui.inspect—UI state, what is hidden (visibility: bodies, origin and construction items, sketches; set it with the browser.visibility command), 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, dark, …), and hiddenBodies, hiddenOrigin, hiddenSketches, shownSketches (whole lists; browser.visibility changes single items)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; orientation tips axis_view:x, axis_view:-x (and y/z)
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); cut by the section the viewport shows (inspect.section, an open Section Analysis dialog’s preview, a sketch’s Slice), with the cut face capped
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.filepath; insert?: boolstarts an interactive background Open or Insert; poll ui.inspect.file_busy for completion; the window stays responsive
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, alias, tinkercad), zoom_reverse, zoom_to_cursor, smooth_wheel_zoom (default true), orbit_cursor, look_at_anim, look_at_speed, msaa, length_decimals, angle_decimals, theme (dark, light, system), language (en, ja; see localization), ui_scale, up_axis (z, y), offer_stand_up; open (a page: General, Navigation, Keyboard Shortcuts, Design, Graphics, Sketch, Selection) opens the Preferences dialog there; button: ok, cancel, apply, defaults; stand_up: true stands up a just-opened Y-up filesets preferences (kept between runs), returns them all plus dialog (the open page or null), dark and stand_up_offer
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: a number from 1 to 5 (times the normal speed) or "instant" (no animation: the command runs at once and the pointer jumps to where it acted), 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"}}

Hold Shift during an orbit gesture to snap to the nearest standard or diagonal view. This applies to right-drag, the Orbit navigation tool, and dragging the axis view. Under the default preset, Shift+middle also orbits with snapping; middle alone pans. Axis-view tips use world coordinates, regardless of the up-axis preference. Clicking an aligned tip again flips to its opposite.

Shift-orbit snapping includes 45-degree edge views and isometric corner views, with a lightly damped spring transition and a small boundary margin to prevent flicker.

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). SolidCraft replays the recipe through its command engine and compares body count, volume and area (relative tolerance 1e-3) and face/edge/vertex counts (exact). SolidCraft 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 SolidCraft/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; SolidCraft 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 SolidCraft/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 SolidCraft/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; SolidCraft’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 SolidCraft 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): 335 / 541 commands live (62%). SOLID + SKETCH: 188 / 291 (65%). Registered commands: 311.

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: 630 / 1175 (54%).

TabPanelLiveTotal%
SOLIDCREATE233566%
SOLIDMODIFY193259%
SOLIDASSEMBLE122157%
SOLIDCONFIGURE33100%
SOLIDCONSTRUCT192095%
SOLIDINSPECT122548%
SOLIDINSERT61155%
SOLIDSELECT0150%
SOLIDPOSITION22100%
SKETCHCREATE4141100%
SKETCHMODIFY1212100%
SKETCHCONSTRAINTS1515100%
SKETCHCONFIGURE33100%
SKETCHINSPECT122548%
SKETCHINSERT71354%
SKETCHASSEMBLE010%
SKETCHSELECT0150%
SKETCHFINISH SKETCH22100%
SHEET METALCREATE151979%
SHEET METALMODIFY122157%
SHEET METALASSEMBLE112055%
SHEET METALCONFIGURE33100%
SHEET METALCONSTRUCT192095%
SHEET METALINSPECT122548%
SHEET METALINSERT61155%
SHEET METALSELECT0150%
SHEET METALREFOLD010%
SHEET METALPOSITION22100%
ASSEMBLYINSERT4850%
ASSEMBLYCREATE5862%
ASSEMBLYRELATIONSHIPS41136%
ASSEMBLYMOTION33100%
ASSEMBLYMODIFY81173%
ASSEMBLYCONTACT22100%
ASSEMBLYCONFIGURE33100%
ASSEMBLYCONSTRUCT192095%
ASSEMBLYINSPECT122548%
ASSEMBLYSELECT0150%
ASSEMBLYPOSITION22100%
PLASTICenclosure subset55100%

Deferred tabs

TabPanelLiveTotal%
SURFACECREATE92536%
SURFACEMODIFY172665%
SURFACEASSEMBLE112055%
SURFACECONFIGURE33100%
SURFACECONSTRUCT192095%
SURFACEINSPECT122548%
SURFACEINSERT61155%
SURFACESELECT0150%
SURFACEPOSITION22100%
MESHCREATE2540%
MESHPREPARE040%
MESHMODIFY63020%
MESHASSEMBLE122157%
MESHCONFIGURE33100%
MESHCONSTRUCT192095%
MESHINSPECT122548%
MESHINSERT51050%
MESHSELECT0100%
MESHEXPORT010%
MESHPOSITION22100%
FORMCREATE21414%
FORMMODIFY53116%
FORMSYMMETRY060%
FORMUTILITIES050%
FORMCONSTRUCT192095%
FORMINSPECT122548%
FORMINSERT71354%
FORMASSEMBLE010%
FORMSELECT0150%
FORMFINISH FORM010%
PLASTICSETUP22100%
PLASTICCREATE273577%
PLASTICMODIFY193259%
PLASTICASSEMBLE112055%
PLASTICCONFIGURE33100%
PLASTICCONSTRUCT192095%
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 › CONFIGURE › Create Configuration Rules
  • SOLID › CONSTRUCT › Offset Plane
  • SOLID › CONSTRUCT › Plane at Angle
  • SOLID › CONSTRUCT › Tangent Plane
  • SOLID › CONSTRUCT › Midplane
  • SOLID › CONSTRUCT › Perpendicular Plane
  • 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 › CONFIGURE › Create Configuration Rules
  • 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 › CONSTRUCT › User Coordinate System (UCS)
  • 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 › 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

SolidCraft 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). SolidCraft 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 (313 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 | construction plane name or id | {origin, x_dir, y_dir} | {face: [world x,y,z], body?: exact world body, index?: face index, path?: complete occurrence ids, fixed?: bool}; new face attachments require a unique certified source, fixed:true explicitly freezes the selected plane; offset?: expr, project_edges?: bool, 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] | [{curves: [ids]}] (the profile whose outer loop is made of exactly these curves, or pieces of them; found again on every recompute, so it follows parameter changes; islands inside are its holes; an error when no profile or several match) | [{point:[x,y]}] (index and point picks are stored by their curves when that finds the same profile); face?: [x,y,z] (extrude a planar body face instead of a sketch profile); direction?: positive|negative|symmetric; distance2?; start_offset?: expr (along the sketch normal, also with direction: negative); 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, otherwise join existing solids; new in an empty component); 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, independent of axis direction); convention?: sketch_normal|legacy_axis_start (default sketch_normal; legacy retains older angular starts); 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] (numbers in mm or length expressions; default: the origin); operation?
  • solid.cylinder Cylinder
    radius | diameter, height: expr; base?: [x,y,z] (centre of the bottom: numbers in mm or length expressions; default: the origin); axis?: X|Y|Z|[x,y,z] (default Z); operation?
  • solid.sphere Sphere
    radius | diameter: expr; center?: [x,y,z] (numbers in mm or length expressions; default: the origin); operation?
  • solid.torus Torus
    major, minor: expr (radii); center?: [x,y,z] (numbers in mm or length expressions; default: the origin); 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|construction plane name or id|{origin, normal}|{origin, x_dir, y_dir} (a construction plane keeps the mirror following it)
  • 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] (numbers in mm or length expressions; default: the origin); 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, profiles?; depth; mode?: emboss|deboss; targets?; face?: world point [x,y,z] selecting a planar or complete exterior cylindrical wall (inferred when exactly one supported tangent cylinder matches the selected profiles)
  • 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]; mid?: [[position 0…1 from start, radius expr], …] (variable radii between the ends); smooth?: bool (smooth instead of straight between the radii)
  • 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 → recomputed: features rebuilt, restored: features whose earlier result for the same inputs was reused (e.g. a value set back)
  • 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
  • construct.plane.perpendicular Perpendicular Plane
    plane: plane or planar face ref; line: straight edge, sketch line or axis ref; name? The plane holds the line and is square to the plane

SOLID › ASSEMBLE

  • component.create New Component
    name?, parent?: component id or name (default: the active one); parent_path?: exact parent placement; 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
    occurrences?: [ids] (default all); drops selected 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 | construction plane name or id | {origin, normal}; offset?: expr along the plane's normal (XY: +Z, XZ: +Y, YZ: +X); at?: expr, the cut's world coordinate on the named plane's axis (XY: Z, XZ: Y, YZ: X), instead of offset; flip?: bool; or clear: true. Returns the cut's origin and normal, and for a named plane its axis and at

SOLID › CONFIGURE

  • config.configure Configure
    params?: [parameter names]; features?: [feature names]; materials?: [body names]; appearances?: [body 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); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.rectangle.two_point 2-Point Rectangle (R)
    p0, p1: opposite corners [x,y]; construction?. Returns sides {bottom, right, top, left} (line ids) and corners {bottom_left, bottom_right, top_right, top_left} (point ids); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.rectangle.three_point 3-Point Rectangle
    p0, p1: first edge; p2: point on the opposite edge; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.rectangle.center Center Rectangle
    center, corner. Returns sides {bottom, right, top, left} (line ids) and corners {bottom_left, bottom_right, top_right, top_left} (point ids); connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.circle.center Center Diameter Circle (C)
    center: [x,y] or point ref, radius | diameter: number; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.circle.two_point 2-Point Circle
    p0, p1: diameter ends; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.circle.three_point 3-Point Circle
    p0, p1, p2; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.arc.three_point 3-Point Arc
    start, end, through; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.arc.center_point Center Point Arc
    center, start, end (counter-clockwise from start) | center, start, sweep: signed degrees; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.polygon.inscribed Inscribed Polygon
    center, radius (to vertices), sides, angle?: deg; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.polygon.circumscribed Circumscribed Polygon
    center, radius (to edge midpoints), sides, angle?: deg; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.polygon.edge Edge Polygon
    p0, p1: one edge, sides; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.slot.center_to_center Center to Center Slot
    p0, p1: arc centres, width; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.slot.overall Overall Slot
    p0, p1: overall ends, width; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.point Point
    point: [x,y], id?; connect?: bool (default true: a new point landing exactly on an existing point, the origin too, is made coincident with it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.dimension Sketch Dimension (D)
    entities: [refs], type?: auto|distance|horizontal|vertical|length|radius|diameter|angle, value?: number or expression (default: current); text_at?: [x, y]. Two lines: the angle between them (acute, or 180° − it when the value is nearer that) unless text_at picks a sector. Returns measured in unit (mm or deg)
  • 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?. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.circle.two_tangent 2-Tangent Circle
    curves: [two lines/circles/arcs], radius | diameter: expr, near: [x,y] (which of the solutions). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.circle.three_tangent 3-Tangent Circle
    curves: [three lines/circles/arcs], near: [x,y] (which of the solutions). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.slot.center_point Center Point Slot
    center: slot centre, end: one arc centre, width. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.slot.arc_three_point Three Point Arc Slot
    start, through, end: points on the centre arc, width. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.slot.arc_center Center Point Arc Slot
    center, start: centre arc start, end: direction of the centre arc end (counter-clockwise), width. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.spline.fit_point Fit Point Spline
    points: [[x,y] or point refs…] (2…500): the spline passes through them, periodic?: boolean (default false); periodic cubic fit closes smoothly through at least 3 distinct samples. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.spline.control_point Control Point Spline
    points: control points (2…500), cubic. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.spline.control_point_5 Control Point Spline (Degree 5)
    points: control points (2…500), degree 5. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.conic Conic Curve
    start, end, apex: [x,y] or point refs; rho?: 0…1 (default 0.5: parabola). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.text Text
    text: string (\n breaks lines), at: baseline start [x,y], height?: capital height mm (default 5), angle?: deg. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.pattern.circular Circular Pattern
    entities: [ids], center: [x,y] or point ref, count: 2…500, angle?: total deg (default 360). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.pattern.rectangular Rectangular Pattern
    entities: [ids], dir?: [x,y] (default x), count, spacing; count2?, spacing2? (perpendicular direction). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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}]. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.intersection_curve Intersection Curve
    a, b: {body} | {face: [x,y,z], body?}: where they meet, as linked 3D curves. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)

SKETCH › CONSTRAINTS

  • sketch.constraint.horizontal_vertical Horizontal/Vertical
    line: id | points: [a, b]; mode?: horizontal|vertical
  • sketch.constraint.coincident Coincident
    a, b: points, a point and a curve, or two distinct lines (joins their closest endpoints)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.fillet Fillet
    point: corner shared by two curves ("l1.end") | points: [corners] (all rounded alike: one radius, the rest equal) | a, b: two lines or arcs meeting at a corner; radius: expr. A corner that dimensions or constraints measure to stays as a virtual sharp corner on both curves' extensions (`virtual_corners`; the moved ones in `moved_to_virtual_corner`). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.chamfer.equal_distance Equal Distance Chamfer
    point | a, b: a corner of two lines; distance. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.chamfer.distance_angle Distance and Angle Chamfer
    point | a, b: a corner of two lines; distance: along a, angle: deg from a. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.chamfer.two_distance Two Distance Chamfer
    point | a, b: a corner of two lines; distance: along a, distance2: along b. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.trim Trim (T)
    curve: id, at: [x,y] (the piece to remove: between the nearest intersections around it). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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 intersections) | points: [[x,y]…] (1…1000 positions projected onto the curve; duplicates are merged). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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.draw.dimensioned Draw Dimensioned Sketch Geometry
    command: sketch creation command, params: object, dimensions: object of role to numeric or expression value; creates geometry and all driving dimensions atomically in one undo step. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.spline3d 3D Spline
    points: [[x,y,z]…] world fit points (2 or more): a smooth 3D curve through them (needs 3D Sketch on). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • sketch.point3d 3D Point
    point: [x,y,z] world point (needs 3D Sketch on). Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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.offset.break Break Offset Association
    curve: derived offset curve id (retains geometry and makes its points editable)
  • sketch.move Move/Copy Sketch Entities
    entities: [ids], translate?: [dx,dy], angle?: deg, center?: [x,y], copy?: bool. Returns created: {points, curves, constraints, dimensions}: the ids of what it added (stable and never reused: usable right away in sketch.dimension, sketch.constraint.*, sketch.delete…)
  • 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: as sketch.create; author_path?: exact owner occurrence ids; explicitly rebinds or freezes the plane while preserving geometry and dimension names
  • 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
  • sheet.export_bend_table Export Bend Table
    body?, format?: csv|json (default csv), path? → bend table in mm and degrees, including rule and flat centre-line coordinates

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); occurrence?, path?: exact placement used to author shared geometry
  • component.list List Components
  • component.properties Component Physical Properties
    component?: id or name (default active); descendants?: bool (default true). Measures the first placement, or all placements for root, including pending moves; material-aware mass and center of mass in world coordinates
  • 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: independently copy its nested definition graph and history, materials and styles; occurrence?, path?: selected source placement; translate?: [x,y,z]. Reassigned external bodies become base geometry; retained external links and omitted boundary joints are reported

Other: occurrence.*

  • occurrence.properties Occurrence Physical Properties
    occurrence: id or name; path?: [occurrence ids] (required if nested under multiple instances); descendants?: bool (default true). Physical properties of this placement in world coordinates, including pending moves
  • occurrence.copy Paste (Instance)
    component (or occurrence): what to place again; path?: validates selected occurrence; parent?: component (default: the same parent); translate?: [x,y,z]. Copies the selected effective transform, including pending moves
  • occurrence.move Move Occurrence
    occurrence; path?: exact placement; space?: local|world (default local); 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). Nested occurrence definitions remain shared
  • 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? | material? | appearance? (target name); delete?: bool
  • config.row Configuration Row
    name; from? (row to copy; default current values); values?: {column key: expression, suppression, material name, appearance name or Look object}; rename?; delete?: bool
  • config.activate Activate Configuration
    row: name — sets its parameter values and suppressions
  • config.preview Preview Configuration
    row: name — validates a rebuild without changing the design or history
  • config.rule Configuration Rule
    rule?: {id,name,enabled,condition,effect}; id? + delete?; id? + index? (ordered move)
  • config.rules Configuration Rules
    ordered typed rules, rows and active state

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)
  • browser.visibility Show/Hide
    visible: bool; items?: [sketch id or name | body name | construction name | O|X|Y|Z|XY|XZ|YZ]; or folder?: bodies|sketches|construction|origin (all of it); or paths?: [[occurrence ids]] (exact placements and descendants), isolate?: bool (requires paths and visible:true). View state: not saved, not an undo step

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)
  • edit.checkpoint Checkpoint
    → {checkpoint: id}: remember the design, active sketch and undo history (restore with edit.restore_checkpoint)
  • edit.restore_checkpoint Restore Checkpoint
    checkpoint: id; forget?: bool (true = drop it without going back). Goes back to it, undo history included

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, merged into the feature's definition as timeline.inspect returns it, e.g. {"extent": {"distance": "30"}} (extrude), {"angle": "90 deg"} (revolve); expressions are text} → recomputed: features rebuilt, restored: features whose earlier result for the same inputs was reused
  • timeline.inspect Inspect Feature
    feature: id|name → type, definition (the fields timeline.edit's set changes, with their current values), inputs: [{label, param, expression, unit}] (named inputs parameters.change can also set), error, warning
  • 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