flattenPath function

List<FlatSubpath> flattenPath(
  1. PdfPath path,
  2. PdfMatrix transform, {
  3. double tolerance = 0.25,
})

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;
}