// lib/editor/engine/stroke_simplify.dart // // Ramer–Douglas–Peucker stroke point reduction. A fast Surface-Pen stroke can // land hundreds of nearly-collinear samples; thinning them before persistence // shrinks the DB row + speeds re-rasterization (R1/R10 perf) with no visible // change. Endpoints + perceptually-significant vertices are kept; pressure/tilt // ride along on the retained points. // // Pure geometry over EditorStroke (normalized coords); fully unit-tested. The // commit path can call this before saveHost; the live in-progress stroke is left // untouched so drawing stays crisp. import 'stroke_model.dart'; /// Returns [stroke] with its points reduced by RDP at [tolerance] (perpendicular /// distance in normalized units; larger = more aggressive). Strokes with <= 2 /// points, or a non-positive tolerance, are returned unchanged. EditorStroke simplifyStroke(EditorStroke stroke, {double tolerance = 0.0008}) { final pts = stroke.points; if (pts.length <= 2 || tolerance <= 0) return stroke; final keep = List.filled(pts.length, false); keep[0] = true; keep[pts.length - 1] = true; _rdp(pts, 0, pts.length - 1, tolerance * tolerance, keep); final reduced = [ for (var i = 0; i < pts.length; i++) if (keep[i]) pts[i], ]; if (reduced.length == pts.length) return stroke; return stroke.copyWith(points: reduced); } // Iterative-friendly recursion over the index range [first, last]. void _rdp( List pts, int first, int last, double tolSq, List keep, ) { if (last <= first + 1) return; var maxDistSq = 0.0; var index = -1; final ax = pts[first].x, ay = pts[first].y; final bx = pts[last].x, by = pts[last].y; for (var i = first + 1; i < last; i++) { final d = _perpDistSq(pts[i].x, pts[i].y, ax, ay, bx, by); if (d > maxDistSq) { maxDistSq = d; index = i; } } if (maxDistSq > tolSq && index != -1) { keep[index] = true; _rdp(pts, first, index, tolSq, keep); _rdp(pts, index, last, tolSq, keep); } } /// Squared perpendicular distance of (px,py) from the segment (ax,ay)-(bx,by). /// Degenerate segment (a==b) falls back to squared distance to the point. double _perpDistSq( double px, double py, double ax, double ay, double bx, double by, ) { final dx = bx - ax, dy = by - ay; final lenSq = dx * dx + dy * dy; if (lenSq == 0) { final ex = px - ax, ey = py - ay; return ex * ex + ey * ey; } final cross = (px - ax) * dy - (py - ay) * dx; return (cross * cross) / lenSq; }