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 /// /// A two dimensional circle defined by a centre and a radius . /// /// /// /// An is categorized by the set of all points in a plane that are at equal distance from /// the /// centre. /// /// /// /// [DataContract] public struct CircleF : IEquatable, IEquatableByRef, IShapeF { /// /// The centre position of this . /// [DataMember] public Vector2 Center; /// /// The distance from the point to any point on the boundary of this . /// [DataMember] public float Radius; /// /// Gets or sets the position of the circle. /// public Vector2 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); } } /// /// Gets the distance from a point to the opposite point, both on the boundary of this . /// public float Diameter => 2 * Radius; /// /// Gets the distance around the boundary of this . /// public float Circumference => 2 * MathHelper.Pi * Radius; /// /// Initializes a new instance of the structure from the specified centre /// and the radius . /// /// The centre point. /// The radius. public CircleF(Vector2 center, float radius) { Center = center; Radius = radius; } /// /// Computes the bounding from a minimum and maximum /// . /// /// The minimum point. /// The maximum point. /// The resulting circle. public static void CreateFrom(Vector2 minimum, Vector2 maximum, out CircleF result) { result.Center = new Vector2((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; } /// /// Computes the bounding from a minimum and maximum /// . /// /// The minimum point. /// The maximum point. /// An . public static CircleF CreateFrom(Vector2 minimum, Vector2 maximum) { CircleF result; CreateFrom(minimum, maximum, out result); return result; } /// /// Computes the bounding from a list of structures. /// /// The points. /// The resulting circle. public static void CreateFrom(IReadOnlyList 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 Vector2(float.MaxValue, float.MaxValue); var maximum = new Vector2(float.MinValue, float.MinValue); // ReSharper disable once ForCanBeConvertedToForeach for (var index = points.Count - 1; index >= 0; --index) { var point = points[index]; minimum = MathExtended.CalculateMinimumVector2(minimum, point); maximum = MathExtended.CalculateMaximumVector2(maximum, point); } CreateFrom(minimum, maximum, out result); } /// /// Computes the bounding from a list of structures. /// /// The points. /// An . public static CircleF CreateFrom(IReadOnlyList points) { CircleF result; CreateFrom(points, out result); return result; } /// /// Determines whether the two specified structures intersect. /// /// The first circle. /// The second circle. /// /// true if the intersects with the ; otherwise, false. /// 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; } /// /// Determines whether the two specified structures intersect. /// /// The first circle. /// The second circle. /// /// true if the intersects with the ; otherwise, false. /// public static bool Intersects(CircleF first, CircleF second) { return Intersects(ref first, ref second); } /// /// Determines whether the specified intersects with this . /// /// The circle. /// /// true if the intersects with this ; otherwise, /// false. /// public bool Intersects(ref CircleF circle) { return Intersects(ref this, ref circle); } /// /// Determines whether the specified intersects with this . /// /// The circle. /// /// true if the intersects with this ; otherwise, /// false. /// public bool Intersects(CircleF circle) { return Intersects(ref this, ref circle); } /// /// Determines whether the specified and structures intersect. /// /// The circle. /// The rectangle. /// /// true if the intersects with the ; otherwise, false /// . /// 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; } /// /// Determines whether the specified and structures intersect. /// /// The circle. /// The rectangle. /// /// true if the intersects with the ; otherwise, false /// . /// public static bool Intersects(CircleF circle, BoundingRectangle rectangle) { return Intersects(ref circle, ref rectangle); } /// /// Determines whether the specified intersects with this . /// /// The rectangle. /// /// true if the intersects with this ; otherwise, /// false. /// public bool Intersects(ref BoundingRectangle rectangle) { return Intersects(ref this, ref rectangle); } /// /// Determines whether the specified intersects with this . /// /// The rectangle. /// /// true if the intersects with this ; otherwise, /// false. /// public bool Intersects(BoundingRectangle rectangle) { return Intersects(ref this, ref rectangle); } /// /// Determines whether the specified contains the specified /// . /// /// The circle. /// The point. /// /// true if the contains the ; otherwise, /// false. /// public static bool Contains(ref CircleF circle, Vector2 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; } /// /// Determines whether the specified contains the specified /// . /// /// The circle. /// The point. /// /// true if the contains the ; otherwise, /// false. /// public static bool Contains(CircleF circle, Vector2 point) { return Contains(ref circle, point); } /// /// Determines whether this contains the specified . /// /// The point. /// /// true if this contains the ; otherwise, /// false. /// public bool Contains(Vector2 point) { return Contains(ref this, point); } /// /// Computes the closest on this to a specified /// . /// /// The point. /// The closest on this to the . public Vector2 ClosestPointTo(Vector2 point) { var distanceVector = point - Center; var lengthSquared = distanceVector.Dot(distanceVector); if (lengthSquared <= Radius * Radius) return point; distanceVector.Normalize(); return Center + Radius * distanceVector; } /// /// Computes the on the boundary of of this using the specified angle. /// /// The angle in radians. /// The on the boundary of this using . public Vector2 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 Vector2 GetPointAlongEdge(float angle) { return Center + new Vector2(Radius * (float) Math.Cos(angle), Radius * (float) Math.Sin(angle)); } /// /// Compares two structures. The result specifies whether the values of the /// and fields of the two structures /// are equal. /// /// The first circle. /// The second circle. /// /// true if the and fields of the two /// structures are equal; otherwise, false. /// public static bool operator ==(CircleF first, CircleF second) { return first.Equals(ref second); } /// /// Compares two structures. The result specifies whether the values of the /// and fields of the two structures /// are unequal. /// /// The first circle. /// The second circle. /// /// true if the and fields of the two /// structures are unequal; otherwise, false. /// public static bool operator !=(CircleF first, CircleF second) { return !(first == second); } /// /// Indicates whether this is equal to another . /// /// The circle. /// /// true if this is equal to the ; otherwise, false. /// public bool Equals(CircleF circle) { return Equals(ref circle); } /// /// Indicates whether this is equal to another . /// /// The bounding rectangle. /// /// true if this is equal to the ; /// otherwise,false. /// public bool Equals(ref CircleF circle) { // ReSharper disable once CompareOfFloatsByEqualityOperator return circle.Center == Center && circle.Radius == Radius; } /// /// Returns a value indicating whether this is equal to a specified object. /// /// The object to make the comparison with. /// /// true if this is equal to ; otherwise, false. /// public override bool Equals(object obj) { return obj is CircleF && Equals((CircleF) obj); } /// /// Returns a hash code of this suitable for use in hashing algorithms and data /// structures like a hash table. /// /// /// A hash code of this . /// public override int GetHashCode() { unchecked { return (Center.GetHashCode() * 397) ^ Radius.GetHashCode(); } } /// /// Performs an explicit conversion from a to a . /// /// The circle. /// /// The resulting . /// 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); } /// /// Performs a conversion from a specified to a . /// /// /// The resulting . /// public Rectangle ToRectangle() { return (Rectangle)this; } /// /// Performs an explicit conversion from a to a . /// /// The rectangle. /// /// The resulting . /// public static explicit operator CircleF(Rectangle rectangle) { var halfWidth = rectangle.Width / 2; var halfHeight = rectangle.Height / 2; return new CircleF(new Vector2(rectangle.X + halfWidth, rectangle.Y + halfHeight), halfWidth > halfHeight ? halfWidth : halfHeight); } /// /// Performs an explicit conversion from a to a . /// /// The circle. /// /// The resulting . /// 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); } /// /// Performs a conversion from a specified to a . /// /// /// The resulting . /// public RectangleF ToRectangleF() { return (RectangleF)this; } /// /// Performs an explicit conversion from a to a . /// /// The rectangle. /// /// The resulting . /// public static explicit operator CircleF(RectangleF rectangle) { var halfWidth = rectangle.Width * 0.5f; var halfHeight = rectangle.Height * 0.5f; return new CircleF(new Vector2(rectangle.X + halfWidth, rectangle.Y + halfHeight), halfWidth > halfHeight ? halfWidth : halfHeight); } /// /// Returns a that represents this . /// /// /// A that represents this . /// public override string ToString() { return $"Centre: {Center}, Radius: {Radius}"; } internal string DebugDisplayString => ToString(); } }