File: GeometryUtil.cs
Web Access
Project: src\src\Graphics\src\Graphics\Graphics.csproj (Microsoft.Maui.Graphics)
using System;
 
namespace Microsoft.Maui.Graphics
{
	public static class GeometryUtil
	{
		public const float Epsilon = 0.0000000001f;
 
		public static float GetDistance(float x1, float y1, float x2, float y2)
		{
			var a = x2 - x1;
			var b = y2 - y1;
 
			return MathF.Sqrt(a * a + b * b);
		}
 
		public static float GetAngleAsDegrees(float x1, float y1, float x2, float y2)
		{
			var dx = x1 - x2;
			var dy = y1 - y2;
 
			var radians = MathF.Atan2(dy, dx);
			var degrees = radians * 180.0f / MathF.PI;
 
			return 180 - degrees;
		}
 
		public static float DegreesToRadians(float angle)
		{
			return MathF.PI * angle / 180;
		}
 
		public static double DegreesToRadians(double angle)
		{
			return Math.PI * angle / 180;
		}
 
		public static float RadiansToDegrees(float angle)
		{
			return angle * (180 / MathF.PI);
		}
 
		public static double RadiansToDegrees(double angle)
		{
			return angle * (180 / Math.PI);
		}
 
		public static PointF RotatePoint(PointF point, float angle)
		{
			var radians = DegreesToRadians(angle);
 
			var x = MathF.Cos(radians) * point.X - MathF.Sin(radians) * point.Y;
			var y = MathF.Sin(radians) * point.X + MathF.Cos(radians) * point.Y;
 
			return new PointF(x, y);
		}
 
		public static PointF RotatePoint(PointF center, PointF point, float angle)
		{
			var radians = DegreesToRadians(angle);
			var x = center.X + MathF.Cos(radians) * (point.X - center.X) - MathF.Sin(radians) * (point.Y - center.Y);
			var y = center.Y + MathF.Sin(radians) * (point.X - center.X) + MathF.Cos(radians) * (point.Y - center.Y);
			return new PointF(x, y);
		}
 
		public static float GetSweep(float angle1, float angle2, bool clockwise)
		{
			if (clockwise)
			{
				if (angle2 > angle1)
				{
					return angle1 + (360 - angle2);
				}
				else
				{
					return angle1 - angle2;
				}
			}
			else
			{
				if (angle1 > angle2)
				{
					return angle2 + (360 - angle1);
				}
				else
				{
					return angle2 - angle1;
				}
			}
		}
 
		public static PointF PolarToPoint(float angleInRadians, float fx, float fy)
		{
			var sin = MathF.Sin(angleInRadians);
			var cos = MathF.Cos(angleInRadians);
			return new PointF(fx * cos, fy * sin);
		}
 
 
		/// <summary>
		/// Gets the point on an ellipse that corresponds to the given angle.
		/// </summary>
		/// <returns>The point.</returns>
		/// <param name="x">The x position of the bounding rectangle.</param>
		/// <param name="y">The y position of the bounding rectangle.</param>
		/// <param name="width">The width of the bounding rectangle.</param>
		/// <param name="height">The height of the bounding rectangle.</param>
		/// <param name="angleInDegrees">Angle in degrees.</param>
		public static PointF EllipseAngleToPoint(float x, float y, float width, float height, float angleInDegrees)
		{
			var radians = DegreesToRadians(angleInDegrees);
 
			var cx = x + width / 2;
			var cy = y + height / 2;
 
			var point = PolarToPoint(radians, width / 2, height / 2);
 
			point.X += cx;
			point.Y += cy;
			return point;
		}
 
		public static PointF GetOppositePoint(PointF pivot, PointF oppositePoint)
		{
			var dx = oppositePoint.X - pivot.X;
			var dy = oppositePoint.Y - pivot.Y;
			return new PointF(pivot.X - dx, pivot.Y - dy);
		}
 
		/**
	   * Return true if c is between a and b.
		*/
 
		private static bool IsBetween(float a, float b, float c)
		{
			return b > a ? c >= a && c <= b : c >= b && c <= a;
		}
 
		/**
		 * Check if two points are on the same side of a given line.
		 * Algorithm from Sedgewick page 350.
		 *
		 * @param x0, y0, x1, y1  The line.
		 * @param px0, py0        First point.
		 * @param px1, py1        Second point.
		 * @return                &lt;0 if points on opposite sides.
		 *                        =0 if one of the points is exactly on the line
		 *                        &gt;0 if points on same side.
		 */
 
		private static int SameSide(float x0, float y0, float x1, float y1, float px0, float py0, float px1, float py1)
		{
			var sameSide = 0;
 
			var dx = x1 - x0;
			var dy = y1 - y0;
			var dx1 = px0 - x0;
			var dy1 = py0 - y0;
			var dx2 = px1 - x1;
			var dy2 = py1 - y1;
 
			// Cross product of the vector from the endpoint of the line to the point
			var c1 = dx * dy1 - dy * dx1;
			var c2 = dx * dy2 - dy * dx2;
 
			// ReSharper disable CompareOfFloatsByEqualityOperator
			if (c1 != 0 && c2 != 0)
			{
				sameSide = c1 < 0 != c2 < 0 ? -1 : 1;
			}
			else if (dx == 0 && dx1 == 0 && dx2 == 0)
			{
				sameSide = !IsBetween(y0, y1, py0) && !IsBetween(y0, y1, py1) ? 1 : 0;
			}
			else if (dy == 0 && dy1 == 0 && dy2 == 0)
			{
				sameSide = !IsBetween(x0, x1, px0) && !IsBetween(x0, x1, px1) ? 1 : 0;
			}
			// ReSharper restore CompareOfFloatsByEqualityOperator
 
			return sameSide;
		}
 
		/**
		 * Check if two line segments intersects. Integer domain.
		 *
		 * @param x0, y0, x1, y1  End points of first line to check.
		 * @param x2, yy, x3, y3  End points of second line to check.
		 * @return                True if the two lines intersects.
		 */
 
		public static bool IsLineIntersectingLine(
			float x0,
			float y0,
			float x1,
			float y1,
			float x2,
			float y2,
			float x3,
			float y3)
		{
			var s1 = SameSide(x0, y0, x1, y1, x2, y2, x3, y3);
			var s2 = SameSide(x2, y2, x3, y3, x0, y0, x1, y1);
 
			return s1 <= 0 && s2 <= 0;
		}
 
 
		public static float GetFactor(float aMin, float aMax, float aValue)
		{
			var vAdjustedValue = aValue - aMin;
			var vRange = aMax - aMin;
 
			if (Math.Abs(vAdjustedValue - vRange) < Epsilon)
			{
				return 1;
			}
 
			return vAdjustedValue / vRange;
		}
 
		public static float GetLinearValue(float aMin, float aMax, float aFactor)
		{
			var d = aMax - aMin;
			d *= aFactor;
			return aMin + d;
		}
	}
}