Skip to content

v1.3.0 — find_correspondences (closes #24) + history-fix

Choose a tag to compare

@gsdali gsdali released this 09 May 20:39
· 27 commits to main since this release
9223bac

OCCTMCP v1.3.0 — find_correspondences (closes #24) + history-fix

Adds the cross-body selection-mapping tool that closes the last gap in the remap matrix, and fixes two latent bugs that turned out to hide each other.

find_correspondences — new tool (50 total)

Maps selectionIds from a source body onto a target body that's a known transform of the source — typically a mirror_or_pattern output. Different contract from remap_selection: not "X mutated into Y on the same body" but "X on body A corresponds to Y on body B under transform T".

find_correspondences({
  sourceSelectionIds: ["sel:src#face[3]"],
  targetBodyId: "mirror-src",
  transform: {
    kind: "mirror",
    planeOrigin: [0, 0, 0],
    planeNormal: [1, 0, 0]
  }
}) → { correspondences: [{ sourceSelectionId, targetSelectionId, fate, confidenceMm }] }

v1 supports translate / mirror / rotate hints. Compound transforms, bbox-alignment inference, and reading mirror_or_pattern manifest metadata as transform defaults are tracked as follow-ups in #24.

Fixed: identity history was a placebo

OCCT's TopologyGraph.findDerived returns empty for identity records (original == replacement). The recordIdentityHistory loop in HistoryRegistry was writing exactly such no-op records — so findDerived always returned empty and remap_selection fell back to the centroid heuristic. Every prior test asserting fate=preserved + confidenceMm=0 was passing because the centroid heuristic also returned 0 (non-overlapping unions, holes on opposite-side faces, etc.).

HistoryRegistry now stores an Entry { graph, isIdentityPreserving }. Identity-preserving recorders set the flag and skip the self-record loop. RemapTools treats findDerived empty + flag set as preserved-at-same-index. transform_body's history path is real for the first time.

Fixed: JSON ints failing in tool dispatch

When a client encodes Value.double(0), the SDK's JSONEncoder writes 0 (no decimal). The decoder picks .int(0). Tool dispatches reading coordinate arrays via arr[N].doubleValue got nil. LLMs sending [0, 0, 1] for unit vectors hit this constantly.

New Value.asDouble extension accepts either .int or .double; 11 array sites + 3 scalar sites converted across transform_body / mirror_or_pattern / select_topology / find_correspondences etc.

Updated history opt-in matrix

Tool Path Notes
transform_body identity flag every node maps 1:1; flag short-circuits findDerived empty to preserved
heal_shape identity flag if guards pass falls back to heuristic if topology counts changed
boolean_op per-input via *WithFullHistory recorded under output + both inputs
apply_feature per-feature via BuildResult.histories[id] every spec kind covered (OCCTSwift v1.0.4)
mirror_or_pattern use find_correspondences instead different contract — pattern instances aren't OCCT-derivatives

Tests

25 swift-testing cases (was 24). New findCorrespondencesAcrossMirror builds a 20³ box at +X, mirrors about YZ, picks a face on the source, calls find_correspondences, asserts fate=matched + targetSelectionId.hasPrefix("sel:mirror-src#face[") + confidenceMm < 0.01. The existing historyRemapPreservesAcrossTransform now actually exercises the history path (was a placebo before this release).