dashSubpaths function

List<FlatSubpath> dashSubpaths(
  1. List<FlatSubpath> subpaths,
  2. List<double> pattern,
  3. double phase
)

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