flattenPath function
Flattens path through transform (page space -> device pixels) into
polylines. Cubics subdivide adaptively to stay within tolerance device
pixels of the true curve (Wang's bound on the second differences).
Implementation
List<FlatSubpath> flattenPath(PdfPath path, PdfMatrix transform,
{double tolerance = 0.25}) {
final out = <FlatSubpath>[];
DoubleBuilder? current;
var closed = false;
var startX = 0.0, startY = 0.0;
var lastX = 0.0, lastY = 0.0;
void finish() {
final done = current;
if (done != null && done.length >= 4) {
out.add(FlatSubpath(Float64List.fromList(done.view), closed: closed));
}
current = null;
closed = false;
}
for (final segment in path.segments) {
final cur = current;
switch (segment) {
case PdfMoveTo(:final x, :final y):
finish();
lastX = startX = transform.transformX(x, y);
lastY = startY = transform.transformY(x, y);
current = DoubleBuilder(64)..add2(startX, startY);
case PdfLineTo(:final x, :final y):
if (cur == null) continue;
lastX = transform.transformX(x, y);
lastY = transform.transformY(x, y);
cur.add2(lastX, lastY);
case PdfCubicTo():
if (cur == null) continue;
final x1 = transform.transformX(segment.x1, segment.y1);
final y1 = transform.transformY(segment.x1, segment.y1);
final x2 = transform.transformX(segment.x2, segment.y2);
final y2 = transform.transformY(segment.x2, segment.y2);
final x3 = transform.transformX(segment.x3, segment.y3);
final y3 = transform.transformY(segment.x3, segment.y3);
final d1 =
math.max((lastX - 2 * x1 + x2).abs(), (lastY - 2 * y1 + y2).abs());
final d2 =
math.max((x1 - 2 * x2 + x3).abs(), (y1 - 2 * y2 + y3).abs());
final d = math.max(d1, d2);
final n = d <= tolerance
? 1
: math.sqrt(3 * d / (4 * tolerance)).ceil().clamp(1, 128);
for (var i = 1; i <= n; i++) {
final t = i / n;
final mt = 1 - t;
final a = mt * mt * mt, b = 3 * mt * mt * t, c = 3 * mt * t * t;
final e = t * t * t;
cur.add2(a * lastX + b * x1 + c * x2 + e * x3,
a * lastY + b * y1 + c * y2 + e * y3);
}
lastX = x3;
lastY = y3;
case PdfClosePath():
if (cur == null) continue;
if (lastX != startX || lastY != startY) {
cur.add2(startX, startY);
}
closed = true;
lastX = startX;
lastY = startY;
// `m ... h m ...` reuses the pen; a close ends the subpath here and
// a following lineTo would be malformed input - treat close as final.
finish();
}
}
finish();
return out;
}