#region License
#endregion
#region Using Statements
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Diagnostics;
using
Microsoft.Xna.Framework.Design;
#endregion
namespace
Microsoft.Xna.Framework
{
/// <summary>
/// Describes a sphere in 3D-space for bounding operations.
/// </summary>
[Serializable]
[TypeConverter(
typeof
(BoundingSphereConverter))]
[DebuggerDisplay(
"{DebugDisplayString,nq}"
)]
public
struct
BoundingSphere : IEquatable<BoundingSphere>
{
#region Internal Properties
internal
string
DebugDisplayString
{
get
{
return
string
.Concat(
"Center( "
, Center.DebugDisplayString,
" ) \r\n"
,
"Radius( "
, Radius.ToString(),
" ) "
);
}
}
#endregion
#region Public Fields
/// <summary>
/// The sphere center.
/// </summary>
public
Vector3 Center;
/// <summary>
/// The sphere radius.
/// </summary>
public
float
Radius;
#endregion
#region Public Constructors
/// <summary>
/// Constructs a bounding sphere with the specified center and radius.
/// </summary>
/// <param name="center">The sphere center.</param>
/// <param name="radius">The sphere radius.</param>
public
BoundingSphere(Vector3 center,
float
radius)
{
this
.Center = center;
this
.Radius = radius;
}
#endregion
#region Public Methods
/// <summary>
/// Creates a new <see cref="BoundingSphere"/> that contains a transformation of translation and scale from this sphere by the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The transformation <see cref="Matrix"/>.</param>
/// <returns>Transformed <see cref="BoundingSphere"/>.</returns>
public
BoundingSphere Transform(Matrix matrix)
{
BoundingSphere sphere =
new
BoundingSphere();
sphere.Center = Vector3.Transform(
this
.Center, matrix);
sphere.Radius =
this
.Radius *
(
(
float
) Math.Sqrt((
double
) Math.Max(
((matrix.M11 * matrix.M11) + (matrix.M12 * matrix.M12)) + (matrix.M13 * matrix.M13),
Math.Max(
((matrix.M21 * matrix.M21) + (matrix.M22 * matrix.M22)) + (matrix.M23 * matrix.M23),
((matrix.M31 * matrix.M31) + (matrix.M32 * matrix.M32)) + (matrix.M33 * matrix.M33))
)
)
);
return
sphere;
}
/// <summary>
/// Creates a new <see cref="BoundingSphere"/> that contains a transformation of translation and scale from this sphere by the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The transformation <see cref="Matrix"/>.</param>
/// <param name="result">Transformed <see cref="BoundingSphere"/> as an output parameter.</param>
public
void
Transform(
ref
Matrix matrix,
out
BoundingSphere result)
{
result.Center = Vector3.Transform(
this
.Center, matrix);
result.Radius =
this
.Radius *
(
(
float
) Math.Sqrt((
double
) Math.Max(
((matrix.M11 * matrix.M11) + (matrix.M12 * matrix.M12)) + (matrix.M13 * matrix.M13),
Math.Max(
((matrix.M21 * matrix.M21) + (matrix.M22 * matrix.M22)) + (matrix.M23 * matrix.M23),
((matrix.M31 * matrix.M31) + (matrix.M32 * matrix.M32)) + (matrix.M33 * matrix.M33))
)
)
);
}
/// <summary>
/// Test if a bounding box is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public
void
Contains(
ref
BoundingBox box,
out
ContainmentType result)
{
result =
this
.Contains(box);
}
/// <summary>
/// Test if a sphere is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public
void
Contains(
ref
BoundingSphere sphere,
out
ContainmentType result)
{
result = Contains(sphere);
}
/// <summary>
/// Test if a point is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="point">The vector in 3D-space for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public
void
Contains(
ref
Vector3 point,
out
ContainmentType result)
{
result = Contains(point);
}
/// <summary>
/// Test if a bounding box is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <returns>The containment type.</returns>
public
ContainmentType Contains(BoundingBox box)
{
bool
inside =
true
;
foreach
(Vector3 corner
in
box.GetCorners())
{
if
(
this
.Contains(corner) == ContainmentType.Disjoint)
{
inside =
false
;
break
;
}
}
if
(inside)
{
return
ContainmentType.Contains;
}
double
dmin = 0;
if
(Center.X < box.Min.X)
{
dmin += (Center.X - box.Min.X) * (Center.X - box.Min.X);
}
else
if
(Center.X > box.Max.X)
{
dmin += (Center.X - box.Max.X) * (Center.X - box.Max.X);
}
if
(Center.Y < box.Min.Y)
{
dmin += (Center.Y - box.Min.Y) * (Center.Y - box.Min.Y);
}
else
if
(Center.Y > box.Max.Y)
{
dmin += (Center.Y - box.Max.Y) * (Center.Y - box.Max.Y);
}
if
(Center.Z < box.Min.Z)
{
dmin += (Center.Z - box.Min.Z) * (Center.Z - box.Min.Z);
}
else
if
(Center.Z > box.Max.Z)
{
dmin += (Center.Z - box.Max.Z) * (Center.Z - box.Max.Z);
}
if
(dmin <= Radius * Radius)
{
return
ContainmentType.Intersects;
}
return
ContainmentType.Disjoint;
}
/// <summary>
/// Test if a frustum is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public
ContainmentType Contains(BoundingFrustum frustum)
{
bool
inside =
true
;
Vector3[] corners = frustum.GetCorners();
foreach
(Vector3 corner
in
corners)
{
if
(
this
.Contains(corner) == ContainmentType.Disjoint)
{
inside =
false
;
break
;
}
}
if
(inside)
{
return
ContainmentType.Contains;
}
double
dmin = 0;
if
(dmin <= Radius * Radius)
{
return
ContainmentType.Intersects;
}
return
ContainmentType.Disjoint;
}
/// <summary>
/// Test if a sphere is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <returns>The containment type.</returns>
public
ContainmentType Contains(BoundingSphere sphere)
{
float
sqDistance;
Vector3.DistanceSquared(
ref
sphere.Center,
ref
Center,
out
sqDistance);
if
(sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius))
{
return
ContainmentType.Disjoint;
}
else
if
(sqDistance <= (Radius * sphere.Radius) * (Radius - sphere.Radius))
{
return
ContainmentType.Contains;
}
return
ContainmentType.Intersects;
}
/// <summary>
/// Test if a point is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="point">The vector in 3D-space for testing.</param>
/// <returns>The containment type.</returns>
public
ContainmentType Contains(Vector3 point)
{
float
sqRadius = Radius * Radius;
float
sqDistance;
Vector3.DistanceSquared(
ref
point,
ref
Center,
out
sqDistance);
if
(sqDistance > sqRadius)
{
return
ContainmentType.Disjoint;
}
else
if
(sqDistance < sqRadius)
{
return
ContainmentType.Contains;
}
return
ContainmentType.Intersects;
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="BoundingSphere"/>.
/// </summary>
/// <param name="other">The <see cref="BoundingSphere"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public
bool
Equals(BoundingSphere other)
{
return
(
this
.Center == other.Center &&
MathHelper.WithinEpsilon(
this
.Radius, other.Radius) );
}
#endregion
#region Public Static Methods
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingBox"/>.
/// </summary>
/// <param name="box">The box to create the sphere from.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public
static
BoundingSphere CreateFromBoundingBox(BoundingBox box)
{
BoundingSphere result;
CreateFromBoundingBox(
ref
box,
out
result);
return
result;
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingBox"/>.
/// </summary>
/// <param name="box">The box to create the sphere from.</param>
/// <param name="result">The new <see cref="BoundingSphere"/> as an output parameter.</param>
public
static
void
CreateFromBoundingBox(
ref
BoundingBox box,
out
BoundingSphere result)
{
Vector3 center =
new
Vector3(
(box.Min.X + box.Max.X) / 2.0f,
(box.Min.Y + box.Max.Y) / 2.0f,
(box.Min.Z + box.Max.Z) / 2.0f
);
float
radius = Vector3.Distance(center, box.Max);
result =
new
BoundingSphere(center, radius);
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="frustum">The frustum to create the sphere from.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public
static
BoundingSphere CreateFromFrustum(BoundingFrustum frustum)
{
return
CreateFromPoints(frustum.GetCorners());
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified list of points in 3D-space.
/// </summary>
/// <param name="points">List of point to create the sphere from.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public
static
BoundingSphere CreateFromPoints(IEnumerable<Vector3> points)
{
if
(points ==
null
)
{
throw
new
ArgumentNullException(
"points"
);
}
Vector3 minx =
new
Vector3(
float
.MaxValue,
float
.MaxValue,
float
.MaxValue);
Vector3 maxx = -minx;
Vector3 miny = minx;
Vector3 maxy = -minx;
Vector3 minz = minx;
Vector3 maxz = -minx;
int
numPoints = 0;
foreach
(Vector3 pt
in
points)
{
numPoints += 1;
if
(pt.X < minx.X)
{
minx = pt;
}
if
(pt.X > maxx.X)
{
maxx = pt;
}
if
(pt.Y < miny.Y)
{
miny = pt;
}
if
(pt.Y > maxy.Y)
{
maxy = pt;
}
if
(pt.Z < minz.Z)
{
minz = pt;
}
if
(pt.Z > maxz.Z)
{
maxz = pt;
}
}
if
(numPoints == 0)
{
throw
new
ArgumentException(
"You should have at least one point in points."
);
}
float
sqDistX = Vector3.DistanceSquared(maxx, minx);
float
sqDistY = Vector3.DistanceSquared(maxy, miny);
float
sqDistZ = Vector3.DistanceSquared(maxz, minz);
Vector3 min = minx;
Vector3 max = maxx;
if
(sqDistY > sqDistX && sqDistY > sqDistZ)
{
max = maxy;
min = miny;
}
if
(sqDistZ > sqDistX && sqDistZ > sqDistY)
{
max = maxz;
min = minz;
}
Vector3 center = (min + max) * 0.5f;
float
radius = Vector3.Distance(max, center);
float
sqRadius = radius * radius;
foreach
(Vector3 pt
in
points)
{
Vector3 diff = (pt - center);
float
sqDist = diff.LengthSquared();
if
(sqDist > sqRadius)
{
float
distance = (
float
) Math.Sqrt(sqDist);
Vector3 direction = diff / distance;
Vector3 G = center - radius * direction;
center = (G + pt) / 2;
radius = Vector3.Distance(pt, center);
sqRadius = radius * radius;
}
}
return
new
BoundingSphere(center, radius);
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain two spheres.
/// </summary>
/// <param name="original">First sphere.</param>
/// <param name="additional">Second sphere.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public
static
BoundingSphere CreateMerged(BoundingSphere original, BoundingSphere additional)
{
BoundingSphere result;
CreateMerged(
ref
original,
ref
additional,
out
result);
return
result;
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain two spheres.
/// </summary>
/// <param name="original">First sphere.</param>
/// <param name="additional">Second sphere.</param>
/// <param name="result">The new <see cref="BoundingSphere"/> as an output parameter.</param>
public
static
void
CreateMerged(
ref
BoundingSphere original,
ref
BoundingSphere additional,
out
BoundingSphere result
) {
Vector3 ocenterToaCenter = Vector3.Subtract(additional.Center, original.Center);
float
distance = ocenterToaCenter.Length();
if
(distance <= original.Radius + additional.Radius)
{
if
(distance <= original.Radius - additional.Radius)
{
result = original;
return
;
}
if
(distance <= additional.Radius - original.Radius)
{
result = additional;
return
;
}
}
float
leftRadius = Math.Max(original.Radius - distance, additional.Radius);
float
Rightradius = Math.Max(original.Radius + distance, additional.Radius);
ocenterToaCenter = ocenterToaCenter +
(
((leftRadius - Rightradius) / (2 * ocenterToaCenter.Length()))
* ocenterToaCenter
);
result =
new
BoundingSphere();
result.Center = original.Center + ocenterToaCenter;
result.Radius = (leftRadius + Rightradius) / 2;
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <returns><c>true</c> if <see cref="BoundingBox"/> intersects with this sphere; <c>false</c> otherwise.</returns>
public
bool
Intersects(BoundingBox box)
{
return
box.Intersects(
this
);
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <param name="result"><c>true</c> if <see cref="BoundingBox"/> intersects with this sphere; <c>false</c> otherwise. As an output parameter.</param>
public
void
Intersects(
ref
BoundingBox box,
out
bool
result)
{
box.Intersects(
ref
this
,
out
result);
}
public
bool
Intersects(BoundingFrustum frustum)
{
return
frustum.Intersects(
this
);
}
/// <summary>
/// Gets whether or not the other <see cref="BoundingSphere"/> intersects with this sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <returns><c>true</c> if other <see cref="BoundingSphere"/> intersects with this sphere; <c>false</c> otherwise.</returns>
public
bool
Intersects(BoundingSphere sphere)
{
bool
result;
Intersects(
ref
sphere,
out
result);
return
result;
}
/// <summary>
/// Gets whether or not the other <see cref="BoundingSphere"/> intersects with this sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <param name="result"><c>true</c> if other <see cref="BoundingSphere"/> intersects with this sphere; <c>false</c> otherwise. As an output parameter.</param>
public
void
Intersects(
ref
BoundingSphere sphere,
out
bool
result)
{
float
sqDistance;
Vector3.DistanceSquared(
ref
sphere.Center,
ref
Center,
out
sqDistance);
result = !(sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius));
}
/// <summary>
/// Gets whether or not a specified <see cref="Ray"/> intersects with this sphere.
/// </summary>
/// <param name="ray">The ray for testing.</param>
/// <returns>Distance of ray intersection or <c>null</c> if there is no intersection.</returns>
public
float
? Intersects(Ray ray)
{
return
ray.Intersects(
this
);
}
/// <summary>
/// Gets whether or not a specified <see cref="Ray"/> intersects with this sphere.
/// </summary>
/// <param name="ray">The ray for testing.</param>
/// <param name="result">Distance of ray intersection or <c>null</c> if there is no intersection as an output parameter.</param>
public
void
Intersects(
ref
Ray ray,
out
float
? result)
{
ray.Intersects(
ref
this
,
out
result);
}
/// <summary>
/// Gets whether or not a specified <see cref="Plane"/> intersects with this sphere.
/// </summary>
/// <param name="plane">The plane for testing.</param>
/// <returns>Type of intersection.</returns>
public
PlaneIntersectionType Intersects(Plane plane)
{
PlaneIntersectionType result =
default
(PlaneIntersectionType);
this
.Intersects(
ref
plane,
out
result);
return
result;
}
/// <summary>
/// Gets whether or not a specified <see cref="Plane"/> intersects with this sphere.
/// </summary>
/// <param name="plane">The plane for testing.</param>
/// <param name="result">Type of intersection as an output parameter.</param>
public
void
Intersects(
ref
Plane plane,
out
PlaneIntersectionType result)
{
float
distance =
default
(
float
);
Vector3.Dot(
ref
plane.Normal,
ref
this
.Center,
out
distance);
distance += plane.D;
if
(distance >
this
.Radius)
{
result = PlaneIntersectionType.Front;
}
else
if
(distance < -
this
.Radius)
{
result = PlaneIntersectionType.Back;
}
else
{
result = PlaneIntersectionType.Intersecting;
}
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares whether current instance is equal to specified <see cref="Object"/>.
/// </summary>
/// <param name="obj">The <see cref="Object"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public
override
bool
Equals(
object
obj)
{
if
(obj
is
BoundingSphere)
{
return
this
.Equals((BoundingSphere)obj);
}
return
false
;
}
/// <summary>
/// Gets the hash code of this <see cref="BoundingSphere"/>.
/// </summary>
/// <returns>Hash code of this <see cref="BoundingSphere"/>.</returns>
public
override
int
GetHashCode()
{
return
this
.Center.GetHashCode() +
this
.Radius.GetHashCode();
}
/// <summary>
/// Returns a <see cref="String"/> representation of this <see cref="BoundingSphere"/> in the format:
/// {Center:[<see cref="Center"/>] Radius:[<see cref="Radius"/>]}
/// </summary>
/// <returns>A <see cref="String"/> representation of this <see cref="BoundingSphere"/>.</returns>
public
override
string
ToString()
{
return
(
"{Center:"
+ Center.ToString() +
" Radius:"
+ Radius.ToString() +
"}"
);
}
/// <summary>
/// Compares whether two <see cref="BoundingSphere"/> instances are equal.
/// </summary>
/// <param name="a"><see cref="BoundingSphere"/> instance on the left of the equal sign.</param>
/// <param name="b"><see cref="BoundingSphere"/> instance on the right of the equal sign.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public
static
bool
operator
==(BoundingSphere a, BoundingSphere b)
{
return
a.Equals(b);
}
/// <summary>
/// Compares whether two <see cref="BoundingSphere"/> instances are not equal.
/// </summary>
/// <param name="a"><see cref="BoundingSphere"/> instance on the left of the not equal sign.</param>
/// <param name="b"><see cref="BoundingSphere"/> instance on the right of the not equal sign.</param>
/// <returns><c>true</c> if the instances are not equal; <c>false</c> otherwise.</returns>
public
static
bool
operator
!=(BoundingSphere a, BoundingSphere b)
{
return
!a.Equals(b);
}
#endregion
}
}