docs: the meshing skill's results on the corpus and samples (#482)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-16 14:57:51 -04:00
1 parent a5c71bc5b0
commit 59a02b0172
30 files changed
+134

No files matched your search

+133
View File
@@ -0,0 +1,133 @@
# The meshing skill on the corpus and the samples
Generated by `cargo xtask skill-results`; do not edit. Each part is meshed by following
[the meshing skill](../../skills/fieldcraft-meshing/SKILL.md) with today's commands, headless, as
an agent over MCP would (`crates/testkit/src/skill.rs`): `geometry.bodies`, `geometry.features`,
`mesh.plan` and `geometry.check` at the plan's size, `mesh.plan.apply` (which meshes one cell of a
mirror-symmetric body and mirrors it), and where the plan fails the skill's fallbacks in order,
`mesh.hex.generate`, `mesh.hex.project` (sphere template) and then `mesh.tet.generate` (tet10);
the cells of a split part are joined with `mesh.node.merge`; then `mesh.quality.mark` at the
skill's limits (scaled Jacobian ≥ 0.3, edge ratio ≤ 10), `mesh.node.relax` round each marked
element when at most 20 are marked (more is a clustered failure: the blocks are wrong, and relaxing
is not the fix), `mesh.quality.mark` again, `mesh.quality` and `mesh.check.duplicates`. Sizes are
the plan's (1/25 of the body's diagonal, 2 layers through a shell).
Columns: `method` is what meshed the part (`plan (project|cells|sweep)` is the skill's target,
anything else a fallback); `symmetry` the mirror planes `mesh.plan.apply` used; `volume error` the
mesh's volume against the geometry's; `untagged faces` B-rep faces no element face is tagged
with (a set on them finds nothing); `marked` the elements failing the limits before and after
relaxing, and what relaxing changed; ¹ a tet mesh has no edge ratio: its largest aspect ratio
stands in; `duplicates` coincident nodes and cracks; `geometry.check` its findings at the size;
`verdict` pass when the minimum scaled Jacobian and the largest edge ratio meet the limits and
the volume is within 2 % (`pass (fallback)` when a fallback meshed it).
The section pictures cut each part through the middle of its longest side, marked elements red.
Times are wall clock on the machine that ran it.
## Results
| part | method | symmetry | elements | min SJ | mean SJ | max edge ratio¹ | volume error | untagged faces | marked → after relax | duplicates | geometry.check | time s | verdict | section |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| bent-tube | plan (sweep) | – | 192 (192 hex8) | 0.861 | 0.865 | 5.0 | -1.7e-1 | 0 | 0 (not relaxed) | none | 8 near_faces, 2 sliver_face | 1.9 | fail (volume) | [y=37.500](skill/bent-tube.png) |
| bolted-assembly | plan (sweep), mesh.hex.generate (auto) | – | 6272 (6272 hex8) | 0.583 | 0.939 | 3.6 | 2.0e-4 | 0 | 0 (not relaxed) | 8 coincident, 0 cracks | – | 3.9 | pass | [x=50.000](skill/bolted-assembly.png) |
| cantilever-i-beam | plan (sweep) | – | 61600 (61600 hex8) | 1.000 | 1.000 | 1.0 | 0.0e0 | 0 | 0 (not relaxed) | none | – | 5.7 | pass | [x=100.000](skill/cantilever-i-beam.png) |
| cross-cylinders | none | – | – | – | – | – | – | – | – | – | – | 18.3 | no mesh | – |
| filleted-bracket | tet10 | – | 19557 (19557 tet10) | 0.042 | 0.519 | 9.1 | -6.7e-6 | 0 | 1709 (not relaxed) | none | – | 35.5 | fail (SJ) | [x=30.000](skill/filleted-bracket.png) |
| filleted-bracket-blocks | plan (sweep) | – | 75410 (75410 hex8) | 0.704 | 0.954 | 3.5 | 3.7e-5 | 0 | 0 (not relaxed) | none (after merging 2348 nodes) | – | 16.7 | pass | [x=30.000](skill/filleted-bracket-blocks.png) |
| i-beam | plan (sweep) | – | 61600 (61600 hex8) | 1.000 | 1.000 | 1.0 | 0.0e0 | 0 | 0 (not relaxed) | none | – | 9.3 | pass | [z=100.000](skill/i-beam.png) |
| loft-transition | plan (sweep) | – | 2873 (2873 hex8) | 0.189 | 0.960 | 2.2 | -2.1e-3 | 0 | 4 → 4 (4 relaxed, 0 undone; min SJ 0.189 → 0.189, max ratio 1.7 → 2.2) | none | – | 2.3 | fail (SJ) | [z=25.000](skill/loft-transition.png) |
| perforated-plate | plan (project) | 2 plane(s) | 8576 (8576 hex8) | 0.707 | 0.888 | 3.0 | 7.7e-4 | 0 | 0 (not relaxed) | none | – | 33.5 | pass | [x=60.000](skill/perforated-plate.png) |
| pinhole-block | plan (project) | 2 plane(s) | 5068 (5068 hex8) | 0.707 | 0.942 | 18.4 | 8.1e-5 | 0 | 1680 (not relaxed) | none | 32 sliver_face | 24.0 | fail (ratio) | [y=25.000](skill/pinhole-block.png) |
| rounded-box | plan (project) | 2 plane(s) | 9260 (9260 hex8) | 0.734 | 0.985 | 2.2 | -4.1e-3 | 0 | 0 (not relaxed) | none | – | 10.3 | pass | [x=20.000](skill/rounded-box.png) |
| shallow-groove | plan (sweep) | – | 1960 (1960 hex8) | 0.991 | 0.999 | 1.1 | 2.7e-4 | 0 | 0 (not relaxed) | none | – | 0.2 | pass | [x=30.000](skill/shallow-groove.png) |
| sharp-wedge | plan (sweep) | – | 1674 (1674 hex8) | 0.014 | 0.301 | 149.6 | 2.7e-15 | 0 | 1524 (not relaxed) | none | – | 0.2 | fail (SJ, ratio) | [x=40.000](skill/sharp-wedge.png) |
| sphere-on-plate | tet10 | – | 15073 (15073 tet10) | 0.121 | 0.558 | 6.2 | -1.1e-4 | 0 | 662 (not relaxed) | none | – | 5.0 | fail (SJ) | [y=0.000](skill/sphere-on-plate.png) |
| spline-vase | plan (sweep) | – | 4536 (4536 hex8) | 0.665 | 0.945 | 9.9 | -6.6e-3 | 0 | 0 (not relaxed) | none | – | 0.8 | pass | [z=-35.000](skill/spline-vase.png) |
| stepped-block | plan (cells) | – | 1416 (1416 hex8) | 1.000 | 1.000 | 1.3 | -4.8e-15 | 0 | 0 (not relaxed) | none | – | 0.5 | pass | [x=30.000](skill/stepped-block.png) |
| thin-ribs | plan (sweep) | – | 301308 (301308 hex8) | 0.999 | 1.000 | 1.3 | -3.4e-12 | 0 | 0 (not relaxed) | none | – | 34.0 | pass | [x=50.000](skill/thin-ribs.png) |
| thin-wall-enclosure | plan (sweep) | – | 124512 (124512 hex8) | 1.000 | 1.000 | 1.0 | 0.0e0 | 0 | 0 (not relaxed) | none | – | 21.8 | pass | [x=40.000](skill/thin-wall-enclosure.png) |
| analytic-sphere | mesh.hex.project (sphere) | – | 864 (864 hex8) | 0.702 | 0.929 | 2.7 | -3.0e-2 | 0 | 0 (not relaxed) | none | – | 2.5 | fail (volume) | [z=0.000](skill/analytic-sphere.png) |
| analytic-torus | plan (sweep) | – | 1080 (1080 hex8) | 0.834 | 0.909 | 2.4 | -4.7e-2 | 0 | 0 (not relaxed) | none | – | 1.0 | fail (volume) | [x=0.000](skill/analytic-torus.png) |
| analytic-cylinder | plan (project) | – | 1700 (1700 hex8) | 0.845 | 0.970 | 1.9 | -1.6e-2 | 0 | 0 (not relaxed) | none | – | 1.4 | pass | [z=20.000](skill/analytic-cylinder.png) |
| analytic-pipe | plan (project) | – | 1008 (1008 hex8) | 0.996 | 0.996 | 1.4 | -5.0e-3 | 0 | 0 (not relaxed) | none | – | 1.0 | pass | [z=30.000](skill/analytic-pipe.png) |
| analytic-plate-hole | plan (project) | – | 5856 (5856 hex8) | 0.937 | 0.996 | 3.0 | 4.3e-5 | 0 | 0 (not relaxed) | none | – | 5.8 | pass | [x=100.000](skill/analytic-plate-hole.png) |
| crank-arm | plan (project) | – | 1372 (1372 hex8) | 0.544 | 0.860 | 10.5 | -3.3e-3 | 0 | 9 (9 relaxations undone: worse) | none | 16 short_edge, 9 sliver_face | 48.2 | fail (ratio) | [x=81.000](skill/crank-arm.png) |
| upright-bracket | plan (sweep) | – | 1364 (1364 hex8) | 0.707 | 0.968 | 3.1 | 2.1e-4 | 0 | 0 (not relaxed) | none | – | 0.2 | pass | [x=30.000](skill/upright-bracket.png) |
| pi5-base | plan (project) | 1 plane(s) | 29852 (29852 hex8) | 0.683 | 0.969 | 4.9 | -3.0e-3 | 0 | 0 (not relaxed) | none | 8 short_edge, 4 sliver_face | 197.5 | pass | [x=56.000](skill/pi5-base.png) |
| pi5-lid | plan (project) | – | 5130 (5130 hex8) | 0.724 | 0.868 | 3.9 | -2.9e-4 | 0 | 0 (not relaxed) | none | – | 10.7 | pass | [x=56.000](skill/pi5-lid.png) |
| cup | plan (project) | – | 1114 (1114 hex8) | 0.701 | 0.921 | 5.0 | -4.5e-3 | 0 | 0 (not relaxed) | none | – | 3.0 | pass | [x=5.000](skill/cup.png) |
| plain-cup | mesh.hex.generate (auto) | – | 2000 (2000 hex8) | 0.820 | 0.995 | 4.7 | -2.3e-3 | 0 | 0 (not relaxed) | none | – | 0.9 | pass (fallback) | [x=0.000](skill/plain-cup.png) |
## Why a part fails
Each earlier method's error, in the order the skill tries them, and for a part that fails the
limits the cause read from its numbers and section picture.
- `bent-tube` (corpus: A 10 mm tube with a 1 mm wall, bent through R30: a thin-walled sweep along a line-arc-line path.):
- **fail (volume)**: the plan's default size (1/25 of the diagonal) puts six elements round the Ø10 tube: the swept section is a hexagon and loses 17 % of the volume. Size from the tube's radius (§5), not the part's diagonal.
- `bolted-assembly` (corpus: A base plate with a clamp block component placed twice, once turned 90 degrees: bodies placed by component transforms.):
- Clamp body: mesh.plan.apply: mesh plan `Plan-Clamp_body`: the projected layout does not mesh: invalid input: node 3076 at [11.000000000000004, 10.0, 5.0] lies on the edge between nodes 2640 and 2715: new elements must meet the mesh face to face, with the same divisions…
- Clamp body (Clamp:2): mesh.plan.apply: mesh plan `Plan-Clamp_body__Clamp_2_`: the projected layout does not mesh: invalid input: node 5884 at [66.42857142857143, 5.000000000000001, 5.0] lies on the edge between nodes 984 and 1059: new elements must meet the mesh face to face, wi…
- `cross-cylinders` (corpus: Two orthogonal R10 cylinders joined: equal radii, so the intersection curves cross at two singular points.):
- Cross: mesh.plan.apply: mesh plan `Plan-Cross`: the cells ["Cross"] (cuts []) do not mesh all-hex: body `Cross`: invalid boundary surface: 28 defects; triangles 142 and 254 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7…
- Cross: mesh.hex.generate: body `Cross`: invalid boundary surface: 28 defects; triangles 146 and 258 are oriented inconsistently across edge 0–402 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 255 and 258 are oriented inconsistently across…
- Cross: mesh.hex.project (sphere): body `Cross`: invalid boundary surface: 28 defects; triangles 146 and 258 are oriented inconsistently across edge 0–402 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 255 and 258 are oriented inconsistently across…
- Cross: mesh.tet.generate: invalid boundary surface: 28 defects; triangles 146 and 258 are oriented inconsistently across edge 0–402 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 255 and 258 are oriented inconsistently across edge 0–565 at…
- **no mesh**: the boundary tessellation of the joined cylinders is invalid (inconsistent orientation at the two singular points), so every mesher refuses the body.
- `filleted-bracket` (corpus: An 8 mm L-bracket with an R6 inside fillet and a hole through each leg: a blend beside holes.):
- Bracket: mesh.plan.apply: mesh plan `Plan-Bracket`: the cells ["Bracket"] (cuts []) do not mesh all-hex: body `Bracket`: not supported yet: no structured hex method fits this body (extrude: cylindrical face 8 is not parallel to the direction; sweep: from face 1: lin…
- Bracket: mesh.hex.generate: body `Bracket`: not supported yet: no structured hex method fits this body (extrude: cylindrical face 8 is not parallel to the direction; sweep: from face 1: linking face 6 has 2 boundary loops (periodic or holed linking faces are not swept…
- Bracket: mesh.hex.project (sphere): body `Bracket`: meshing failed: 120 of 5104 elements are inverted, twisted or degenerate after projection (certified Jacobian; smallest corner scaled Jacobian -0.760): block 1 cell [4, 0, 0] near (20.373, 3.413, 3.606); block 1 cell [5, 0, …
- **fail (SJ)**: no structured method takes the whole body (the plan has no cuts for the fillet beside the holes), and tet10 at the plan's size leaves slivers (min SJ 0.04, 1709 marked). The same part partitioned into mapped cells (`filleted-bracket-blocks`) passes: partition (§3.4).
- `loft-transition` (corpus: A 40 mm square lofted to an R15 circle 50 mm above: a ruled free-form surface between unlike sections.):
- **fail (SJ)**: the lofted sides are swept one to one, and the elements where the section changes shape are skewed (min SJ 0.19). Relaxing the four marked elements did not raise it: the blocks are wrong, not the nodes (§6.3). A loft has no template yet (`unmapped`).
- `pinhole-block` (corpus: A 50 mm block drilled through by sixteen 0.6 mm pinholes: features 80 times smaller than the part, genus 16.):
- **fail (ratio)**: the plan's cuts run beside the small holes' frames and leave strips of thin elements along the cut planes (red in the picture, edge ratio 18): 1680 marked, a clustered failure. §4: offset a cut one element out of the feature.
- `sharp-wedge` (corpus: A wedge whose faces meet at under 3 degrees: a sharp edge with a needle-thin cross-section.):
- **fail (SJ, ratio)**: the wedge's sharp edge is swept as hex, so every element tapers to it (min SJ 0.01, edge ratio 150). §4: a column of wedge6 at the edge; the plan has no wedge template.
- `sphere-on-plate` (corpus: A sphere sunk 0.1 mm into a plate: the faces meet at under 6 degrees around a 2 mm circle.):
- Plate: mesh.plan.apply: mesh plan `Plan-Plate`: the cells ["Plate"] (cuts []) do not mesh all-hex: body `Plate`: not supported yet: no structured hex method fits this body (extrude: face 6 is curved across the direction; sweep: from face 0: 6 faces are not on the …
- Plate: mesh.hex.generate: body `Plate`: not supported yet: no structured hex method fits this body (extrude: face 6 is curved across the direction; sweep: from face 0: 6 faces are not on the sweep path; from face 5: linking face 11 has 2 boundary loops (periodic or …
- Plate: mesh.hex.project (sphere): body `Plate`: meshing failed: the mesh volume 32014.728344815157 is far from the body's 69353.30815403847: the blocks do not cover the part (every outer block face must stand for part of its surface)
- **fail (SJ)**: no structured method takes the plate with the sunk sphere, and tet10 leaves slivers (min SJ 0.12, 662 marked).
- `analytic-sphere` (corpus: Sphere r 20: curvature everywhere, no edges.):
- Sphere: mesh.plan.apply: mesh plan `Plan-Sphere`: the cells ["Sphere"] (cuts []) do not mesh all-hex: body `Sphere`: not supported yet: no structured hex method fits this body (extrude: no direction to try; sweep: no planar face to sweep from; revolve: no axis to t…
- Sphere: mesh.hex.generate: body `Sphere`: not supported yet: no structured hex method fits this body (extrude: no direction to try; sweep: no planar face to sweep from; revolve: no axis to try; mapped: meshing failed: meshing failed: mesh validity gate: 8 inverted or…
- **fail (volume)**: the sphere template meshes it cleanly, but hex8 faces chord the sphere at the plan's size and the mesh is 3 % short of its volume: refine, or hex20 (`order: 2`).
- `analytic-torus` (corpus: Torus 30/8: genus 1 with doubly curved faces and no edges.):
- **fail (volume)**: the revolve is clean, but hex8 faces chord the torus at the plan's size and the mesh is 4.7 % short of its volume: refine, or hex20.
- `crank-arm` (sample `crank-arm`: the 170 mm bicycle crank, mirror symmetric across y = 0):
- **fail (ratio)**: the edge ratio is 10.5 in the nine elements at the square bore's R1.5 corners, just over the limit. Relaxing each raised its region's edge ratio, so each was undone (§6.3). `mesh.plan.apply` did not find the y = 0 symmetry plane; the sample meshes the partitioned half and mirrors it.
- `plain-cup` (the cup without its handle (#418)):
- Cup: mesh.plan.apply: mesh plan `Plan-Cup`: the projected layout does not mesh: body `Cup`: meshing failed: the mesh volume 4994.312243612882 is far from the body's 16760.91944296662: the blocks do not cover the part (every outer block face must stand for part o…
## Parts
- `bent-tube`: corpus: A 10 mm tube with a 1 mm wall, bent through R30: a thin-walled sweep along a line-arc-line path.
- `bolted-assembly`: corpus: A base plate with a clamp block component placed twice, once turned 90 degrees: bodies placed by component transforms.
- `cantilever-i-beam`: corpus: The 200 mm I-beam of `i-beam` lying along X with its web upright (Z up): a sweep along a horizontal axis, and the cantilever of the README demo.
- `cross-cylinders`: corpus: Two orthogonal R10 cylinders joined: equal radii, so the intersection curves cross at two singular points.
- `filleted-bracket`: corpus: An 8 mm L-bracket with an R6 inside fillet and a hole through each leg: a blend beside holes.
- `filleted-bracket-blocks`: corpus: The filleted bracket split into 30 cells with SolidCraft's Split Body (three slabs across its width, the corner halved along its diagonal, an O-grid of four cells round each hole) so that every cell is a mapped block: conforming multi-block hex meshing.
- `i-beam`: corpus: A 200 mm I-beam with 3 mm flanges and a 2 mm web: a long sweepable prism with thin walls.
- `loft-transition`: corpus: A 40 mm square lofted to an R15 circle 50 mm above: a ruled free-form surface between unlike sections.
- `perforated-plate`: corpus: A 3 mm plate perforated by a 6 x 4 grid of 6 mm holes: genus 24 in a thin sheet.
- `pinhole-block`: corpus: A 50 mm block drilled through by sixteen 0.6 mm pinholes: features 80 times smaller than the part, genus 16.
- `rounded-box`: corpus: A 40 x 30 x 20 box with every edge rounded R3: cylinder and sphere blends meeting at the corners.
- `shallow-groove`: corpus: A block with a 0.5 mm deep groove cut by an R30 cylinder: groove walls 10 degrees off the top face.
- `sharp-wedge`: corpus: A wedge whose faces meet at under 3 degrees: a sharp edge with a needle-thin cross-section.
- `sphere-on-plate`: corpus: A sphere sunk 0.1 mm into a plate: the faces meet at under 6 degrees around a 2 mm circle.
- `spline-vase`: corpus: A vase revolved from a spline profile: doubly curved faces of varying curvature around a closed axis.
- `stepped-block`: corpus: Three stacked blocks with a slot cut through the base: axis-aligned steps for polycube and octree meshers.
- `thin-ribs`: corpus: A 4 mm plate carrying five 0.8 mm ribs 20 mm tall: thin walls standing on a thicker base.
- `thin-wall-enclosure`: corpus: An 80 x 60 x 40 box shelled to 1 mm walls, open on top: thin walls over a large span.
- `analytic-sphere`: corpus: Sphere r 20: curvature everywhere, no edges.
- `analytic-torus`: corpus: Torus 30/8: genus 1 with doubly curved faces and no edges.
- `analytic-cylinder`: corpus: Cylinder r 10, h 40: the round bar of beam and torsion checks.
- `analytic-pipe`: corpus: Thick-walled pipe 20/30, length 60: the Lamé cylinder.
- `analytic-plate-hole`: corpus: Plate 200 x 100 x 5 with a 20 mm hole: the Kirsch plate.
- `crank-arm`: sample `crank-arm`: the 170 mm bicycle crank, mirror symmetric across y = 0
- `upright-bracket`: samples `drop-test`, `flow-past`: the upright L-bracket
- `pi5-base`: the Raspberry Pi 5 enclosure's base: a shelled box with fillets and standoffs
- `pi5-lid`: the Raspberry Pi 5 enclosure's lid
- `cup`: the cup with a handle of #418
- `plain-cup`: the cup without its handle (#418)
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB