Add mesh_curvature: Rusinkiewicz per-vertex curvature (Phase 3 mesh-analysis expansion)#110
Merged
Conversation
… mesh-analysis expansion) Ships the one Phase 3 tool unblocked today: OCCTSwiftMesh 1.5.0 (already pinned) shipped Mesh.vertexCurvatures() in v1.4.0 but OCCTMCP hadn't consumed it yet. mesh_curvature welds a body's mesh internally (vertexCurvatures' own precondition), computes k1/k2/mean/gaussian stats plus flatFraction and a clamp-relative highCurvatureFraction, and renders a colored PNG reusing the band-group + colorbar trick HeatmapTools/MeshZoneTools established. Phase 3's remaining design-intent primitives (slippage classification, RANSAC segmentation, crease-edge detection, curvature-ordered seeding, generalized winding number) need new upstream primitives and are tracked as already-filed issues (SecondMouseAU/OCCTSwiftMesh#26-#30, #107-#109) rather than implemented ad hoc. Tools: 71 -> 72. serverVersion -> 1.23.0. Tests: 113 -> 118. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gsdali
added a commit
that referenced
this pull request
Jul 22, 2026
serverVersion stays 1.24.0; test count reconciles to 133 (128 + #110's 5); CLAUDE.md's OCCTSwiftMesh bullet keeps the 1.6.0 slippage entry with mesh_curvature marked consumed; mesh-analysis.md intro unions both phases' additions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 3 of the mesh-analysis expansion (
.claude/plans/2026-07-21-mesh-analysis-expansion.md): one new tool,mesh_curvature, plus the Phase 3 primitive backlog filed and cross-linked.mesh_curvature: per-vertex discrete curvature over a body's own mesh viaOCCTSwiftMesh.Mesh.vertexCurvatures()(v1.4.0, Rusinkiewicz per-face tensor, already pinned —Package.swiftfloors OCCTSwiftMesh at 1.5.0 foralign_bodies, so no repin needed). Reportsk1/k2/mean/gaussianstats (min/p05/median/p95/max),flatFraction,highCurvatureFraction, and an optional colored render + histogram. The single-body curvature render mode deferred from segment_mesh_zones: raw-mesh region segmentation with per-zone render + metadata (see OCCTReconstruct zone model) #101.Tool count: 71 → 72.
serverVersion→1.23.0. Test count: 113 → 118.Scope decision (mirrors how #104/align_bodies was handled in #105/#106)
Phase 3's remaining primitives are mesh-domain algorithms that belong upstream in OCCTSwiftMesh per the ecosystem's factoring rule (OCCTMCP wraps, never implements mesh algorithms).
mesh_curvatureships because its one primitive is already released; everything else needs a new upstream primitive first and is tracked as a filed issue rather than implemented ad hoc:SecondMouseAU/OCCTSwiftMesh (primitive backlog):
segmented(_:)(now thatvertexCurvatures()exists)SecondMouseAU/OCCTMCP (tracking issues, blocked-on links like #104 had):
fit_primitives: RANSAC primitive report (blocked on OCCTSwiftMesh#27)detect_mesh_features: crease-ring feature outlines (blocked on OCCTSwiftMesh#28)segment_mesh_zones+ sweep axis defaults (blocked on OCCTSwiftMesh#26)All 8 were pre-filed and are cross-linked from the tool description,
CLAUDE.md, anddocs/reference/mesh-analysis.md's new "Phase 3 backlog" section.Design decisions
vertexCurvatures()'s own precondition is a welded mesh (unwelded input degrades to zero curvature everywhere). The tool welds internally before computing anything, and both the stats and the render are built entirely from that SAME welded mesh — no triangle-index correspondence problem to guard, unlikeMeshZoneTools'adjacentZones.k1/k2/meanare 1/mm;gaussian = k1*k2is 1/mm² — a genuinely different unit.highCurvatureFraction's clamp is always computed from the SAME channelcolorByselects (never cross-compared against a different channel), so this never creates a silent unit mismatch.highCurvatureFractionis the fraction of vertices whose|colorBy value|exceeds the SAME clamp used for the render — by construction close to1 - clampPercentile(that's exactly what "clamped for color" means).clampPercentile: 1.0drives it to 0; this relationship is whatMeshCurvatureToolsTests.clampPercentileSemanticspins.flatFractionis deliberately colorBy-independent:max(|k1|,|k2|) < 0.1/bboxDiag(1/mm), an absolute model-scale threshold, not a percentile of the sample — so it means the same thing regardless of how curved the rest of the body is.vertexCount == triangleCount*3post-weld) — a mesh-topology fact, never a curvature-value heuristic, so a genuinely flat body (which also reads near-zero almost everywhere) can't false-positive it.colorBydispatch validation follows the Add align_bodies: GOM-style point-to-plane ICP registration (#104) #106 convention: an unrecognized value errors naming the valid ones, rather than silently falling back to the default.Test plan
OCCTMCP_FORCE_REMOTE_DEPS=1 swift build,swift build -c release, andswift test— all green: 118 tests in 21 suites, 0 failures (was 113; +5 new).New
MeshCurvatureToolsTests(5), following the #105/#106 patterns (scene()helper + Decodable mirrors + hand-written STL fixtures, "reimplemented locally to keep this file self-contained"):Shape.box's native tessellation — a flat BREP face doesn't necessarily get any interior vertices from BRepMesh at all):k1/k2/mean/gaussianmedians are exactly zero (the ~56% face-interior majority is exactly co-planar; curvature contamination from the fold bleeds one hop past the literal edge/corner vertices, so the naive "~84% flat" estimate from edge-counting alone doesn't hold — measured and documented in the fixture's own comment),flatFraction > 0.45, render off.Shape.cylinder— its native tessellation doesn't reliably subdivide along the height axis): median k1 ~ 1/radius, median k2 ~ 0, median gaussian ~ 0, generous 20-30% tolerance since the report has no per-ring breakdown to exclude the open boundary's documented bias.colorByerrors naming valid values.vertexCount == triangleCount*3post-weld) trips the warning; curvature degrades cleanly to zero, not garbage.highCurvatureFractionresponds as designed, the underlyingk1stat block doesn't move.Docs
docs/reference/mesh-analysis.md(mesh_curvaturesection: args, units, clamp semantics, welded-internally note, colorBy table, Phase 3 backlog with all 8 issue links); README tool table + counts 71 → 72 (README, CLAUDE.md all spots,docs/reference/README.md,docs/guides/getting-started.md);CLAUDE.mdbullet forMeshCurvatureTools.swift;CLAUDE.md's OCCTSwiftMesh dependency bullet updated to notevertexCurvaturesis now consumed.🤖 Generated with Claude Code