buildPlan function
Builds the morph plan between two lists of sampled subpaths. The plan is cacheable; it accepts any list — including intermediate shapes (interruptions).
Implementation
MorphPlan buildPlan(List<Sampled> srcSubs, List<Sampled> dstSubs) {
final p = srcSubs.length;
final q = dstSubs.length;
if (p == 0 || q == 0) morphFail('icon has no subpaths');
final a = srcSubs.map((s) => s.pts).toList();
final b = dstSubs.map((s) => s.pts).toList();
// Undocumented precondition of the TypeScript, made explicit here: n comes
// from the first source subpath and every subpath on both sides is assumed
// to have been sampled at the same N.
assert(
a.every((x) => x.length == a[0].length) &&
b.every((x) => x.length == a[0].length),
'all subpaths must be sampled at the same N',
);
final pairs = <(int, int)>[];
if (p == q) {
final perm = _bestPermutation(_costMatrix(a, b));
for (var i = 0; i < p; i++) {
pairs.add((i, perm[i]));
}
} else if (p < q) {
final f = _bestSurjection(_costMatrix(b, a));
for (var j = 0; j < q; j++) {
pairs.add((f[j], j));
}
} else {
final f = _bestSurjection(_costMatrix(a, b));
for (var i = 0; i < p; i++) {
pairs.add((i, f[i]));
}
}
final n = a[0].length ~/ 2;
final items = pairs.map((pair) {
final (si, di) = pair;
final al = alignPair(a[si], b[di], srcSubs[si].closed, dstSubs[di].closed);
final ap = al.a;
final aC = Float64List(2 * n);
final bT = Float64List(2 * n);
final bO = Float64List(2 * n);
final cos = math.cos(-al.theta);
final sin = math.sin(-al.theta);
for (var i = 0; i < n; i++) {
aC[2 * i] = ap[2 * i] - al.ca.$1;
aC[2 * i + 1] = ap[2 * i + 1] - al.ca.$2;
final bx = al.b[2 * i] - al.cb.$1;
final by = al.b[2 * i + 1] - al.cb.$2;
bT[2 * i] = (bx * cos - by * sin) / al.sigma;
bT[2 * i + 1] = (bx * sin + by * cos) / al.sigma;
bO[2 * i] = al.b[2 * i];
bO[2 * i + 1] = al.b[2 * i + 1];
}
return PlanItem(
a: ap,
aC: aC,
bT: bT,
bO: bO,
ca: al.ca,
cb: al.cb,
theta: al.theta,
lnSigma: math.log(al.sigma),
res: al.res,
closed: srcSubs[si].closed && dstSubs[di].closed,
block: null,
);
}).toList();
if (items.length > 1) _applyGlobal(items, n);
return MorphPlan(items, n);
}