public static SvgPathSegmentList NormalizeAndTruncate(this SvgPathSegmentList path, out DoubleMatrix transform, double halfSize = 2048) { // Calculate the bounding box of the path var points = path.Curves().SelectMany(c => c.EnclosingPolygon).ToArray(); // Don't normalize an empty path if (points.Length == 0) { transform = DoubleMatrix.Identity; return(path); } var minx = double.PositiveInfinity; var maxx = double.NegativeInfinity; var miny = double.PositiveInfinity; var maxy = double.NegativeInfinity; // Find the bounding box foreach (var pt in points) { minx = Math.Min(minx, pt.X); maxx = Math.Max(maxx, pt.X); miny = Math.Min(miny, pt.Y); maxy = Math.Max(maxy, pt.Y); } // Calculate the points var center = new Double2(minx + maxx, miny + maxy) / 2; // And the half-sizes var hx = (maxx - minx) / 2; var hy = (maxy - miny) / 2; // Chose the maximum value of it var d = halfSize / Math.Max(hx, hy); var dm = Double2.Zero; // And transform the path var newPath = new SvgPathSegmentList(); foreach (var segment in path) { if (segment is SvgMoveToSegment move) { newPath.Add(new SvgMoveToSegment(Transform(move.Start))); } else if (segment is SvgLineSegment line) { newPath.Add(new SvgLineSegment(Transform(line.Start), Transform(line.End))); } else if (segment is SvgQuadraticCurveSegment quad) { newPath.Add(new SvgQuadraticCurveSegment(Transform(quad.Start), Transform(quad.ControlPoint), Transform(quad.End))); } else if (segment is SvgCubicCurveSegment cubic) { newPath.Add(new SvgCubicCurveSegment(Transform(cubic.Start), Transform(cubic.FirstControlPoint), Transform(cubic.SecondControlPoint), Transform(cubic.End))); } else if (segment is SvgArcSegment arc) { newPath.Add(new SvgArcSegment(Transform(arc.Start), (float)(arc.RadiusX * d).Truncate(), (float)(arc.RadiusY * d).Truncate(), arc.Angle, arc.Size, arc.Sweep, Transform(arc.End))); } else if (segment is SvgClosePathSegment) { newPath.Add(new SvgClosePathSegment()); } } // Mount the inverse matrix transform = new DoubleMatrix(1 / d, 0, 0, 1 / d, center.X, center.Y); return(newPath); System.Drawing.PointF Transform(System.Drawing.PointF point) { var pt = (point.ToDouble2() * d - center * d).Truncate() + dm; return(new System.Drawing.PointF((float)pt.X, (float)pt.Y)); } }
// The bounding box matrix should map [-2048,2048] to [0,1] on both directions public static DoubleRectangle BoundingBox(this SvgPathSegmentList path) => path.Curves().Select(c => c.BoundingBox).Aggregate(DoubleRectangle.Union);