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();
}
}