Slot index is the correspondence for a whole rig, and one global mapping cannot be the right answer for every pair. This is the page about what to do then.
The arrows are the clearest case. Rotating arrow-right by 90 degrees carries its upper
barb onto arrow-down's lower barb. Under identity correspondence the barbs therefore
slide across the shaft while it turns, instead of the whole icon spinning as one piece.
Both are defensible motions. Only one is what you probably wanted.
import { buildAlignment, describeAlignment } from "@/lib/morph/compile";
import { coreRig } from "@/lib/morph/sets/core";
// Under the identity mapping this pair is not rigid: the global residual is ~0.91,
// because rotating an arrow carries its upper barb onto the target's LOWER barb.
const identity = buildAlignment(coreRig, "arrow-right", "arrow-down", { pair: [0, 1, 2] });
console.log(identity.hybrid, identity.globalRes);
console.log(describeAlignment(identity));describeAlignment prints the per-slot rotation, scale and residual. Two things give the
problem away: a global residual nowhere near zero, and slots whose rotations disagree in
sign when the icon ought to be turning one way.
import { findRigidPairing } from "@/lib/morph/compile";
import { coreRig } from "@/lib/morph/sets/core";
// Search correspondence and orientation for a mapping that makes the pair congruent.
// Returns null when no permutation does, which is the common and correct case for pairs
// that genuinely deform.
const proposed = findRigidPairing(coreRig, "arrow-right", "arrow-down");
// [0, 2, 1]: keep the shaft, swap the barbs.
console.log(proposed);findRigidPairing searches correspondence and traversal orientation for a mapping that
makes the pair congruent. It returns null when none does, which is the common and correct
answer for pairs that genuinely deform.
import { coreRig } from "@/lib/morph/sets/core";
// Committed on the rig itself, keyed "from>to". The inverse is derived, so one direction
// per family is enough. Now the pair compiles as a single rigid rotation.
export const rigWithPairs = {
...coreRig,
pairs: {
...coreRig.pairs,
"arrow-right>arrow-down": [0, 2, 1],
},
};Keyed "from>to", with the inverse derived, so one direction per family is enough. The
core set commits five of these and every one is the same barb swap.
It can, once, on each visitor's device, and it will make the same choice every time including when that choice is wrong. There is nowhere to record that you disagreed.
Here the proposal is a build-time suggestion and the decision is a line in your repo.