dashSubpaths function
Re-cuts subpaths into dash segments (ยง8.4.3.6). pattern and phase
are already in device pixels. Zero-length "on" dashes survive as
single-point subpaths so round caps still paint dots.
Implementation
List<FlatSubpath> dashSubpaths(
List<FlatSubpath> subpaths, List<double> pattern, double phase) {
final dashes = [
for (final d in pattern)
if (d >= 0) d,
];
if (dashes.length.isOdd) dashes.addAll(List.of(dashes));
final cycle = dashes.fold(0.0, (a, b) => a + b);
if (dashes.isEmpty || cycle <= 0) return subpaths;
final out = <FlatSubpath>[];
void emit(DoubleBuilder b) {
if (b.length >= 2) {
out.add(FlatSubpath(Float64List.fromList(b.view), closed: false));
}
}
for (final sub in subpaths) {
final p = sub.points;
var index = 0;
var on = true;
var remaining = dashes[0];
var toSkip = phase.abs() % cycle;
while (toSkip > 0) {
if (toSkip >= remaining) {
toSkip -= remaining;
index = (index + 1) % dashes.length;
on = !on;
remaining = dashes[index];
} else {
remaining -= toSkip;
toSkip = 0;
}
}
DoubleBuilder? current = on ? (DoubleBuilder(32)..add2(p[0], p[1])) : null;
for (var i = 0; i + 3 < p.length; i += 2) {
var ax = p[i], ay = p[i + 1];
final bx = p[i + 2], by = p[i + 3];
var segLen = math.sqrt((bx - ax) * (bx - ax) + (by - ay) * (by - ay));
while (segLen > 1e-12) {
if (remaining >= segLen) {
remaining -= segLen;
segLen = 0;
if (on) {
current!.add2(bx, by);
}
} else {
final t = remaining / segLen;
final mx = ax + (bx - ax) * t, my = ay + (by - ay) * t;
if (on) {
current!.add2(mx, my);
emit(current);
current = null;
} else {
current = DoubleBuilder(32)..add2(mx, my);
}
ax = mx;
ay = my;
segLen -= remaining;
remaining = 0;
}
if (remaining <= 1e-12) {
index = (index + 1) % dashes.length;
on = !on;
remaining = dashes[index];
if (remaining <= 0 && cycle <= 1e-9) break;
if (on && current == null) current = DoubleBuilder(32)..add2(ax, ay);
if (!on && current != null) {
emit(current);
current = null;
}
}
}
}
if (current != null) emit(current);
}
return out;
}