* Paper scene: one walk onto a writer's number grid, for every writer (#4890, C4) The SVG writer kept its own number grid: each coordinate whole nanometres, every joint written once, a closed path ending exactly on its first point, slivers merged into the next piece and a path's last piece kept. PDF, PNG and the Drawing workspace's canvas need the same rules, so they move into `geom::paper::grid`, and the SVG writer only formats what the walk gives it. - `Grid`: `per_mm` steps a millimetre. `Grid::NANOMETRE` (six decimals of a millimetre) is SVG's. `Grid::new` takes 1 to 1e9; a power of two keeps every number exact as a binary float, which a writer of 32-bit numbers such as PDF can use. - `Prepared::on_grid(grid) -> OnGrid`: each group's items as paths of `Piece`s (lines, arcs of at most a quarter turn, cubics, parabolas), y up as in the scene, a closed path's closing line left to the writer's close, and a report of widths snapped and pieces merged. Slivers are measured in the grid's own steps, so a coarser grid merges more. - The SVG writer flips y on the whole numbers it's given and writes the same bytes as before. `SvgReport` is the grid's report. - Ellipses, hyperbolas and Béziers above degree 3 are still refused (A3b), now by the walk. DXF stays on its own number rule for now: it writes exact entities (ELLIPSE, SPLINE) and is being reworked under #5318 to #5321; whether its polylines take a grid is decided there. Tests: every SVG test unchanged, bytes included; the grid's rounding and its bounds; the A3 border on the grid (y up, closing line left out, fill loops closed); slivers in the grid's own steps, with the merge boundary both ways (10 and 11 steps, a 6-8 and a 7-8 step); an arc's quarters with their radius and turn in steps; a width under half a step made one step; an ellipse refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Paper scene: a sheet PNG at a chosen dpi and colour (#4890 A4) A new writer, io::paper::paper_png, that reads the shared walk on the nanometre grid, the one the SVG writer uses, so a PNG draws the same joints, closures and merges. Neither the walk nor the SVG writer changes. - Curves are flattened within 1/8 px by geom::paper::flatten, which turns each walked piece back into its Seg2 (an arc's centre from its ends, radius and direction) and takes Seg2::chords' steps. chords is the bound Seg2::polyline already used (the chord's bulge for arcs, Wang's bound for the rest), moved out of polyline unchanged so both read one rule. polyline itself doesn't fit: it floors an arc's tolerance at a millionth of its radius and caps a piece at 4096 steps, coarser than asked without a word (0.1 mm, 9 px, on a 100 m arc at 2400 dpi). Over 2^20 points a path, or 2^24 a sheet, is refused instead. tiny-skia only gets lines: its own flattening stops at 64 lines a curve, more than a pixel off on a large arc at a high dpi. - 25 to 2400 dpi (300 by default). 8-bit greyscale (the default), 1-bit black and white (no anti-aliasing, every line at least a pixel wide), 24-bit colour, or 32-bit colour with transparent paper. - The file records its dpi (pHYs) and sRGB, so it prints at the sheet's size. - Each line is stroked once, then the sheet is drawn in bands of 16 MiB of pixels streamed into the file, so memory doesn't grow with the sheet. Each band also draws a row above and below that isn't written: tiny-skia starts an edge a little differently where a clip cuts it (one subsample, 16 levels, in a few pixels), so no written row sits on a cut and the bytes are the same whatever the band. - A line under a pixel wide (in greyscale and colour) is drawn as tiny-skia's hairline, its coverage scaled by its width: its stroked outline would fall between the rows the rasteriser samples and vanish. Tests: the pixel size and pHYs of A4 at 300 and 25 dpi; a line's and a fill's pixels at 254 dpi (butt ends, the fill's hole); each colour's PNG type, ink and paper; a hairline between two rows' centres that is light grey in greyscale and stays one pixel in black and white, and a bar a fifth of a pixel wide that black and white inks by its centre; a closed outline joined at its start; a 40 mm circle round to the pixel at 600 dpi; the same bytes drawn 1, 3 or 7 rows at a time or all at once, and again; the refusals; the flattener's bounds and counts, and its point cap at the exact boundary; the scene-JSON fuzz through the PNG writer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Geom: the pixel grid of a paper rectangle (#5624, W17b S1) One rule for where a picture's pixels lie on paper, shared by the picture renderer and the writers that place it: anchored at the rectangle's lower-left corner, rounded outward (W = ceil(w / p - 1e-9) with p = 25.4 / dpi), so the overhang is at the top and the right; rows run top-down. At most 65,536 px a side; nothing caps the total, as pictures are drawn in tiles and strips. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Paper scene: pictures, and a sheet's pixel grid and windows (#5624, W17b S3; #4890 A9) A picture on a sheet is `Item::Picture`: the rectangle it sits in, the name of its pixels (`source`, opaque as a group's object id is) and its opacity. The pixels stay out of the scene: a writer asks a `Pictures` provider for them on the grid it draws on. A PNG at 1200 dpi then gets a shaded view drawn at 1200 dpi, not one stretched, and a scene stays small, plain data. The pixel grid gains the two rules a sheet needs, beside the rectangle's own grid: - `PixelGrid::sheet`: the whole sheet's grid, anchored at its top-left corner, as an image of the sheet is drawn. - `PixelGrid::window`: the columns and rows a rectangle meets; a side on a pixel edge takes no sliver beyond it. A picture drawn into its window of the sheet grid is placed by copying whole pixels. `Picture::grid` is a picture's own grid at a dpi, for SVG and PDF to embed. `Scene::validate` counts a picture as one record. It refuses a rectangle outside the coordinate bound or without area, and an opacity that isn't above 0 and at most 1. `Aabb2` is read and written as `{min, max}`. On the walk (C4), which S3 follows here: `GridItem::Picture` passes a picture through with its rectangle in millimetres, since the pixel grid places its pixels, and the SVG and PNG writers refuse it ("not supported yet: pictures (A9)") until their own slices. `GridItem`, `GridGroup` and `OnGrid` are `PartialEq` only, as a picture holds numbers that aren't `Eq`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Paper scene: pictures in a sheet PNG (#5624, W17b S5) The sheet PNG writer draws the scene's pictures (S3's Item::Picture) where the scene orders them, between its lines and fills, from a Pictures provider. - The PNG is now the sheet's pixel grid (PixelGrid::sheet, S1): anchored at the sheet's top-left corner and rounded outward, so nothing is clipped. 210 mm at 300 dpi is 2481 px (it was 2480, rounded to the nearest). A side over 65,536 px is refused and the refusal names the limit ("a sheet PNG side of ... px at ... dpi is over the limit of 65536 px"); an A0 sheet fits up to about 1400 dpi. - A picture fills exactly its window of that grid, so it registers with the lines drawn over the same rectangle. Each band asks the provider for just its own rows, as straight-alpha sRGB RGBA, and the bytes are checked: too few or too many, or the provider's error, refuse the PNG with the picture's source named. paper::NoPictures refuses every picture. - Pictures are asked for at most MAX_PICTURE_DPI (1200): above it, on the grid k = ceil(dpi / 1200) times coarser, whose pixels each fill exactly k x k of the sheet's (both grids share the top-left anchor), so a 2400 dpi export doesn't ask a renderer for four times the pixels. - Source-over in sRGB, the picture's alpha times its opacity; transparent paper keeps the alpha. In black and white each picture pixel is dithered with an 8 x 8 ordered (Bayer) matrix at its place on the sheet, so bands don't show; lines stay solid. PngReport says whether anything was dithered. - In the writer a picture is one more shape a band draws, beside A4's outlines, fills and hairlines; the walk's picture arm passes pictures through, and the SVG writer still refuses them (A9). Tests: a picture's exact window and its register with a fill over the same rectangle, also on a sheet 100.8 px high (the grid anchored at its top); pictures keep their place in the paint order; opacity scales the alpha (on white and transparent paper, and the picture's own half alpha); above 1200 dpi a coarser picture's pixels are each copied 2 x 2; black and white dithers half of mid grey and keeps a line over it solid, whatever the band; a provider's short rows, its error and no provider are refused, and a picture off the sheet asks for nothing; the bands test draws three pictures 1, 3 or 7 rows at a time, the same bytes and each picture row asked for once; the sheet sizes on the outward grid and the 65,536 px refusal; the PNG fuzz with a picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Paper scene: pictures in a sheet SVG (#5624, W17b S5) A picture is an <image> holding a PNG as a data URI, on the picture's own pixel grid (Picture::grid) at the picture resolution the caller gives, 25 to 1200 dpi: paper_svg(scene, pictures, picture_dpi), the same shape as paper_png. Only its window over the sheet is embedded and asked for, so it never costs more pixels than the sheet holds; one off the sheet is left out. Its x, y, width and height are the grid's own pixel edges put on the nanometre grid every line is on, so a line on its edge has its number. Opacity below 1 is written, in millionths as lengths are. The PNG writer gives the embed its encoder settings (sRGB, the dpi recorded) and its provider check, now one function each, and streams the picture a band of rows at a time, refused once it passes the room left under the file's 64 MiB. Tests: the 1182 x 591 px grid at 300 dpi and its edges, a line on the edge, paint order and opacity, the crop and the picture off the sheet, the bands, and each refusal; the SVG fuzz case gains a picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
SolidCraft
An open-source parametric 3D CAD application built in pure Rust.
Sketch, constrain, and build solids with a parametric feature timeline. Change a dimension
and watch your model rebuild. Run it on your desktop, in your browser, or let AI agents
model through the built-in MCP server.
Website ·
Download ·
Try it in the browser ·
Documentation ·
Discord ·
SolidCore
A Raspberry Pi 4 case from an empty design: a sketch dimensioned with the board's parameters, extrude, fillet, shell, the rim rounded in one pick, an M2.5 standoff patterned on the 58 × 49 mm hole rectangle, port cut-outs, vent slots, "Pi 4" engraved in DejaVu Sans Bold and a PETG material defined in the design, with its mass in Properties, then height 24 → 32 mm in Parameters and it all rebuilds. 6× playback with the idle pauses cut. Full video, recorded with the demo script.
Download
- Windows: the installer.
- macOS: the disk image (Apple silicon and Intel).
- Linux: the AppImage, or
sudo snap install solidcraft(Snap Store). - Web: try it in your browser, no install.
ARM64 and 32-bit builds, portable zip, .deb, .rpm, Flatpak and the other packages are on the downloads page. The macOS app is signed with a Developer ID and notarized by Apple. The Windows downloads aren't signed yet: choose More info, then Run anyway in SmartScreen. On SolidCraft 0.11 to 0.14, download once by hand; see updating.
What works
Early but usable for simple parts: 75 of 76 public reference parts match their analytic or FreeCAD-computed volume, area, centre of mass and bounding box (oracle); the other is a tracked bug.
- Sketches and constraints: lines, arcs, splines, text and more, solved by our own constraint solver, with parameter-driven dimensions.
- Features: extrude, revolve, sweep, loft, fillet, chamfer, shell, draft, holes, threads, patterns, mirror, split and combine on an editable parametric timeline.
- Parameters and configurations with unit-aware expressions.
- Assemblies with joints, motion studies, contact sets, interference and mass properties.
- Sheet metal with flanges, hems, fold/unfold and flat patterns, and plastic enclosure features.
- Files: STEP, IGES, 3MF, STL, OBJ and glTF in and out where supported, URDF export,
.scdesigns (.solidcraftfiles from earlier releases still open). - Desktop app, browser app, headless CLI and MCP server, all running the same commands.
The full list is in docs/capabilities.md and docs/features.md; status and plans are in ROADMAP.md. See also what people built, 3D mouse support and the architecture.
Build from source
cargo run --release -p solidcraft -- --sample # desktop app with a sample part
cargo run --release -p solidcraft-cli -- commands # the command registry
cargo run --release -p solidcraft-cli -- run examples/bracket.json --out bracket.step
cargo xtask ci # fmt, clippy, tests, icons, assets, layers, wasm
More commands, the Windows cross-build and the browser build are in docs/building.md.
AI agents (MCP)
solidcraft-cli mcp is a Model Context Protocol server. Headless it
runs its own design session, or with --connect it drives the running app so you can watch.
Claude Code: claude mcp add solidcraft -- solidcraft-cli mcp. If a terminal doesn't find
solidcraft-cli (older Windows installers don't put it on PATH), give its full path (by default
"C:\\Program Files\\SolidCraft\\solidcraft-cli.exe"). Tools, access rules and an example session:
docs/mcp.md.
Scripts
A script is a JSON list of the same commands the UI runs, executed with solidcraft-cli run.
Format and example: docs/scripts.md. Pages can also be driven through the
browser command API.
Contributing
Help is welcome. CONTRIBUTING.md covers how to build and test, the code map, the clean-room and licence rules, and where help is wanted most. Open work is in GitHub issues; chat is on Discord.
Sponsors
SolidCraft's continuous integration runs on Blacksmith, which sponsors its CI minutes.
Disclaimer
The files in this repository are provided as is, without warranty, liability or indemnity, and Tangency Labs and the contributors accept no responsibility for any harmful or unlawful use of them: read DISCLAIMER.md.
Licence
MIT OR Apache-2.0, at your option. Developed under the clean-room contribution rules; third-party code and assets retain their documented licences and attribution.
SolidCraft is independent. Behaviour is designed from public documentation, general CAD conventions and user feedback. Reference tests use analytic values and open-source tools. SolidCraft is not affiliated with, sponsored by or endorsed by Autodesk, Dassault Systèmes, PTC, Siemens or any other CAD vendor. Fusion, AutoCAD, Inventor, SolidWorks, Onshape, Creo, Blender and Tinkercad are trademarks of their respective owners, named only to describe compatibility (see NOTICE).

