//port from https://github.com/mrdoob/three.js/blob/7e0a78beb9317e580d7fa4da9b5b12be051c6feb/examples/js/loaders/SVGLoader.js#L569 public static IList <Point> Flatten(this ArcSegment segment, Point startPoint) { var start = startPoint; var end = segment.Point; var rx = segment.Size.Width; var ry = segment.Size.Height; var x_axis_rotation = segment.RotationAngle; var large_arc_flag = segment.IsLargeArc ? 1 : 0; var sweep_flag = (int)segment.SweepDirection; // Ensure radii are positive rx = Math.Abs(rx); ry = Math.Abs(ry); // Compute (x1′, y1′) var dx2 = (start.x - end.x) / 2.0; var dy2 = (start.y - end.y) / 2.0; var x1p = Math.Cos(x_axis_rotation) * dx2 + Math.Sin(x_axis_rotation) * dy2; var y1p = -Math.Sin(x_axis_rotation) * dx2 + Math.Cos(x_axis_rotation) * dy2; // Compute (cx′, cy′) var rxs = rx * rx; var rys = ry * ry; var x1ps = x1p * x1p; var y1ps = y1p * y1p; // Ensure radii are large enough var cr = x1ps / rxs + y1ps / rys; if (cr > 1) { // scale up rx,ry equally so cr == 1 var s = Math.Sqrt(cr); rx = s * rx; ry = s * ry; rxs = rx * rx; rys = ry * ry; } var dq = (rxs * y1ps + rys * x1ps); var pq = (rxs * rys - dq) / dq; var q = Math.Sqrt(Math.Max(0, pq)); if (large_arc_flag == sweep_flag) { q = -q; } var cxp = q * rx * y1p / ry; var cyp = -q * ry * x1p / rx; // Step 3: Compute (cx, cy) from (cx′, cy′) var cx = Math.Cos(x_axis_rotation) * cxp - Math.Sin(x_axis_rotation) * cyp + (start.x + end.x) / 2; var cy = Math.Sin(x_axis_rotation) * cxp + Math.Cos(x_axis_rotation) * cyp + (start.y + end.y) / 2; // Step 4: Compute θ1 and Δθ var theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry); var delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2); var curve = new EllipseCurve(cx, cy, rx, ry, theta, theta + delta, sweep_flag == 0, x_axis_rotation); // Calculate number of points for polyline approximation var max = (int)(Math.Abs(delta) / MathEx.Deg2Rad(3)); IList <Point> points = new List <Point>(max); for (double i = 0; i <= max; i++) { var p = curve.getPoint(i / max); points.Add(p); } return(points); }
public void ArcTo(Point point, double radius, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked) { ArcSegment segment = new ArcSegment(point, new Size(radius, radius), rotationAngle, isLargeArc, sweepDirection, isStroked); Figure.Segments.Add(segment); }
public void EllipseArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection, bool isStroked) { ArcSegment segment = new ArcSegment(point, size, rotationAngle, isLargeArc, sweepDirection, isStroked); Figure.Segments.Add(segment); }
/// <summary> /// Flatten the ArcSegment into a List of Points /// </summary> /// <param name="segment">the arc segment</param> /// <param name="startPoint">start point</param> /// <param name="tolerance">tolerance</param> /// <returns>flattened point list</returns> /// <remarks> /// ref: http://www.charlespetzold.com/blog/2008/01/Mathematics-of-ArcSegment.html /// </remarks> public static IList <Point> Flatten(this ArcSegment segment, Point startPoint /*start point*/, double tolerance = 1) { var pt1 = startPoint; var pt2 = segment.Point; var radiusX = segment.Size.Width; var radiusY = segment.Size.Height; var angleRotation = segment.RotationAngle; var isLargeArc = segment.IsLargeArc; var isCounterclockwise = segment.SweepDirection == SweepDirection.Counterclockwise; // Adjust for different radii and rotation angle Matrix matx = new Matrix(); matx.Rotate(-angleRotation); matx.Scale(radiusY / radiusX, 1); pt1 = matx.Transform(pt1); pt2 = matx.Transform(pt2); // Get info about chord that connects both points Point midPoint = new Point((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2); Vector vect = pt2 - pt1; double halfChord = vect.Length / 2; // Get vector from chord to center Vector vectRotated; // (comparing two Booleans here!) if (isLargeArc == isCounterclockwise) { vectRotated = new Vector(-vect.Y, vect.X); } else { vectRotated = new Vector(vect.Y, -vect.X); } vectRotated.Normalize(); // Distance from chord to center double centerDistance = Math.Sqrt(radiusY * radiusY - halfChord * halfChord); // Calculate center point Point center = midPoint + centerDistance * vectRotated; // Get angles from center to the two points double angle1 = Math.Atan2(pt1.Y - center.Y, pt1.X - center.X); double angle2 = Math.Atan2(pt2.Y - center.Y, pt2.X - center.X); // (another comparison of two Booleans!) if (isLargeArc == (Math.Abs(angle2 - angle1) < Math.PI)) { if (angle1 < angle2) { angle1 += 2 * Math.PI; } else { angle2 += 2 * Math.PI; } } // Invert matrix for final point calculation matx.Invert(); // Calculate number of points for polyline approximation int max = (int)((4 * (radiusX + radiusY) * Math.Abs(angle2 - angle1) / (2 * Math.PI)) / tolerance); IList <Point> points = new List <Point>(max); // Loop through the points for (int i = 0; i <= max; i++) { double angle = ((max - i) * angle1 + i * angle2) / max; double x = center.X + radiusY * Math.Cos(angle); double y = center.Y + radiusY * Math.Sin(angle); // Transform the point back Point pt = matx.Transform(new Point(x, y)); points.Add(pt); } return(points); }