public override VectorPoint Copy() { EllipseLinePoint point = new EllipseLinePoint(X, Y); point.Length = Length; point.SweepAngle = SweepAngle; point.StartAngle = StartAngle; point.SinXAxis = SinXAxis; point.CosXAxis = CosXAxis; point.CenterX = CenterX; point.CenterY = CenterY; point.RadiusX = RadiusX; point.RadiusY = RadiusY; return(point); }
/// <summary>Handles SVG arcs.</summary> public void EllipseArc(float rx, float ry, float xAxisRotation, float p1x, float p1y, bool largeArcFlag, bool sweepFlag) { // In accordance to: http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters rx = Math.Abs(rx); ry = Math.Abs(ry); // If the endpoints are identical, do nothing. if (LatestPathNode.X == p1x && LatestPathNode.Y == p1y) { return; } // If rx = 0 or ry = 0 then this arc is treated as a straight line segment joining the endpoints. if (rx == 0f || ry == 0f) { LineTo(p1x, p1y); return; } // Normalize rotation from deg to radians: xAxisRotation = (xAxisRotation % 360f) * UnityEngine.Mathf.Deg2Rad; float cosXAxis = (float)Math.Cos(xAxisRotation); float sinXAxis = (float)Math.Sin(xAxisRotation); // Conversion from endpoint to center parameterization // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter // #1: Compute transformed point float dx = (LatestPathNode.X - p1x) / 2f; float dy = (LatestPathNode.Y - p1y) / 2f; float transformedX = (float)(cosXAxis * dx + sinXAxis * dy); float transformedY = (float)(-sinXAxis * dx + cosXAxis * dy); // Ensure radii are large enough float radiiCheck = (float)( Math.Pow(transformedX, 2f) / Math.Pow(rx, 2f) + Math.Pow(transformedY, 2f) / Math.Pow(ry, 2f) ); if (radiiCheck > 1) { rx = (float)Math.Sqrt(radiiCheck) * rx; ry = (float)Math.Sqrt(radiiCheck) * ry; } // #2: Compute transformed center float cSquareNumerator = (float)( Math.Pow(rx, 2f) * Math.Pow(ry, 2f) - Math.Pow(rx, 2f) * Math.Pow(transformedY, 2f) - Math.Pow(ry, 2f) * Math.Pow(transformedX, 2f) ); float cSquareRootDenom = (float)( Math.Pow(rx, 2f) * Math.Pow(transformedY, 2f) + Math.Pow(ry, 2f) * Math.Pow(transformedX, 2f) ); float cRadicand = cSquareNumerator / cSquareRootDenom; // Make sure this never drops below zero because of precision cRadicand = cRadicand < 0f ? 0f : cRadicand; float cCoef = (largeArcFlag != sweepFlag ? 1f : -1f) * (float)Math.Sqrt(cRadicand); float transformedCenterX = cCoef * ((rx * transformedY) / ry); float transformedCenterY = cCoef * (-(ry * transformedX) / rx); // #3: Compute center float centerX = (float)( cosXAxis * transformedCenterX - sinXAxis * transformedCenterY + ((LatestPathNode.X + p1x) / 2f) ); float centerY = (float)( sinXAxis * transformedCenterX + cosXAxis * transformedCenterY + ((LatestPathNode.Y + p1y) / 2f) ); // #4: Compute start/sweep angles // Start angle of the elliptical arc prior to the stretch and rotate operations. // Difference between the start and end angles UnityEngine.Vector2 startVector = new UnityEngine.Vector2( (transformedX - transformedCenterX) / rx, (transformedY - transformedCenterY) / ry ); float startAngle = AngleBetween(new UnityEngine.Vector2(1f, 0f), startVector); UnityEngine.Vector2 endVector = new UnityEngine.Vector2( (-transformedX - transformedCenterX) / rx, (-transformedY - transformedCenterY) / ry ); float sweepAngle = AngleBetween(startVector, endVector); if (!sweepFlag && sweepAngle > 0) { sweepAngle -= 2f * (float)Math.PI; } else if (sweepFlag && sweepAngle < 0) { sweepAngle += 2f * (float)Math.PI; } // We use % instead of `mod(..)` because we want it to be -360deg to 360deg(but actually in radians) sweepAngle %= 2f * (float)Math.PI; // Ellipse point: EllipseLinePoint elp = new EllipseLinePoint(p1x, p1y); // Apply values: elp.SweepAngle = sweepAngle; elp.StartAngle = startAngle; elp.SinXAxis = sinXAxis; elp.CosXAxis = cosXAxis; elp.CenterX = centerX; elp.CenterY = centerY; elp.RadiusX = rx; elp.RadiusY = ry; // Add it: AddPathNode(elp); }