Public reference tests: analytic and FreeCAD expected values (#310)

The oracle no longer uses Fusion measurements. tests/oracle/<case>/ now holds the 76 recipes
(our own format, with recorded measurements and tool-specific fields removed) and an
expected.json per case:

- 55 analytic cases: closed-form values from tools/oracle-analytic/ (stdlib Python, formulas
  in each case's "method").
- 21 FreeCAD cases: the same recipe built with FreeCAD's public scripting API by
  tools/freecad-oracle/ (an external program, not a dependency), with a reference.stl for 20.

solvecraft-cli oracle compares body count, volume, area, centre of mass, bbox, face counts
where the geometry fixes them, and, against reference.stl, the sampled Hausdorff distance and
the principal moments (spatial.rs from #301). cargo xtask oracle reads tests/oracle, writes
docs/oracle.md with the source of each value, and supports known_failure marks for tracked
bugs. CI runs it on every PR; a manual workflow regenerates the FreeCAD values. step-corpus,
seam normalisation and the through-all emulation, which only existed to compare with Fusion,
are removed.

75/76 pass; 75-sm-bracket-holes is a known failure (#296).


Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

Co-authored-by: bherbruck <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-10-12 10:43:43 -04:00
1 parent f0dda667bb
commit e12758ff91
204 files changed
+27106 -932

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@
- [Persistent naming](dev/naming.md)
- [Test harnesses](dev/testing.md)
- [Control protocol](dev/control-protocol.md)
- [Fusion oracle](dev/oracle.md)
- [Reference tests](dev/oracle.md)
- [Command parity](dev/parity.md)
- [STEP import](dev/step-import.md)
+1 -1
View File
@@ -7,7 +7,7 @@ SolidCraft is a Cargo workspace in layers. A crate only depends on crates in low
The web app, `apps/solvecraft-web`, is the desktop front end compiled to wasm with
[trunk](https://trunkrs.dev). `xtask` holds the repository's own tooling: `cargo xtask ci`,
`layers`, `assets`, `oracle`, `parity`, `step-corpus` and `book`.
`layers`, `assets`, `oracle`, `parity` and `book`.
A few rules follow from the layering:
+1 -2
View File
@@ -13,8 +13,7 @@ profile and the wasm build check.
| Layout tests | `crates/ui-egui/tests/ui_layout.rs`, `window_widths.rs` | panels and dialogs at every window width |
| Design-file corpus | `apps/solvecraft-cli/tests/corpus` | every saved file version still opens and evaluates the same |
| Kill mid-save | `apps/solvecraft-cli/tests/kill_mid_save.rs` | a save killed at any moment leaves a readable file |
| Fusion oracle | `cargo xtask oracle` → [docs/oracle.md](oracle.md) | parts rebuilt from recipes, compared with Fusion's measurements |
| STEP corpus | `cargo xtask step-corpus` → [docs/step-import.md](step-import.md) | STEP files from Fusion opened and measured |
| Reference tests | `cargo xtask oracle` → [docs/oracle.md](oracle.md) | parts rebuilt from `tests/oracle/*/recipe.json`, compared with analytic or FreeCAD expected values |
## UI scenarios
+1 -1
View File
@@ -21,7 +21,7 @@ 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](https://github.com/bherbruck/solidcraft/blob/main/ROADMAP.md) has the details, and
[command parity](dev/parity.md) and the [Fusion oracle](dev/oracle.md) measure how far along it
[command parity](dev/parity.md) and the [reference tests](dev/oracle.md) measure how far along it
is.
Existing `.solvecraft` designs and `solvecraft-cli` commands keep their names and behavior.
-84
View File
@@ -1,84 +0,0 @@
# IGES import (Fusion corpus)
Generated by `cargo xtask step-corpus --iges`. Each case is a reference part exported from Fusion as IGES 5.3 (kept locally in `plan/fusion/oracle/`, never committed). SolidCraft opens `part.igs` exactly as File → Open does and compares with Fusion's measurements of the part it exported: body count, volume and area (relative tolerance 1e-3) and the number of faces read from the file. How IGES is read: see [STEP import](step-import.md#iges). 50-sweep-tight-bend is written by Fusion as five loose, partly degenerate surfaces (seams running into slits, a ring given twice); Fusion cannot read it back as a solid either.
**75 / 76 files pass.**
| Case | Result | Bodies | Volume mm³ (got / Fusion) | Area mm² (got / Fusion) | Faces (got / Fusion) | ms | Notes |
|---|---|---|---|---|---|---|---|
| 01-box | pass | Body1 | 24000.000 / 24000.000 | 5200.000 / 5200.000 | 6 / 6 | 35 | |
| 02-cylinder | pass | Body1 | 35339.346 / 35342.917 | 6125.844 / 6126.106 | 3 / 3 | 399 | |
| 03-box-through-hole | pass | Body1 | 45738.459 / 45738.053 | 9327.794 / 9327.788 | 7 / 7 | 292 | |
| 04-revolve-vase | pass | Body1 | 112488.577 / 112499.479 | 13156.092 / 13156.615 | 5 / 5 | 1515 | |
| 05-taper-extrude | pass | Body1 | 61814.826 / 61814.826 | 9676.915 / 9676.915 | 6 / 6 | 28 | |
| 06-symmetric-extrude | pass | Body1 | 30000.000 / 30000.000 | 6200.000 / 6200.000 | 6 / 6 | 33 | |
| 07-fillet-all-edges | pass | Body1 | 23340.463 / 23340.849 | 4726.195 / 4726.265 | 26 / 26 | 1285 | |
| 08-chamfer | pass | Body1 | 23730.667 / 23730.667 | 5029.352 / 5029.352 | 10 / 10 | 64 | |
| 09-shell | pass | Body1 | 13632.000 / 13632.000 | 13992.000 / 13992.000 | 11 / 11 | 76 | |
| 10-rect-pattern-holes | pass | Body1 | 29803.844 / 29803.540 | 14652.460 / 14652.389 | 18 / 18 | 1988 | |
| 11-circular-pattern-flange | pass | Body1 | 66758.418 / 66758.844 | 19320.582 / 19320.795 | 10 / 10 | 2360 | |
| 12-mirror-feature | pass | Body1 | 21392.312 / 21392.920 | 6530.923 / 6530.973 | 10 / 10 | 753 | |
| 13-combine-join | pass | Body1 | 45569.249 / 45571.680 | 8634.154 / 8634.336 | 9 / 9 | 383 | |
| 14-combine-cut | pass | Body1 | 42932.838 / 42931.417 | 9706.788 / 9706.858 | 7 / 7 | 1401 | |
| 15-combine-intersect | pass | Body1 | 54451.677 / 54454.273 | 7382.501 / 7382.743 | 7 / 7 | 2559 | solid 'Body1': face wrapping around a closed surface kept whole: unsupported seam loop layout (8 runs) |
| 16-sweep | pass | Body1 | 2584.300 / 2584.724 | 1779.617 / 1779.698 | 5 / 5 | 1957 | |
| 17-loft | pass | Body1 | 35120.291 / 35125.498 | 6495.290 / 6495.000 | 6 / 6 | 601 | |
| 18-hole-features | pass | Body1 | 33890.962 / 33890.710 | 8568.687 / 8568.696 | 13 / 13 | 1552 | |
| 19-l-bracket-constrained | pass | Body1 | 14720.000 / 14720.000 | 5472.000 / 5472.000 | 8 / 8 | 39 | |
| 20-slot-constrained | pass | Body1 | 2965.385 / 2965.487 | 1774.641 / 1774.690 | 6 / 6 | 88 | |
| 21-draft | pass | Body1 | 54574.737 / 54574.737 | 8781.998 / 8781.998 | 6 / 6 | 38 | |
| 22-split-body | pass | Body1, Body2 | 75390.604 / 75398.224 | 12954.438 / 12955.175 | 6 / 6 | 802 | |
| 23-bracket-multifeature | pass | Body1 | 59331.547 / 59331.129 | 18290.759 / 18290.737 | 19 / 19 | 1441 | |
| 24-user-parameters | pass | Body1 | 34822.114 / 34821.903 | 8114.166 / 8114.159 | 7 / 7 | 437 | |
| 25-sketch-triangle | pass | Body1 | 4032.155 / 4032.155 | 2274.099 / 2274.099 | 5 / 5 | 51 | |
| 26-sketch-belt | pass | Body1 | 11326.049 / 11326.170 | 5637.345 / 5637.404 | 7 / 7 | 599 | |
| 27-sketch-trapezoid | pass | Body1 | 5342.307 / 5342.257 | 3066.958 / 3066.946 | 8 / 8 | 832 | |
| 28-blind-pocket-fillets | pass | Body1 | 84299.377 / 84299.249 | 16279.553 / 16279.572 | 23 / 23 | 2087 | |
| 29-two-sided-and-partial-revolve | pass | Body1 | 25731.359 / 25731.307 | 6312.589 / 6312.602 | 12 / 12 | 229 | |
| 30-fillet-after-shell | pass | Body1 | 21745.916 / 21745.876 | 14968.283 / 14968.332 | 27 / 27 | 1298 | |
| 31-fillet-l-all-edges | pass | Body1 | 14321.380 / 14321.626 | 5023.977 / 5024.045 | 38 / 38 | 2673 | |
| 32-boss-plate-fillets | pass | Body1 | 33590.273 / 33590.350 | 9929.855 / 9929.864 | 10 / 10 | 3878 | |
| 33-fillet-tangent-chain | pass | Body1 | 8236.989 / 8237.122 | 2793.535 / 2793.569 | 14 / 14 | 1687 | |
| 34-chamfer-then-fillet | pass | Body1 | 22892.359 / 22892.421 | 4753.672 / 4753.688 | 18 / 18 | 577 | |
| 35-fillet-curved-curved | pass | Body1 | 63169.296 / 63171.443 | 10269.098 / 10269.348 | 6 / 6 | 9098 | solid 'Body1': face wrapping around a closed surface kept whole: split line does not reach the rings (×2); solid 'Body1': face wrapping around a closed surface kept whole: unsupported seam loop layout (8 runs) |
| 36-corner-blend-two-radii | pass | Body1 | 57799.380 / 57800.240 | 8585.423 / 8585.524 | 18 / 18 | 3015 | |
| 37-pyramid-apex-blend | pass | Body1 | 15465.384 / 15465.575 | 3914.545 / 3914.581 | 18 / 18 | 5090 | |
| 38-coincident-cylinders | pass | Body1 | 15706.154 / 15707.963 | 3769.748 / 3769.911 | 3 / 3 | 354 | |
| 39-tee-pipe | pass | Body1 | 24095.711 / 24096.237 | 16709.770 / 16710.048 | 7 / 7 | 2800 | solid 'Body1': face wrapping around a closed surface kept whole: unsupported seam loop layout (8 runs) (×2) |
| 40-box-sphere-torus | pass | Body1 | 36287.765 / 36287.433 | 10696.188 / 10696.165 | 10 / 10 | 6187 | |
| 41-tube-radial-holes | pass | Body1 | 20243.087 / 20243.981 | 14873.680 / 14873.773 | 22 / 22 | 9763 | |
| 42-overlapping-patterns | pass | Body1 | 38488.942 / 38487.064 | 21687.167 / 21686.603 | 43 / 43 | 5322 | a solid's shell does not close; solid 'Body1': imported as an open body (the shell is not closed) |
| 43-cut-splits-body | pass | Body1, Body2 | 28000.000 / 28000.000 | 7200.000 / 7200.000 | 12 / 12 | 128 | |
| 44-shell-l-bracket | pass | Body1 | 8384.000 / 8384.000 | 8784.000 / 8784.000 | 15 / 15 | 132 | |
| 45-shell-boss-plate | pass | Body1 | 13676.813 / 13676.602 | 14156.507 / 14156.602 | 15 / 15 | 785 | |
| 46-shell-two-open-faces | pass | Body1 | 16044.000 / 16044.000 | 11398.000 / 11398.000 | 10 / 10 | 141 | |
| 47-draft-curved-pocket | pass | Body1 | 111388.066 / 111387.712 | 17700.333 / 17700.361 | 11 / 11 | 290 | |
| 48-pulley | pass | Body1 | 168508.132 / 168510.841 | 23718.191 / 23718.644 | 12 / 12 | 4041 | |
| 49-helical-sweep | pass | Body1 | 4740.421 / 4738.099 | 4768.066 / 4767.652 | 3 / 3 | 33507 | |
| 50-sweep-tight-bend | FAIL | Body1 | 1621.826 / 3758.443 | – / – | 4 / 5 | 813 | the surfaces do not close into a solid: imported as an open body; solid 'Body1': face #231: face boundary is not a closed loop; solid 'Body1': imported as an open body (the shell is not closed) |
| 51-loft-rect-circle-offset | pass | Body1 | 61129.309 / 61133.116 | 9412.484 / 9411.655 | 6 / 6 | 532 | |
| 52-circular-pattern-fillet | pass | Body1 | 47206.121 / 47206.850 | 14141.995 / 14142.171 | 21 / 21 | 5990 | |
| 53-path-pattern | pass | Body1 | 35010.576 / 35010.398 | 16463.935 / 16463.894 | 13 / 13 | 1431 | |
| 54-mirror-body-fillets | pass | Body1 | 22780.189 / 22780.089 | 6972.821 / 6972.832 | 12 / 12 | 861 | |
| 55-tapped-hole-cosmetic | pass | Body1 | 17542.374 / 17542.314 | 4761.564 / 4761.596 | 8 / 8 | 543 | |
| 56-holes-on-curved-face | pass | Body1 | 77940.974 / 77942.388 | 10592.891 / 10593.139 | 7 / 7 | 3231 | solid 'Body1': face wrapping around a closed surface kept whole: a hole crosses the split line |
| 57-hole-pattern-sketch-points | pass | Body1 | 36767.089 / 36766.712 | 11818.157 / 11818.153 | 24 / 24 | 2099 | |
| 58-phone-stand | pass | Body1 | 74559.445 / 74559.177 | 29148.741 / 29148.785 | 19 / 19 | 181 | |
| 59-enclosure-base | pass | Body1 | 34309.026 / 34309.486 | 33795.404 / 33795.610 | 39 / 39 | 2120 | solid 'Body1': 2 face(s) could not be meshed and are not shown; solid 'Body1': face wrapping around a closed surface kept whole: cannot cut a ring edge at the split line (×4) |
| 60-pipe-flange | pass | Body1 | 323660.822 / 323660.979 | 56984.283 / 56984.564 | 16 / 16 | 4410 | |
| 61-shaft-keyway | pass | Body1 | 47521.369 / 47525.946 | 9676.777 / 9677.096 | 14 / 14 | 3019 | solid 'Body1': face wrapping around a closed surface kept whole: a hole crosses the split line |
| 62-heat-sink | pass | Body1 | 43125.000 / 43125.000 | 45725.000 / 45725.000 | 66 / 66 | 278 | |
| 63-hinge-knuckle | pass | Body1 | 4783.060 / 4783.117 | 4110.247 / 4110.287 | 14 / 14 | 974 | solid 'Body1': face wrapping around a closed surface kept whole: a hole crosses the split line |
| 64-sketch-tangent-arc-chain | pass | Body1 | 4800.000 / 4800.000 | 3843.701 / 3843.712 | 9 / 9 | 113 | |
| 65-sketch-gear-tooth | pass | Body1 | 3987.829 / 3988.084 | 1945.416 / 1945.485 | 9 / 9 | 119 | |
| 66-sketch-linkage | pass | Body1 | 8045.432 / 8045.386 | 4308.169 / 4308.175 | 10 / 10 | 1662 | |
| 67-sm-base-flange | pass | Body1 | 15000.000 / 15000.000 | 12800.000 / 12800.000 | 6 / 6 | 31 | |
| 68-sm-edge-flange | pass | Body1 | 18972.511 / 18972.622 | 16057.510 / 16057.550 | 14 / 14 | 182 | |
| 69-sm-tray | pass | Body1 | 29665.924 / 29666.234 | 24979.972 / 24980.086 | 46 / 46 | 568 | |
| 70-sm-flange-45 | pass | Body1 | 18450.720 / 18450.777 | 15629.618 / 15629.637 | 14 / 14 | 112 | |
| 71-sm-custom-rule | pass | Body1 | 10053.662 / 10053.717 | 13896.235 / 13896.299 | 22 / 22 | 277 | |
| 72-sm-contour-flange | pass | Body1 | 12097.511 / 12097.622 | 10411.960 / 10412.002 | 22 / 22 | 264 | |
| 73-sm-hem | pass | Body1 | 21334.436 / 21334.723 | 17994.374 / 17994.473 | 22 / 22 | 317 | |
| 74-sm-unfold-refold | pass | Body1 | 15800.138 / 15800.304 | 13610.263 / 13610.326 | 32 / 32 | 286 | |
| 75-sm-bracket-holes | pass | Body1 | 13915.905 / 13916.012 | 12035.926 / 12035.962 | 25 / 25 | 558 | |
| 76-sm-thickness-variant | pass | Body1 | 22520.416 / 22520.575 | 16064.080 / 16064.128 | 14 / 14 | 139 | |
+95 -90
View File
@@ -1,96 +1,101 @@
# Fusion oracle
# Reference tests
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.
Generated by `cargo xtask oracle`. Each case in [`tests/oracle/`](../tests/oracle/) is a part described by our own `recipe.json`. SolidCraft replays the recipe through its command engine and compares the result with `expected.json`: body count, volume and area (relative tolerance 1e-3 unless the case states another), centre of mass and bounding box (within 1e-3 of the part's size), face count where the geometry fixes it, and, where the case has a `reference.stl`, the sampled symmetric (Hausdorff) distance between the two surfaces and the principal second moments of both triangulations. Every rebuilt part is also exported as STEP, validated and read back.
**76 / 76 cases pass** (0 need features not implemented yet).
Where the expected values come from (the **Source** column):
| Case | Result | Notes |
|---|---|---|
| 01-box | pass | |
| 02-cylinder | pass | |
| 03-box-through-hole | pass | |
| 04-revolve-vase | pass | |
| 05-taper-extrude | pass | |
| 06-symmetric-extrude | pass | |
| 07-fillet-all-edges | pass | |
| 08-chamfer | pass | |
| 09-shell | pass | |
| 10-rect-pattern-holes | pass | |
| 11-circular-pattern-flange | pass | |
| 12-mirror-feature | pass | |
| 13-combine-join | pass | |
| 14-combine-cut | pass | |
| 15-combine-intersect | pass | |
| 16-sweep | pass | |
| 17-loft | pass | |
| 18-hole-features | pass | |
| 19-l-bracket-constrained | pass | |
| 20-slot-constrained | pass | |
| 21-draft | pass | |
| 22-split-body | pass | |
| 23-bracket-multifeature | pass | |
| 24-user-parameters | pass | |
| 25-sketch-triangle | pass | |
| 26-sketch-belt | pass | |
| 27-sketch-trapezoid | pass | |
| 28-blind-pocket-fillets | pass | |
| 29-two-sided-and-partial-revolve | pass | |
| 30-fillet-after-shell | pass | |
| 31-fillet-l-all-edges | pass | |
| 32-boss-plate-fillets | pass | |
| 33-fillet-tangent-chain | pass | |
| 34-chamfer-then-fillet | pass | |
| 35-fillet-curved-curved | pass | topology 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-radii | pass | |
| 37-pyramid-apex-blend | pass | |
| 38-coincident-cylinders | pass | |
| 39-tee-pipe | pass | |
| 40-box-sphere-torus | pass | |
| 41-tube-radial-holes | pass | |
| 42-overlapping-patterns | pass | |
| 43-cut-splits-body | pass | |
| 44-shell-l-bracket | pass | |
| 45-shell-boss-plate | pass | |
| 46-shell-two-open-faces | pass | |
| 47-draft-curved-pocket | pass | |
| 48-pulley | pass | |
| 49-helical-sweep | pass | Fusion 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-bend | pass | area 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-offset | pass | |
| 52-circular-pattern-fillet | pass | |
| 53-path-pattern | pass | |
| 54-mirror-body-fillets | pass | |
| 55-tapped-hole-cosmetic | pass | |
| 56-holes-on-curved-face | pass | topology 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-points | pass | |
| 58-phone-stand | pass | |
| 59-enclosure-base | pass | |
| 60-pipe-flange | pass | |
| 61-shaft-keyway | pass | topology 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-sink | pass | |
| 63-hinge-knuckle | pass | known 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-chain | pass | |
| 65-sketch-gear-tooth | pass | |
| 66-sketch-linkage | pass | |
| 67-sm-base-flange | pass | |
| 68-sm-edge-flange | pass | |
| 69-sm-tray | pass | |
| 70-sm-flange-45 | pass | |
| 71-sm-custom-rule | pass | |
| 72-sm-contour-flange | pass | |
| 73-sm-hem | pass | |
| 74-sm-unfold-refold | pass | |
| 75-sm-bracket-holes | pass | |
| 76-sm-thickness-variant | pass | |
- **analytic**: closed-form geometry worked out by hand (prisms, cylinders, cones, spheres, tori and Pappus' theorems, constant-radius fillets on straight edges, sheet-metal bends as annular sectors). The scripts in [`tools/oracle-analytic/`](../tools/oracle-analytic/) compute them and give the formulas.
- **freecad &lt;version&gt;**: the same recipe built with FreeCAD's public Python scripting API by [`tools/freecad-oracle/`](../tools/freecad-oracle/), an independent kernel run as an external program. FreeCAD is not a dependency of SolidCraft. Where the two kernels legitimately build a different shape (for example the corner patch where fillets meet), only volume and area are compared, within the tolerance the case states.
## No oracle
No expected value comes from a commercial CAD product.
| Feature | Status | Tested by |
|---|---|---|
| Boss (draft, root fillet, hole, ribs) | no oracle (licence) | post, hole and rib volumes (`boss_with_hole_fillet_and_ribs`) |
| Lip / Groove | no oracle (licence) | band offsets and groove clearance on a shelled box (`lip_and_groove_on_a_shelled_box`, `groove_takes_the_rule_clearance`) |
| Snap Fit | no oracle (licence) | arm and catch volume, hook direction (`snap_fit_arm_and_catch`) |
| Rest | no 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`) |
**75 / 76 cases pass**; sources: 55 analytic, 21 freecad. 1 known failure(s) tracked in issues; 0 case(s) without expected values yet.
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.
| Case | Source | Result | Surface distance mm (max / mean) | Method | Notes |
|---|---|---|---|---|---|
| 01-box | analytic | pass | | box 40 x 30 x 20 from the origin: V = abc, A = 2(ab + bc + ca) | |
| 02-cylinder | analytic | pass | | cylinder r=15 h=50 on XY: V = pi r^2 h, A = 2 pi r^2 + 2 pi r h | |
| 03-box-through-hole | analytic | pass | | box 60x40x20 minus centred through cylinder r=6: V = abc - pi r^2 c, A = 2(ab+bc+ca) - 2 pi r^2 + 2 pi r c; hole is centred so the centroid stays at the box centre | |
| 04-revolve-vase | analytic | pass | | revolve of a line/3-point-arc profile (XZ, x = radius, z = -sketch y) 360 deg about Z. Pappus/Green: V = 2 pi * int x dA, z_c = int x z dA / int x dA, A = sum over profile edges of 2 pi int x ds (bottom disk r20, frustum r20->25, torus zone of the arc (centre (-16.000000, 33.000000), R=41.109610), frustum r15->18, top disk r18) | |
| 05-taper-extrude | analytic | pass | | square frustum: base 40 at z=0, top a1 = 40 + 2*30*tan10 at z=30 (positive taper grows outward); V = h/3 (A0 + A1 + sqrt(A0 A1)), z_c = h (A0 + 2 sqrt(A0A1) + 3 A1) / (4 (A0 + sqrt(A0A1) + A1)), sides = 4 trapezoids (a0+a1)/2 * h/cos(10) | |
| 06-symmetric-extrude | analytic | pass | | 50 (x) by 20 (z) rectangle on XZ extruded +-15 along Y: box 50x30x20 centred at origin | |
| 07-fillet-all-edges | analytic | pass | | box 40x30x20 with every edge rounded r=3 = inner box (34x24x14) grown by a ball r (Steiner): V = abc + 2r(ab+bc+ca) + pi r^2 (a+b+c) + 4/3 pi r^3, A = 2(ab+bc+ca) + 2 pi r (a+b+c) + 4 pi r^2; faces 6 planes + 12 quarter cylinders + 8 sphere octants | |
| 08-chamfer | analytic | pass | | box 40x30x20 with the 4 top edges chamfered 2: lower box 40x30x18 + top slab with section (36+2u)x(26+2u) at depth u (V, moment by exact polynomial integration); A = bottom + sides(h=18) + top 36x26 + 4 trapezoid bevels of slant 2 sqrt2 | |
| 09-shell | analytic | pass | | 50x40x30 box shelled inward 2 with the top open: box minus cavity [2,48]x[2,38]x[2,30]; A = outer bottom + outer sides + rim + cavity floor + cavity walls | |
| 10-rect-pattern-holes | analytic | pass | | plate 90x70x5 minus 12 through cylinders r=3 at (15+20i, 15+20j), i<4, j<3: V = abc - 12 pi r^2 t, A = 2(ab+bc+ca) + 12(2 pi r t - 2 pi r^2) | |
| 11-circular-pattern-flange | analytic | pass | | annulus R50/r15 h10 minus 6 through cylinders r=5 on a 40 radius: V = pi(R^2-r^2)h - 6 pi rh^2 h, A = 2 pi(R^2-r^2) + 2 pi(R+r)h + 6(2 pi rh h - 2 pi rh^2); 6-fold symmetry keeps the centroid on the axis | |
| 12-mirror-feature | analytic | pass | | block [-30,30]x[0,30]x[0,10] + two bosses r=6 h=15 at (+-15,15) on top (mirror across YZ): V = abc + 2 pi r^2 h, A = 2(ab+bc+ca) + 2 * 2 pi r h | |
| 13-combine-join | analytic | pass | | box [0,40]^2x[0,20] union cylinder r=12 at (40,20) z 0..40: V = box + half-cylinder (x>40, z<20, centroid 40 + 4r/3pi) + full cylinder z 20..40; A = bottom (1600 + pi r^2/2) + box top (1600 - pi r^2/2) + cyl top + 3 full sides + x=40 side minus 24x20 + cyl side (pi r 40 outside half + pi r 20 upper inside half) | |
| 14-combine-cut | analytic | pass | | box [-25,25]^2x[-20,0] minus sphere r=15 at the origin (lower hemisphere removed): V = abc - 2/3 pi r^3, hemisphere centroid z = -3r/8; A = 2(ab+bc+ca) - pi r^2 + 2 pi r^2 | |
| 15-combine-intersect | analytic | pass | | cube [-20,20]^3 intersect sphere R=25: sphere minus 6 disjoint caps of height h=5 (caps cannot overlap: two would need x^2+y^2 > 800 > R^2); V = 4/3 pi R^3 - 6 pi h^2 (3R-h)/3, A = 4 pi R^2 - 6 * 2 pi R h + 6 pi (R^2-20^2) | |
| 16-sweep | analytic | pass | | circle r=3 swept along line 30 (+Z) + tangent R20 quarter arc + line 30 (+X): two cylinders + quarter torus (Pappus: V = pi r^2 R theta, A = 2 pi r R theta; torus centroid at rho = (R^2 + r^2/4)/R * sin(theta/2)/(theta/2) from the bend axis along the bisector) + 2 end disks | |
| 17-loft | freecad 1.1.4 | pass | 0.013 / 0.0019 (worst at -7.9, -10.8, 31.2) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part.makeLoft | |
| 18-hole-features | analytic | pass | | plate 60x40x15 minus: counterbore (r7 x 5 deep + r4 through), countersink (90 deg cone frustum r6->r3 over 3 mm + r3 through), blind r3 hole 10 deep to the shoulder with a 118 deg cone point (height 3/tan59, centroid 1/4 up from the apex); areas: removed top/bottom disks, cylinder walls 2 pi r h, cb floor annulus, cone laterals pi r s | |
| 19-l-bracket-constrained | analytic | pass | | solved L: (0,0)-(60,0)-(60,8)-(8,8)-(8,40)-(0,40); area 60x8 + 8x32, extruded 20: V = A2 h, A = 2 A2 + perimeter*h, centroid of the two rectangles | |
| 20-slot-constrained | analytic | pass | | obround: arc centres (0,0) and (40,0), R6, tangent horizontal lines y=+-6: A2 = 2rL + pi r^2, V = A2 h, A = 2 A2 + perimeter (2L + 2 pi r) h | |
| 21-draft | analytic | pass | | 40x40x30 box, 5 deg draft hinged on the bottom (z=0), pull -Z: square frustum 40 -> 40 + 2*30*tan5; V = h/3 (A0 + A1 + sqrt(A0A1)), sides 4 trapezoids of slant h/cos5 | |
| 22-split-body | analytic | pass | | cylinder r20 z -30..30 split by the plane through the X axis tilted 30 deg (z = y tan30, stays within /z/<=11.6 inside the cylinder): 2 congruent halves (point symmetry), V total = pi r^2 h, A total = cylinder + 2 ellipses pi r (r/cos30) | |
| 23-bracket-multifeature | freecad 1.1.4 | pass | 0.007 / 0.0010 (worst at 57.3, 49.3, 25.8) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part makeFillet, Part extrude join, Part extrude cut, Part makeChamfer | |
| 24-user-parameters | analytic | pass | | width 60 x depth 40 x height 15 block from the origin with a centred d10 through hole: V = (wd - pi r^2) h, A = 2(wd - pi r^2) + 2(w+d)h + 2 pi r h | |
| 25-sketch-triangle | analytic | pass | | solved triangle (0,0), (50,0), P = t(cos60, sin60) with t = 25 + sqrt(150) from /P-(50,0)/ = 45 (the root nearest the drawn apex); V = (1/2 * 50 * Py) * 5, A = 2 A2 + perimeter*5, centroid = vertex mean | |
| 26-sketch-belt | analytic | pass | | belt: convex hull of circles R20 @ (0,0) and r10 @ (50,0) (external tangents, sin a = (R-r)/d) minus the d8 hole at (50,0), extruded 5 as one body; hull area = (R+r) sqrt(d^2-(R-r)^2) + R^2(pi+2a)/2 + r^2(pi-2a)/2; moments by Green's theorem over the line/arc boundary | |
| 27-sketch-trapezoid | analytic | pass | | solved trapezoid bottom (-30,0)-(30,0), top (-15,25)-(15,25) minus two d6 holes at (+-18, 8) (symmetric about the axis, 36 apart, 8 above l1); trapezoid centroid y = H(a+2b)/(3(a+b)); V = A2 t, A = 2 A2 + perimeter t | |
| 28-blind-pocket-fillets | freecad 1.1.4 | pass | 0.009 / 0.0015 (worst at 60.0, 15.1, 13.4) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, Part makeFillet | |
| 29-two-sided-and-partial-revolve | analytic | pass | | block [-20,20]x[-25,10]x[0,20] minus the 270 deg revolve (from +X towards +Y) of the 8x8 square r 4..12, z 12..20; the removed section is the annular sector minus the circular segment y > 10 (R^2 acos(d/R) - d sqrt(R^2-d^2), y-moment 2/3 (R^2-d^2)^1.5) that lies outside the block; sector moments (R^3-r^3)/3 (sin, 1-cos); A = box - top opening + floor - front-face notch 2 sqrt(44) x 8 + inner wall r th h + outer wall (th - pi + 2 asin(5/6)) R h + 2 end caps | |
| 30-fillet-after-shell | freecad 1.1.4 | pass | 0.008 / 0.0025 (worst at 56.3, 35.7, 3.5) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, makeThickness shell, Part makeFillet | |
| 31-fillet-l-all-edges | freecad 1.1.4 | pass | 0.009 / 0.0018 (worst at 7.9, 38.6, 18.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet | |
| 32-boss-plate-fillets | analytic | pass | | plate 60x60x8 + boss (r10, z 8..23) with R3 concave base and R2 convex top fillets taken as one solid of revolution: V, z-moment and lateral area from Pappus/Green on its (r,z) profile (quarter-circle fillet arcs); plate top loses the r13 disc | |
| 33-fillet-tangent-chain | analytic | pass | | obround (R8, centres 40 apart) x 10 minus R2 fillet spandrel R^2(1-pi/4) and 1 mm chamfer triangle swept round the whole loop (Pappus: section x path of its centroid, inset R(10-3pi)/(3(4-pi)) resp. c/3); surfaces likewise (fillet arc centroid inset R-2R/pi) | |
| 34-chamfer-then-fillet | freecad 1.1.4 | pass | 0.009 / 0.0022 (worst at 38.5, 5.6, 19.9) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeChamfer, Part makeFillet | |
| 35-fillet-curved-curved | freecad 1.1.4 | pass | 0.011 / 0.0024 (worst at -9.5, 5.3, 14.6) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part makeFillet | |
| 36-corner-blend-two-radii | freecad 1.1.4 | pass | 0.012 / 0.0026 (worst at 45.4, 39.2, 26.5) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet | |
| 37-pyramid-apex-blend | freecad 1.1.4 | pass | 0.020 / 0.0029 (worst at -0.0, 0.0, 28.4) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part.makeLoft, Part makeFillet | |
| 38-coincident-cylinders | analytic | pass | | union of r10 z0..30 and r10 z20..50 = cylinder r10 h50: V = pi r^2 h, A = 2 pi r h + 2 pi r^2 | |
| 39-tee-pipe | freecad 1.1.4 | pass | 0.008 / 0.0014 (worst at -9.7, 2.4, 40.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part extrude cut | |
| 40-box-sphere-torus | analytic | pass | | box 60x60x10 (z -10..0) + upper hemisphere r12 + spherical cap h2 below z=-10 (V = pi h^2(3R-h)/3, A = 2 pi R h) - lower half torus R22 r4 (Pappus: V = pi r^2/2 * 2 pi R, centroid -4r/3pi, A = pi r * 2 pi R); top loses disc r12 and annulus 18..26, bottom loses disc r sqrt(44) | faces: bottom, 4 sides, top split by the groove into an annulus (r 12..18) and the outer part, upper sphere, lower cap, torus |
| 41-tube-radial-holes | freecad 1.1.4 | pass | 0.010 / 0.0012 (worst at 1.9, 19.9, 24.8) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, pattern (tools transformed and re-applied) | |
| 42-overlapping-patterns | freecad 1.1.4 | pass | 0.030 / 0.0010 (worst at -20.0, -0.0, 5.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, pattern (tools transformed and re-applied), Part extrude cut, pattern (tools transformed and re-applied) | |
| 43-cut-splits-body | analytic | pass | | two boxes 35x20x20 (x 0..35 and 45..80): V = 2abc, A = 2*2(ab+bc+ca) | |
| 44-shell-l-bracket | analytic | pass | | L prism (area 736, perimeter 200) x 20 minus cavity = L shrunk by 2 (SolidCraft's intersection-join shell convention: inner faces offset by 2 and extended to meet, sharp inner corner at (6,6); area 352) from z=2 to 20. A rounded (arc) join would give V 8368.5487, A 8768.5487, 16 faces | |
| 45-shell-boss-plate | analytic | pass | | plate 60x60x10 + boss r12 h20 minus cavity (box 56x56x8 + cylinder r10 z8..28) under SolidCraft's intersection-join shell convention (offset faces extended to meet: sharp circular inner edge at r10, z8). A rounded (arc) join would give V 13620.2567, A 14104.4455, 16 faces (torus round r2) | |
| 46-shell-two-open-faces | analytic | pass | | box 50x40x30 minus cavity x3..47, y0..37, z3..30 (3 mm walls, top and front open) | |
| 47-draft-curved-pocket | freecad 1.1.4 | pass | 0.008 / 0.0019 (worst at 22.1, 21.3, 15.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude cut, PartDesign Draft | |
| 48-pulley | analytic | pass | | revolve of the 12-segment (r,z) polygon about Z: V = 2 pi int r dA, Mz = 2 pi int r z dA, A = 2 pi int r ds (Pappus) | |
| 49-helical-sweep | analytic | pass | | true helix coil (screw motion of the axial d4 section, R20, pitch 8, 3 turns): V = turns * 2 pi R * pi a^2; lateral A = turns * 2 pi * int sqrt(r^2 + (p/2pi)^2 (dr/ds)^2) ds (periodic trapezoid, exact to round-off) + two end discs; COM z = pitch*turns/2 | |
| 50-sweep-tight-bend | analytic | pass | | tube r5 along line 20 + R5 quarter bend + line 20: V = pi r^2 L, A = 2 pi r L + 2 pi r^2 (Pappus, valid while bend radius >= r; here equal so the inner side pinches to a line); bend centroid at (R^2 + r^2/4)/R * sinc(pi/4) along the bisector | |
| 51-loft-rect-circle-offset | freecad 1.1.4 | pass | 0.025 / 0.0030 (worst at -11.7, 15.0, 0.4) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part.makeLoft | |
| 52-circular-pattern-fillet | analytic | pass | | disc r40 h8 + 6 x (boss r6 h10 with R2 concave base fillet as a solid of revolution, Pappus/Green on its (r,z) profile) at radius 28; disc top loses 6 discs r8 | |
| 53-path-pattern | analytic | pass | | plate 120x60x5 minus 7 through holes d6 at arc lengths k*17.875031 along the arc through (100,15),(60,45),(20,15) (centre (60,10/3), R 125/3), translated copies | |
| 54-mirror-body-fillets | analytic | pass | | prism h10 of the 60x40 rectangle with four R5 corner rounds and two d8 holes at (+-15,20): V = A h, area = 2A + perimeter*h | |
| 55-tapped-hole-cosmetic | analytic | pass | | plate 40x30x15 minus d6.8 cylinder 12 deep (to the shoulder) and 118 deg drill point cone (h = r/tan59, V = pi r^2 h/3, lateral pi r * r/sin59); cosmetic thread adds no geometry | |
| 56-holes-on-curved-face | freecad 1.1.4 | pass | 0.009 / 0.0018 (worst at -16.9, 18.5, 0.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, hole tool (cylinder/cone) cut | |
| 57-hole-pattern-sketch-points | analytic | pass | | plate 80x50x10 minus 6 counterbored through holes (cb d10 x 5.5, hole d5.5) at x 15/40/65, y 15/35 | |
| 58-phone-stand | freecad 1.1.4 | pass | 0.007 / 0.0010 (worst at 82.5, 11.7, 6.1) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet, Part extrude cut | |
| 59-enclosure-base | freecad 1.1.4 | pass | 0.008 / 0.0017 (worst at 90.9, 51.0, 6.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part makeFillet, makeThickness shell, Part extrude join, Part extrude cut | |
| 60-pipe-flange | freecad 1.1.4 | pass | | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part revolve, Part extrude cut, pattern (tools transformed and re-applied), Part makeChamfer, Part makeFillet | No reference.stl: over 409600 bytes even at 0.02 mm deflection (11380 facets). |
| 61-shaft-keyway | freecad 1.1.4 | pass | 0.008 / 0.0019 (worst at 11.9, 3.9, 45.8) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part revolve, Part makeChamfer, Part extrude cut | |
| 62-heat-sink | analytic | pass | | base 60x50x5 + 15 fins 1.5x50x25 at x = 2 + 4k: sums of boxes; fin ends coplanar with the base ends | faces: bottom, x=0, x=60, y=0 and y=50 (each merged with the fin ends), 16 base-top strips, 15x3 fin faces |
| 63-hinge-knuckle | freecad 1.1.4 | pass | 0.008 / 0.0016 (worst at 28.0, 36.9, 4.7) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Part extrude, Part extrude join, Part extrude cut | |
| 64-sketch-tangent-arc-chain | analytic | pass | | solved sketch: 80x20 rectangle whose top is 4 tangent R12 arcs on 20 mm chords alternating up/down (net area 1600); prism h3: V = A h, area = 2A + perimeter*h, Green's theorem for centroid | |
| 65-sketch-gear-tooth | freecad 1.1.4 | pass | 0.008 / 0.0017 (worst at 6.5, 39.5, 8.0) | FreeCAD (OpenCASCADE) build of recipe.json by tools/freecad-oracle/build.py: Sketcher-solved sketch, Part extrude | |
| 66-sketch-linkage | analytic | pass | | solved sketch: hull of circles R12 at (0,0) and R8 at (60,0) (outer tangent lines, cos a = 4/60) minus holes d12, d8 (concentric) and d4 at (30,+-4) (symmetry keeps radii equal); prism h6 | |
| 67-sm-base-flange | analytic | pass | | plate 100 x 60 x t, t = 2.5 (Steel (mm) rule), thickness along +Z: V = 100*60*t, A = 2(ab+bc+ca) | |
| 68-sm-edge-flange | analytic | pass | | extrusion along x (100) of the YZ section: base y 5..60 x t; 90 deg bend annulus R 2.5..5 about (y 5, z 5); flange y 0..2.5, z 5..20. Setback (R+t)tan45 = 5 (inside bend position, outer-face height datum: outer face of flange reaches z = 20); V = L*A_sec, A = 2 A_sec + L P_sec | trim/setback and height datum are SolidCraft's documented edge-flange conventions (sheet.rs add_flanges) |
| 69-sm-tray | analytic | pass | | tray: base 90 x 50 x 2.5 (each edge trimmed by the setback 5); four 90 deg bends R 2.5..5 along the trimmed edges (90, 50, 90, 50 long); four walls 2.5 thick, z 5..25, each running on past its bend by ext = (R+t) - (t + gap/sqrt2) = 0.732233047 at both ends; open corners. A = sum of prisms - 2 x joints | corner treatment is SolidCraft's convention (sheet.rs close_corners: panels extended until their inner corners are the rule gap = t apart; no corner fill, open corner relief below the walls) |
| 70-sm-flange-45 | analytic | pass | | as 68 with a 45 deg bend: setback (R+t)tan22.5 = 2.071068, base y 2.071068..60, bend annulus 2.5..5 over 45 deg about (y 2.071068, z 5), flat leg 15 - setback = 12.928932 turned 45 deg; extrusion along x 100 | SolidCraft convention: flange height (15) is measured along the flange's outer face from the virtual sharp at the original edge (y 0, z 0), so the outer top corner is at (y, z) = (-15 cos45, 15 sin45). |
| 71-sm-custom-rule | analytic | pass | | U channel, rule t 1.5, R 3, K 0.33 (K unused folded): base y 4.5..45.5 x 1.5; two 90 deg bend annuli R 3..4.5; flanges y 0..1.5 and 48.5..50, z 4.5..20; extrusion along x 80 | the rule edit applies to the whole sheet body (base built before it is 1.5 thick too, as the recipe's edge picks at z = 1.5 state) |
| 72-sm-contour-flange | analytic | pass | | contour flange, width 50 along +Y, XZ section: plate x 0..35 z 0..2.5; bend R 2.5..5 about (35, 5) (sketch corner = outer virtual sharp, setback R+t); wall x 37.5..40 z 5..27.5; bend R 2.5..5 about (42.5, 27.5) (sketch corner = inner virtual sharp, setback R); plate x 42.5..70 z 30..32.5 | SolidCraft convention: the sketch lines are the sheet face away from the material (material right of travel); thickness grows to the material side |
| 73-sm-hem | analytic | pass | | 68's part (height 20) plus a flat hem on the flange top edge: 180 deg bend, inner radius = hem gap 0.01, outer 2.51, about (y 2.51, z 20), folded toward +y (inside); hem leg 8 - (0.01 + 2.5) = 5.49: y 2.52..5.02, z 14.51..20; extrusion along x 100 | SolidCraft conventions: hem gap 0.01 mm (rule hem_gap), hem length measured to the outer face of the hem bend (top at z 22.51), hem folds over the flange's inner face. |
| 74-sm-unfold-refold | analytic | pass | | U channel 80x50 (t 2.5, R 2.5, base y 5..45, flanges z 5..20) with the flat-pattern slot x 35..45, y -12..10 refolded: BA = (pi/2)(R + K t) = 5.654866776, so the slot removes base y 5..10, the whole y=0 bend and the flange from z 5 to 5 + (12 - (BA - 5)) = 16.345133224; x-segments 0..35 / 35..45 / 45..80 | unfold/refold maps flat distance through the K-factor neutral layer (K 0.44) |
| 75-sm-bracket-holes | analytic | known failure (#296) | | base x 0..75, y 5..40, z 0..2.5 (trimmed 5 by each flange's setback); bend 1 (R 2.5..5 about y 5, z 5) x 0..75; flange 1 y 0..2.5, z 5..30, x 0..80; bend 2 (R 2.5..5 about x 75, z 5) y 5..40; flange 2 x 77.5..80, z 5..15, y 5..40; minus two d6 through holes at (25,20), (55,20) in the base. A = sum of prism areas - 2 x 4 joint areas (75t, 75t, 35t, 35t) + hole walls - hole discs | step 14 (solid.extrude): invalid input: sew: nonmanifold edge contact; SolidCraft convention: the second flange trims the base under the first bend, so bend 1 stops at x 75 while flange 1's panel runs to x 80 (open corner relief, no fill). |
| 76-sm-thickness-variant | analytic | pass | | final rule thickness 3 (bend radius = Thickness = 3): base y 6..60 x 3; bend annulus R 3..6 about (y 6, z 6); flange y 0..3, z 6..20; extrusion along x 100 | the rule's thickness edits regenerate the whole sheet body; only the final 3 mm state matters |
## Not covered by a reference case
| Feature | Tested by |
|---|---|
| Boss (draft, root fillet, hole, ribs) | post, hole and rib volumes (`boss_with_hole_fillet_and_ribs`) |
| Lip / Groove | band offsets and groove clearance on a shelled box (`lip_and_groove_on_a_shelled_box`, `groove_takes_the_rule_clearance`) |
| Snap Fit | arm and catch volume, hook direction (`snap_fit_arm_and_catch`) |
| Rest | rectangle, round, hollow and drafted pads (`rest_pads`) |
| Plastic Rules (manage, assign) | library values, Thickness-based values, rule draft as the boss default (`plastic_rules_library_edit_and_assign`) |
+1 -1
View File
@@ -1,6 +1,6 @@
# Kernel robustness
Real users hit edge cases the Fusion oracle (docs/oracle.md) never sees: faces that coincide
Real users hit edge cases the reference tests (docs/oracle.md) never see: faces that coincide
by accident, tangencies, seams lying where another body cuts, walls thinner than a shell. The
robustness runs build random but realistic designs and check every step.
-85
View File
@@ -18,88 +18,3 @@ display on the resulting body.
## 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.**
| Case | Result | Bodies | Volume mm³ (got / Fusion) | Area mm² (got / Fusion) | Faces (got / Fusion) | ms | Notes |
|---|---|---|---|---|---|---|---|
| 01-box | pass | Box | 24000.000 / 24000.000 | 5200.000 / 5200.000 | 6 / 6 | 3 | |
| 02-cylinder | pass | Cylinder | 35339.429 / 35342.917 | 6125.845 / 6126.106 | 3 / 3 | 87 | |
| 03-box-through-hole | pass | Box | 45738.283 / 45738.053 | 9327.809 / 9327.788 | 7 / 7 | 62 | |
| 04-revolve-vase | pass | Vase | 112488.847 / 112499.479 | 13156.093 / 13156.615 | 5 / 5 | 511 | |
| 05-taper-extrude | pass | Frustum | 61814.826 / 61814.826 | 9676.915 / 9676.915 | 6 / 6 | 2 | |
| 06-symmetric-extrude | pass | Block | 30000.000 / 30000.000 | 6200.000 / 6200.000 | 6 / 6 | 1 | |
| 07-fillet-all-edges | pass | Box | 23340.621 / 23340.849 | 4726.220 / 4726.265 | 26 / 26 | 532 | |
| 08-chamfer | pass | Box | 23730.667 / 23730.667 | 5029.352 / 5029.352 | 10 / 10 | 3 | |
| 09-shell | pass | Box | 13632.000 / 13632.000 | 13992.000 / 13992.000 | 11 / 11 | 2 | |
| 10-rect-pattern-holes | pass | Plate | 29803.713 / 29803.540 | 14652.481 / 14652.389 | 18 / 18 | 252 | |
| 11-circular-pattern-flange | pass | Flange | 66758.144 / 66758.844 | 19320.617 / 19320.795 | 10 / 10 | 168 | |
| 12-mirror-feature | pass | Base | 21392.575 / 21392.920 | 6530.944 / 6530.973 | 10 / 10 | 80 | |
| 13-combine-join | pass | A | 45570.295 / 45571.680 | 8634.196 / 8634.336 | 9 / 9 | 43 | |
| 14-combine-cut | pass | A | 42933.034 / 42931.417 | 9706.783 / 9706.858 | 7 / 7 | 402 | |
| 15-combine-intersect | pass | A | 54452.172 / 54454.273 | 7382.532 / 7382.743 | 7 / 7 | 1022 | |
| 16-sweep | pass | Tube | 2584.428 / 2584.724 | 1779.638 / 1779.698 | 5 / 5 | 612 | |
| 17-loft | pass | Loft | 35121.595 / 35125.498 | 6495.373 / 6495.000 | 6 / 6 | 191 | |
| 18-hole-features | pass | Plate | 33890.908 / 33890.710 | 8568.695 / 8568.696 | 13 / 13 | 184 | |
| 19-l-bracket-constrained | pass | LBracket | 14720.000 / 14720.000 | 5472.000 / 5472.000 | 8 / 8 | 2 | |
| 20-slot-constrained | pass | Slot | 2965.428 / 2965.487 | 1774.644 / 1774.690 | 6 / 6 | 15 | |
| 21-draft | pass | Box | 54574.737 / 54574.737 | 8781.998 / 8781.998 | 6 / 6 | 1 | |
| 22-split-body | pass | Cyl, Cyl (1) | 75390.813 / 75398.224 | 12954.440 / 12955.175 | 6 / 6 | 109 | |
| 23-bracket-multifeature | pass | Bracket | 59331.477 / 59331.129 | 18290.767 / 18290.737 | 19 / 19 | 117 | |
| 24-user-parameters | pass | Block | 34822.022 / 34821.903 | 8114.175 / 8114.159 | 7 / 7 | 58 | |
| 25-sketch-triangle | pass | Triangle | 4032.155 / 4032.155 | 2274.099 / 2274.099 | 5 / 5 | 1 | |
| 26-sketch-belt | pass | Belt | 11326.042 / 11326.170 | 5637.347 / 5637.404 | 7 / 7 | 42 | |
| 27-sketch-trapezoid | pass | Trapezoid | 5342.286 / 5342.257 | 3066.961 / 3066.946 | 8 / 8 | 53 | |
| 28-blind-pocket-fillets | pass | Block | 84299.345 / 84299.249 | 16279.560 / 16279.572 | 23 / 23 | 320 | |
| 29-two-sided-and-partial-revolve | pass | Block | 25731.356 / 25731.307 | 6312.589 / 6312.602 | 12 / 12 | 26 | |
| 30-fillet-after-shell | pass | Box | 21745.915 / 21745.876 | 14968.284 / 14968.332 | 27 / 27 | 464 | |
| 31-fillet-l-all-edges | pass | L | 14321.483 / 14321.626 | 5024.004 / 5024.045 | 38 / 38 | 747 | |
| 32-boss-plate-fillets | pass | Part | 33590.273 / 33590.350 | 9929.854 / 9929.864 | 10 / 10 | 1214 | |
| 33-fillet-tangent-chain | pass | Slot | 8237.037 / 8237.122 | 2793.541 / 2793.569 | 14 / 14 | 555 | |
| 34-chamfer-then-fillet | pass | Box | 22892.383 / 22892.421 | 4753.677 / 4753.688 | 18 / 18 | 54 | |
| 35-fillet-curved-curved | pass | Tee | 63169.685 / 63171.443 | 10269.168 / 10269.348 | 6 / 6 | 904 | |
| 36-corner-blend-two-radii | pass | Box | 57799.504 / 57800.240 | 8585.437 / 8585.524 | 18 / 18 | 519 | |
| 37-pyramid-apex-blend | pass | Pyramid | 15465.376 / 15465.575 | 3914.529 / 3914.581 | 18 / 18 | 312 | |
| 38-coincident-cylinders | pass | A | 15706.406 / 15707.963 | 3769.760 / 3769.911 | 3 / 3 | 67 | |
| 39-tee-pipe | pass | Tee | 24095.763 / 24096.237 | 16709.772 / 16710.048 | 7 / 7 | 182 | |
| 40-box-sphere-torus | pass | A | 36287.558 / 36287.433 | 10696.143 / 10696.165 | 10 / 10 | 2393 | |
| 41-tube-radial-holes | pass | Tube | 20243.046 / 20243.981 | 14873.689 / 14873.773 | 22 / 22 | 1409 | |
| 42-overlapping-patterns | pass | Plate | 38488.411 / 38487.064 | 21687.215 / 21686.603 | 43 / 43 | 533 | solid 'Plate': imported as an open body (the shell is not closed) |
| 43-cut-splits-body | pass | Bar, Bar (1) | 28000.000 / 28000.000 | 7200.000 / 7200.000 | 12 / 12 | 5 | |
| 44-shell-l-bracket | pass | L | 8384.000 / 8384.000 | 8784.000 / 8784.000 | 15 / 15 | 3 | |
| 45-shell-boss-plate | pass | Part | 13676.354 / 13676.602 | 14156.531 / 14156.602 | 15 / 15 | 69 | |
| 46-shell-two-open-faces | pass | Box | 16044.000 / 16044.000 | 11398.000 / 11398.000 | 10 / 10 | 3 | |
| 47-draft-curved-pocket | pass | Block | 111387.998 / 111387.712 | 17700.336 / 17700.361 | 11 / 11 | 27 | |
| 48-pulley | pass | Pulley | 168508.160 / 168510.841 | 23718.215 / 23718.644 | 12 / 12 | 321 | |
| 49-helical-sweep | pass | Spring | 4733.931 / 4738.099 | 4765.903 / 4767.652 | 3 / 3 | 10552 | solid 'Spring': face wrapping around a closed surface kept whole: split line does not reach the rings |
| 50-sweep-tight-bend | pass | Bend | 3758.025 / 3758.443 | – / – | 5 / 5 | 405 | |
| 51-loft-rect-circle-offset | pass | Loft | 61129.957 / 61133.116 | 9412.473 / 9411.655 | 6 / 6 | 251 | |
| 52-circular-pattern-fillet | pass | Hub | 47206.182 / 47206.850 | 14141.988 / 14142.171 | 21 / 21 | 820 | |
| 53-path-pattern | pass | Plate | 35010.499 / 35010.398 | 16463.947 / 16463.894 | 13 / 13 | 130 | |
| 54-mirror-body-fillets | pass | Bracket | 22780.113 / 22780.089 | 6972.831 / 6972.832 | 12 / 12 | 98 | |
| 55-tapped-hole-cosmetic | pass | Plate | 17542.360 / 17542.314 | 4761.590 / 4761.596 | 8 / 8 | 84 | |
| 56-holes-on-curved-face | pass | Cyl | 77941.171 / 77942.388 | 10592.951 / 10593.139 | 7 / 7 | 378 | |
| 57-hole-pattern-sketch-points | pass | Plate | 36767.029 / 36766.712 | 11818.165 / 11818.153 | 24 / 24 | 197 | |
| 58-phone-stand | pass | Stand | 74559.442 / 74559.177 | 29148.741 / 29148.785 | 19 / 19 | 24 | |
| 59-enclosure-base | pass | Enclosure | 34309.038 / 34309.486 | 33795.405 / 33795.610 | 39 / 39 | 286 | |
| 60-pipe-flange | pass | Flange | 323661.027 / 323660.979 | 56984.270 / 56984.564 | 16 / 16 | 993 | |
| 61-shaft-keyway | pass | Shaft | 47521.504 / 47525.946 | 9676.781 / 9677.096 | 14 / 14 | 182 | |
| 62-heat-sink | pass | HeatSink | 43125.000 / 43125.000 | 45725.000 / 45725.000 | 66 / 66 | 8 | |
| 63-hinge-knuckle | pass | Hinge | 4783.029 / 4783.117 | 4110.255 / 4110.287 | 14 / 14 | 118 | |
| 64-sketch-tangent-arc-chain | pass | Wave | 4800.000 / 4800.000 | 3843.701 / 3843.712 | 9 / 9 | 10 | |
| 65-sketch-gear-tooth | pass | Tooth | 3987.831 / 3988.084 | 1945.416 / 1945.485 | 9 / 9 | 8 | |
| 66-sketch-linkage | pass | Link | 8045.410 / 8045.386 | 4308.172 / 4308.175 | 10 / 10 | 129 | |
| 67-sm-base-flange | pass | Body1 | 15000.000 / 15000.000 | 12800.000 / 12800.000 | 6 / 6 | 2 | |
| 68-sm-edge-flange | pass | Body1 | 18972.476 / 18972.622 | 16057.518 / 16057.550 | 14 / 14 | 26 | |
| 69-sm-tray | pass | Body1 | 29665.828 / 29666.234 | 24979.993 / 24980.086 | 46 / 46 | 66 | |
| 70-sm-flange-45 | pass | Body1 | 18450.704 / 18450.777 | 15629.621 / 15629.637 | 14 / 14 | 14 | |
| 71-sm-custom-rule | pass | Body1 | 10053.579 / 10053.717 | 13896.249 / 13896.299 | 22 / 22 | 38 | |
| 72-sm-contour-flange | pass | Body1 | 12097.478 / 12097.622 | 10411.968 / 10412.002 | 22 / 22 | 36 | |
| 73-sm-hem | pass | Body1 | 21334.478 / 21334.723 | 17994.397 / 17994.473 | 22 / 22 | 60 | |
| 74-sm-unfold-refold | pass | Body1 | 15800.086 / 15800.304 | 13610.275 / 13610.326 | 32 / 32 | 84 | |
| 75-sm-bracket-holes | pass | Body1 | 13915.866 / 13916.012 | 12035.934 / 12035.962 | 25 / 25 | 100 | |
| 76-sm-thickness-variant | pass | Body1 | 22520.368 / 22520.575 | 16064.089 / 16064.128 | 14 / 14 | 31 | |