diff options
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended/Math/CircleF.cs')
-rw-r--r-- | Plugins/MonoGame.Extended/source/MonoGame.Extended/Math/CircleF.cs | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended/Math/CircleF.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended/Math/CircleF.cs new file mode 100644 index 0000000..70f9307 --- /dev/null +++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended/Math/CircleF.cs @@ -0,0 +1,519 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Microsoft.Xna.Framework; + +namespace MonoGame.Extended +{ + // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.3; Bounding Volumes - Spheres. pg 88 + + /// <summary> + /// A two dimensional circle defined by a centre <see cref="Point2" /> and a radius <see cref="float" />. + /// </summary> + /// <remarks> + /// <para> + /// An <see cref="CircleF" /> is categorized by the set of all points in a plane that are at equal distance from + /// the + /// centre. + /// </para> + /// </remarks> + /// <seealso cref="IEquatable{T}" /> + /// <seealso cref="IEquatableByRef{T}" /> + [DataContract] + public struct CircleF : IEquatable<CircleF>, IEquatableByRef<CircleF>, IShapeF + { + /// <summary> + /// The centre position of this <see cref="CircleF" />. + /// </summary> + [DataMember] public Point2 Center; + + /// <summary> + /// The distance from the <see cref="Center" /> point to any point on the boundary of this <see cref="CircleF" />. + /// </summary> + [DataMember] public float Radius; + + /// <summary> + /// Gets or sets the position of the circle. + /// </summary> + public Point2 Position + { + get => Center; + set => Center = value; + } + + public RectangleF BoundingRectangle + { + get + { + var minX = Center.X - Radius; + var minY = Center.Y - Radius; + return new RectangleF(minX, minY, Diameter, Diameter); + } + } + + /// <summary> + /// Gets the distance from a point to the opposite point, both on the boundary of this <see cref="CircleF" />. + /// </summary> + public float Diameter => 2 * Radius; + + /// <summary> + /// Gets the distance around the boundary of this <see cref="CircleF" />. + /// </summary> + public float Circumference => 2 * MathHelper.Pi * Radius; + + /// <summary> + /// Initializes a new instance of the <see cref="CircleF" /> structure from the specified centre + /// <see cref="Point2" /> and the radius <see cref="float" />. + /// </summary> + /// <param name="center">The centre point.</param> + /// <param name="radius">The radius.</param> + public CircleF(Point2 center, float radius) + { + Center = center; + Radius = radius; + } + + /// <summary> + /// Computes the bounding <see cref="CircleF" /> from a minimum <see cref="Point2" /> and maximum + /// <see cref="Point2" />. + /// </summary> + /// <param name="minimum">The minimum point.</param> + /// <param name="maximum">The maximum point.</param> + /// <param name="result">The resulting circle.</param> + public static void CreateFrom(Point2 minimum, Point2 maximum, out CircleF result) + { + result.Center = new Point2((maximum.X + minimum.X) * 0.5f, (maximum.Y + minimum.Y) * 0.5f); + var distanceVector = maximum - minimum; + result.Radius = distanceVector.X > distanceVector.Y ? distanceVector.X * 0.5f : distanceVector.Y * 0.5f; + } + + /// <summary> + /// Computes the bounding <see cref="CircleF" /> from a minimum <see cref="Point2" /> and maximum + /// <see cref="Point2" />. + /// </summary> + /// <param name="minimum">The minimum point.</param> + /// <param name="maximum">The maximum point.</param> + /// <returns>An <see cref="CircleF" />.</returns> + public static CircleF CreateFrom(Point2 minimum, Point2 maximum) + { + CircleF result; + CreateFrom(minimum, maximum, out result); + return result; + } + + /// <summary> + /// Computes the bounding <see cref="CircleF" /> from a list of <see cref="Point2" /> structures. + /// </summary> + /// <param name="points">The points.</param> + /// <param name="result">The resulting circle.</param> + public static void CreateFrom(IReadOnlyList<Point2> points, out CircleF result) + { + // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.3; Bounding Volumes - Spheres. pg 89-90 + + if (points == null || points.Count == 0) + { + result = default(CircleF); + return; + } + + var minimum = new Point2(float.MaxValue, float.MaxValue); + var maximum = new Point2(float.MinValue, float.MinValue); + + // ReSharper disable once ForCanBeConvertedToForeach + for (var index = points.Count - 1; index >= 0; --index) + { + var point = points[index]; + minimum = Point2.Minimum(minimum, point); + maximum = Point2.Maximum(maximum, point); + } + + CreateFrom(minimum, maximum, out result); + } + + /// <summary> + /// Computes the bounding <see cref="CircleF" /> from a list of <see cref="Point2" /> structures. + /// </summary> + /// <param name="points">The points.</param> + /// <returns>An <see cref="CircleF" />.</returns> + public static CircleF CreateFrom(IReadOnlyList<Point2> points) + { + CircleF result; + CreateFrom(points, out result); + return result; + } + + /// <summary> + /// Determines whether the two specified <see cref="CircleF" /> structures intersect. + /// </summary> + /// <param name="first">The first circle.</param> + /// <param name="second">The second circle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="first" /> intersects with the <see cref="second" />; otherwise, <c>false</c>. + /// </returns> + public static bool Intersects(ref CircleF first, ref CircleF second) + { + // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.3; Bounding Volumes - Spheres. pg 88 + + // Calculate squared distance between centers + var distanceVector = first.Center - second.Center; + var distanceSquared = distanceVector.Dot(distanceVector); + var radiusSum = first.Radius + second.Radius; + return distanceSquared <= radiusSum * radiusSum; + } + + /// <summary> + /// Determines whether the two specified <see cref="CircleF" /> structures intersect. + /// </summary> + /// <param name="first">The first circle.</param> + /// <param name="second">The second circle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="first" /> intersects with the <see cref="second" />; otherwise, <c>false</c>. + /// </returns> + public static bool Intersects(CircleF first, CircleF second) + { + return Intersects(ref first, ref second); + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="CircleF" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="circle" /> intersects with this <see cref="CircleF" />; otherwise, + /// <c>false</c>. + /// </returns> + public bool Intersects(ref CircleF circle) + { + return Intersects(ref this, ref circle); + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="CircleF" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="circle" /> intersects with this <see cref="CircleF" />; otherwise, + /// <c>false</c>. + /// </returns> + public bool Intersects(CircleF circle) + { + return Intersects(ref this, ref circle); + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> and <see cref="BoundingRectangle" /> structures intersect. + /// </summary> + /// <param name="circle">The circle.</param> + /// <param name="rectangle">The rectangle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="circle" /> intersects with the <see cref="rectangle" />; otherwise, <c>false</c> + /// . + /// </returns> + public static bool Intersects(ref CircleF circle, ref BoundingRectangle rectangle) + { + // Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.25; Basic Primitives Test - Testing Sphere Against AABB. pg 165-166 + + // Compute squared distance between sphere center and AABB boundary + var distanceSquared = rectangle.SquaredDistanceTo(circle.Center); + // Circle and AABB intersect if the (squared) distance between the AABB's boundary and the circle is less than the (squared) circle's radius + return distanceSquared <= circle.Radius * circle.Radius; + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> and <see cref="BoundingRectangle" /> structures intersect. + /// </summary> + /// <param name="circle">The circle.</param> + /// <param name="rectangle">The rectangle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="circle" /> intersects with the <see cref="rectangle" />; otherwise, <c>false</c> + /// . + /// </returns> + public static bool Intersects(CircleF circle, BoundingRectangle rectangle) + { + return Intersects(ref circle, ref rectangle); + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="BoundingRectangle" />. + /// </summary> + /// <param name="rectangle">The rectangle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="rectangle" /> intersects with this <see cref="CircleF" />; otherwise, + /// <c>false</c>. + /// </returns> + public bool Intersects(ref BoundingRectangle rectangle) + { + return Intersects(ref this, ref rectangle); + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> intersects with this <see cref="BoundingRectangle" />. + /// </summary> + /// <param name="rectangle">The rectangle.</param> + /// <returns> + /// <c>true</c> if the <paramref name="rectangle" /> intersects with this <see cref="CircleF" />; otherwise, + /// <c>false</c>. + /// </returns> + public bool Intersects(BoundingRectangle rectangle) + { + return Intersects(ref this, ref rectangle); + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> contains the specified + /// <see cref="Point2" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <param name="point">The point.</param> + /// <returns> + /// <c>true</c> if the <paramref name="circle" /> contains the <paramref name="point" />; otherwise, + /// <c>false</c>. + /// </returns> + public static bool Contains(ref CircleF circle, Point2 point) + { + var dx = circle.Center.X - point.X; + var dy = circle.Center.Y - point.Y; + var d2 = dx * dx + dy * dy; + var r2 = circle.Radius * circle.Radius; + return d2 <= r2; + } + + /// <summary> + /// Determines whether the specified <see cref="CircleF" /> contains the specified + /// <see cref="Point2" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <param name="point">The point.</param> + /// <returns> + /// <c>true</c> if the <paramref name="circle" /> contains the <paramref name="point" />; otherwise, + /// <c>false</c>. + /// </returns> + public static bool Contains(CircleF circle, Point2 point) + { + return Contains(ref circle, point); + } + + /// <summary> + /// Determines whether this <see cref="CircleF" /> contains the specified <see cref="Point2" />. + /// </summary> + /// <param name="point">The point.</param> + /// <returns> + /// <c>true</c> if this <see cref="BoundingRectangle" /> contains the <paramref name="point" />; otherwise, + /// <c>false</c>. + /// </returns> + public bool Contains(Point2 point) + { + return Contains(ref this, point); + } + + /// <summary> + /// Computes the closest <see cref="Point2" /> on this <see cref="CircleF" /> to a specified + /// <see cref="Point2" />. + /// </summary> + /// <param name="point">The point.</param> + /// <returns>The closest <see cref="Point2" /> on this <see cref="CircleF" /> to the <paramref name="point" />.</returns> + public Point2 ClosestPointTo(Point2 point) + { + var distanceVector = point - Center; + var lengthSquared = distanceVector.Dot(distanceVector); + if (lengthSquared <= Radius * Radius) + return point; + distanceVector.Normalize(); + return Center + Radius * distanceVector; + } + + /// <summary> + /// Computes the <see cref="Point2" /> on the boundary of of this <see cref="CircleF" /> using the specified angle. + /// </summary> + /// <param name="angle">The angle in radians.</param> + /// <returns>The <see cref="Point2" /> on the boundary of this <see cref="CircleF" /> using <paramref name="angle" />.</returns> + public Point2 BoundaryPointAt(float angle) + { + var direction = new Vector2((float) Math.Cos(angle), (float) Math.Sin(angle)); + return Center + Radius * direction; + } + + [Obsolete("Circle.GetPointAlongEdge() may be removed in the future. Use BoundaryPointAt() instead.")] + public Point2 GetPointAlongEdge(float angle) + { + return Center + new Vector2(Radius * (float) Math.Cos(angle), Radius * (float) Math.Sin(angle)); + } + + /// <summary> + /// Compares two <see cref="CircleF" /> structures. The result specifies whether the values of the + /// <see cref="Center" /> and <see cref="Radius" /> fields of the two <see cref="CircleF" /> structures + /// are equal. + /// </summary> + /// <param name="first">The first circle.</param> + /// <param name="second">The second circle.</param> + /// <returns> + /// <c>true</c> if the <see cref="Center" /> and <see cref="Radius" /> fields of the two + /// <see cref="BoundingRectangle" /> structures are equal; otherwise, <c>false</c>. + /// </returns> + public static bool operator ==(CircleF first, CircleF second) + { + return first.Equals(ref second); + } + + /// <summary> + /// Compares two <see cref="CircleF" /> structures. The result specifies whether the values of the + /// <see cref="Center" /> and <see cref="Radius" /> fields of the two <see cref="CircleF" /> structures + /// are unequal. + /// </summary> + /// <param name="first">The first circle.</param> + /// <param name="second">The second circle.</param> + /// <returns> + /// <c>true</c> if the <see cref="Center" /> and <see cref="Radius" /> fields of the two + /// <see cref="CircleF" /> structures are unequal; otherwise, <c>false</c>. + /// </returns> + public static bool operator !=(CircleF first, CircleF second) + { + return !(first == second); + } + + /// <summary> + /// Indicates whether this <see cref="CircleF" /> is equal to another <see cref="CircleF" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <returns> + /// <c>true</c> if this <see cref="CircleF" /> is equal to the <paramref name="circle" />; otherwise, <c>false</c>. + /// </returns> + public bool Equals(CircleF circle) + { + return Equals(ref circle); + } + + /// <summary> + /// Indicates whether this <see cref="CircleF" /> is equal to another <see cref="CircleF" />. + /// </summary> + /// <param name="circle">The bounding rectangle.</param> + /// <returns> + /// <c>true</c> if this <see cref="CircleF" /> is equal to the <paramref name="circle" />; + /// otherwise,<c>false</c>. + /// </returns> + public bool Equals(ref CircleF circle) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + return circle.Center == Center && circle.Radius == Radius; + } + + /// <summary> + /// Returns a value indicating whether this <see cref="CircleF" /> is equal to a specified object. + /// </summary> + /// <param name="obj">The object to make the comparison with.</param> + /// <returns> + /// <c>true</c> if this <see cref="CircleF" /> is equal to <paramref name="obj" />; otherwise, <c>false</c>. + /// </returns> + public override bool Equals(object obj) + { + return obj is CircleF && Equals((CircleF) obj); + } + + /// <summary> + /// Returns a hash code of this <see cref="CircleF" /> suitable for use in hashing algorithms and data + /// structures like a hash table. + /// </summary> + /// <returns> + /// A hash code of this <see cref="CircleF" />. + /// </returns> + public override int GetHashCode() + { + unchecked + { + return (Center.GetHashCode() * 397) ^ Radius.GetHashCode(); + } + } + + /// <summary> + /// Performs an explicit conversion from a <see cref="CircleF" /> to a <see cref="Rectangle" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <returns> + /// The resulting <see cref="Rectangle" />. + /// </returns> + public static explicit operator Rectangle(CircleF circle) + { + var diameter = (int) circle.Diameter; + return new Rectangle((int) (circle.Center.X - circle.Radius), (int) (circle.Center.Y - circle.Radius), + diameter, diameter); + } + + /// <summary> + /// Performs a conversion from a specified <see cref="CircleF" /> to a <see cref="Rectangle" />. + /// </summary> + /// <returns> + /// The resulting <see cref="Rectangle" />. + /// </returns> + public Rectangle ToRectangle() + { + return (Rectangle)this; + } + + /// <summary> + /// Performs an explicit conversion from a <see cref="Rectangle" /> to a <see cref="CircleF" />. + /// </summary> + /// <param name="rectangle">The rectangle.</param> + /// <returns> + /// The resulting <see cref="CircleF" />. + /// </returns> + public static explicit operator CircleF(Rectangle rectangle) + { + var halfWidth = rectangle.Width / 2; + var halfHeight = rectangle.Height / 2; + return new CircleF(new Point2(rectangle.X + halfWidth, rectangle.Y + halfHeight), + halfWidth > halfHeight ? halfWidth : halfHeight); + } + + /// <summary> + /// Performs an explicit conversion from a <see cref="CircleF" /> to a <see cref="RectangleF" />. + /// </summary> + /// <param name="circle">The circle.</param> + /// <returns> + /// The resulting <see cref="RectangleF" />. + /// </returns> + public static explicit operator RectangleF(CircleF circle) + { + var diameter = circle.Diameter; + return new RectangleF(circle.Center.X - circle.Radius, circle.Center.Y - circle.Radius, diameter, diameter); + } + + /// <summary> + /// Performs a conversion from a specified <see cref="CircleF" /> to a <see cref="RectangleF" />. + /// </summary> + /// <returns> + /// The resulting <see cref="RectangleF" />. + /// </returns> + public RectangleF ToRectangleF() + { + return (RectangleF)this; + } + + /// <summary> + /// Performs an explicit conversion from a <see cref="RectangleF" /> to a <see cref="CircleF" />. + /// </summary> + /// <param name="rectangle">The rectangle.</param> + /// <returns> + /// The resulting <see cref="CircleF" />. + /// </returns> + public static explicit operator CircleF(RectangleF rectangle) + { + var halfWidth = rectangle.Width * 0.5f; + var halfHeight = rectangle.Height * 0.5f; + return new CircleF(new Point2(rectangle.X + halfWidth, rectangle.Y + halfHeight), + halfWidth > halfHeight ? halfWidth : halfHeight); + } + + /// <summary> + /// Returns a <see cref="string" /> that represents this <see cref="CircleF" />. + /// </summary> + /// <returns> + /// A <see cref="string" /> that represents this <see cref="CircleF" />. + /// </returns> + public override string ToString() + { + return $"Centre: {Center}, Radius: {Radius}"; + } + + internal string DebugDisplayString => ToString(); + } +} |