buildPlan function
Implementation
MeldPlan buildPlan(List<SampledPath> source, List<SampledPath> target,
{bool cacheHit = false}) {
final stopwatch = Stopwatch()..start();
if (source.isEmpty || target.isEmpty)
throw MeldException(
'empty-icon', 'Both icons must contain at least one subpath.');
final sampleCount = source.first.pointCount;
if (source.any((path) => path.pointCount != sampleCount) ||
target.any((path) => path.pointCount != sampleCount)) {
throw MeldException('sample-count-mismatch',
'All source and target subpaths must use the same sample count.');
}
final a = source.map((path) => path.points).toList(growable: false);
final b = target.map((path) => path.points).toList(growable: false);
final pairs = <(int, int)>[];
if (a.length == b.length) {
final permutation = _bestPermutation(_costMatrix(a, b));
for (var i = 0; i < a.length; i++) {
pairs.add((i, permutation[i]));
}
} else if (a.length < b.length) {
final assignment = _bestSurjection(_costMatrix(b, a));
for (var j = 0; j < b.length; j++) {
pairs.add((assignment[j], j));
}
} else {
final assignment = _bestSurjection(_costMatrix(a, b));
for (var i = 0; i < a.length; i++) {
pairs.add((i, assignment[i]));
}
}
final items = <MeldPlanItem>[];
for (final pair in pairs) {
final alignment = alignPair(
a[pair.$1],
b[pair.$2],
closedA: source[pair.$1].closed,
closedB: target[pair.$2].closed,
);
final centeredA = Float64List(sampleCount * 2);
final transformedB = Float64List(sampleCount * 2);
final orientedB = Float64List(sampleCount * 2);
final cos = math.cos(-alignment.theta);
final sin = math.sin(-alignment.theta);
for (var i = 0; i < sampleCount; i++) {
centeredA[i * 2] = alignment.a[i * 2] - alignment.centerA.$1;
centeredA[i * 2 + 1] = alignment.a[i * 2 + 1] - alignment.centerA.$2;
final bx = alignment.b[i * 2] - alignment.centerB.$1;
final by = alignment.b[i * 2 + 1] - alignment.centerB.$2;
transformedB[i * 2] = (bx * cos - by * sin) / alignment.scale;
transformedB[i * 2 + 1] = (bx * sin + by * cos) / alignment.scale;
orientedB[i * 2] = alignment.b[i * 2];
orientedB[i * 2 + 1] = alignment.b[i * 2 + 1];
}
items.add(
MeldPlanItem(
a: alignment.a,
centeredA: centeredA,
transformedB: transformedB,
orientedB: orientedB,
centerA: alignment.centerA,
centerB: alignment.centerB,
theta: alignment.theta,
logScale: math.log(alignment.scale),
residual: alignment.residual,
closed: source[pair.$1].closed && target[pair.$2].closed,
sourceIndex: pair.$1,
targetIndex: pair.$2,
),
);
}
final plannedItems = items.length > 1
? _applyGlobal(items, sampleCount)
: List<MeldPlanItem>.unmodifiable(items);
stopwatch.stop();
final residuals = plannedItems.map((item) => item.residual);
final diagnostics = PlanDiagnostics(
sourceSubpaths: source.length,
targetSubpaths: target.length,
sampleCount: sampleCount,
meanResidual:
residuals.fold<double>(0, (sum, value) => sum + value) / items.length,
maxResidual: residuals.fold<double>(0, math.max),
usedGlobalBlock: plannedItems.any((item) => item.block != null),
elapsedMicros: stopwatch.elapsedMicroseconds,
cacheHit: cacheHit,
);
return MeldPlan(
items: plannedItems, sampleCount: sampleCount, diagnostics: diagnostics);
}