File: PathArcExtensions.cs
Web Access
Project: src\src\Graphics\src\Graphics\Graphics.csproj (Microsoft.Maui.Graphics)
using System;
 
namespace Microsoft.Maui.Graphics
{
	public static class PathArcExtensions
	{
		public static void SVGArcTo(this PathF aTarget, float rx, float ry, float angle, bool largeArcFlag, bool sweepFlag, float x, float y, float lastPointX, float lastPointY)
		{
			float[] vValues = ComputeSvgArc(rx, ry, angle, largeArcFlag, sweepFlag, x, y, lastPointX, lastPointY);
			aTarget.DrawArc(vValues[0], vValues[1], vValues[2], vValues[3], vValues[4], vValues[5], vValues[6]);
		}
 
		/**
		* Converts a svg arc specification to a Degrafa arc.
		**/
 
		static float[] ComputeSvgArc(float rx, float ry, float angle, bool largeArcFlag, bool sweepFlag, float x, float y, float lastPointX, float lastPointY)
		{
			//store before we do anything with it
			float xAxisRotation = angle;
 
			// Compute the half distance between the current and the final point
			float dx2 = (lastPointX - x) / 2.0f;
			float dy2 = (lastPointY - y) / 2.0f;
 
			// Convert angle from degrees to radians
			angle = GeometryUtil.DegreesToRadians(angle);
			float cosAngle = MathF.Cos(angle);
			float sinAngle = MathF.Sin(angle);
 
			//Compute (x1, y1)
			float x1 = cosAngle * dx2 + sinAngle * dy2;
			float y1 = -sinAngle * dx2 + cosAngle * dy2;
 
			// Ensure radii are large enough
			rx = MathF.Abs(rx);
			ry = MathF.Abs(ry);
			float prx = rx * rx;
			float pry = ry * ry;
			float px1 = x1 * x1;
			float py1 = y1 * y1;
 
			// check that radii are large enough
			float radiiCheck = px1 / prx + py1 / pry;
			if (radiiCheck > 1)
			{
				rx = MathF.Sqrt(radiiCheck) * rx;
				ry = MathF.Sqrt(radiiCheck) * ry;
				prx = rx * rx;
				pry = ry * ry;
			}
 
			//Compute (cx1, cy1)
			float sign = largeArcFlag == sweepFlag ? -1 : 1;
			float sq = (prx * pry - prx * py1 - pry * px1) / (prx * py1 + pry * px1);
			sq = sq < 0 ? 0 : sq;
			float coef = sign * MathF.Sqrt(sq);
			float cx1 = coef * (rx * y1 / ry);
			float cy1 = coef * -(ry * x1 / rx);
 
			//Compute (cx, cy) from (cx1, cy1)
			float sx2 = (lastPointX + x) / 2.0f;
			float sy2 = (lastPointY + y) / 2.0f;
			float cx = sx2 + (cosAngle * cx1 - sinAngle * cy1);
			float cy = sy2 + (sinAngle * cx1 + cosAngle * cy1);
 
			//Compute the angleStart (angle1) and the angleExtent (dangle)
			float ux = (x1 - cx1) / rx;
			float uy = (y1 - cy1) / ry;
			float vx = (-x1 - cx1) / rx;
			float vy = (-y1 - cy1) / ry;
 
			//Compute the angle start
			float n = MathF.Sqrt(ux * ux + uy * uy);
			float p = ux;
 
			sign = uy < 0 ? -1.0f : 1.0f;
 
			float angleStart = GeometryUtil.RadiansToDegrees(sign * MathF.Acos(p / n));
 
			// Compute the angle extent
			n = MathF.Sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
			p = ux * vx + uy * vy;
			sign = ux * vy - uy * vx < 0 ? -1.0f : 1.0f;
			float angleExtent = GeometryUtil.RadiansToDegrees(sign * MathF.Acos(p / n));
 
			if (!sweepFlag && angleExtent > 0)
			{
				angleExtent -= 360;
			}
			else if (sweepFlag && angleExtent < 0)
			{
				angleExtent += 360;
			}
 
			angleExtent %= 360;
			angleStart %= 360;
 
			return new[] { cx, cy, angleStart, angleExtent, rx, ry, xAxisRotation };
		}
 
		/**
		* Draws an arc of type "open" only. Accepts an optional x axis rotation value
		**/
 
		public static void DrawArc(this PathF aPath, float x, float y, float startAngle, float arc, float radius, float yRadius, float xAxisRotation)
		{
			// Circumvent drawing more than is needed
			if (MathF.Abs(arc) > 360)
			{
				arc = 360;
			}
 
			// Draw in a maximum of 45 degree segments. First we calculate how many
			// segments are needed for our arc.
			float segs = MathF.Ceiling(MathF.Abs(arc) / 45);
 
			// Now calculate the sweep of each segment
			float segAngle = arc / segs;
 
			float theta = GeometryUtil.DegreesToRadians(segAngle);
			float angle = GeometryUtil.DegreesToRadians(startAngle);
 
			// Draw as 45 degree segments
			if (segs > 0)
			{
				float beta = GeometryUtil.DegreesToRadians(xAxisRotation);
				float sinbeta = MathF.Sin(beta);
				float cosbeta = MathF.Cos(beta);
 
				// Loop for drawing arc segments
				for (int i = 0; i < segs; i++)
				{
					angle += theta;
 
					float sinangle = MathF.Sin(angle - theta / 2);
					float cosangle = MathF.Cos(angle - theta / 2);
 
					float div = MathF.Cos(theta / 2);
					float cx = x + (radius * cosangle * cosbeta - yRadius * sinangle * sinbeta) / div;
					//Why divide by Math.cos(theta/2)? - FIX THIS
					float cy = y + (radius * cosangle * sinbeta + yRadius * sinangle * cosbeta) / div;
					//Why divide by Math.cos(theta/2)? - FIX THIS
 
					sinangle = MathF.Sin(angle);
					cosangle = MathF.Cos(angle);
 
					float x1 = x + (radius * cosangle * cosbeta - yRadius * sinangle * sinbeta);
					float y1 = y + (radius * cosangle * sinbeta + yRadius * sinangle * cosbeta);
 
					aPath.QuadTo(cx, cy, x1, y1);
				}
			}
		}
	}
}