/Utilities/Datatypes/Plane.cs
C# | 471 lines | 295 code | 33 blank | 143 comment | 20 complexity | 1268be908a6bb440a190a9df5b04dbec MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Diagnostics;
- using System.IO;
- using System.Runtime.InteropServices;
- using Delta.Utilities.Datatypes.Advanced;
- using Delta.Utilities.Helpers;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Datatypes
- {
- /// <summary>
- /// Plane helper struct to manage planes with just a normal
- /// vector and D for the distance. Details can be found at:
- /// http://en.wikipedia.org/wiki/Plane_%28geometry%29
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- [DebuggerDisplay("Plane(Normal={Normal.X}, {Normal.Y}, {Normal.Z}, D={D})")]
- public struct Plane : ISaveLoadBinary, IEquatable<Plane>, IIntersects
- {
- #region Constants
- private const float ZeroTolerance = 0.00001f;
- #endregion
-
- #region Normal (Public)
- /// <summary>
- /// Normal for the plane.
- /// </summary>
- public Vector Normal;
- #endregion
-
- #region Distance (Public)
- /// <summary>
- /// Distance of the plane from the origin.
- /// </summary>
- public float Distance;
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create plane
- /// </summary>
- /// <param name="distance">distance</param>
- /// <param name="normal">normal</param>
- public Plane(Vector normal, float distance)
- {
- Normal = normal;
- Normal.Normalize();
- Distance = distance;
- }
-
- /// <summary>
- /// Create plane
- /// </summary>
- /// <param name="distance">distance</param>
- /// <param name="x">X</param>
- /// <param name="y">Y</param>
- /// <param name="z">Z</param>
- public Plane(float x, float y, float z, float distance)
- {
- Normal = new Vector(x, y, z);
- Normal.Normalize();
- Distance = distance;
- }
-
- /// <summary>
- /// Create plane
- /// </summary>
- /// <param name="point1">point1</param>
- /// <param name="point2">point2</param>
- /// <param name="point3">point3</param>
- public Plane(Vector point1, Vector point2, Vector point3)
- {
- // See http://en.wikipedia.org/wiki/Plane_%28geometry%29 for help.
- Vector diff1 = point2 - point1;
- Vector diff2 = point3 - point1;
-
- Normal = new Vector(
- (diff1.Y * diff2.Z) - (diff1.Z * diff2.Y),
- (diff1.Z * diff2.X) - (diff1.X * diff2.Z),
- (diff1.X * diff2.Y) - (diff1.Y * diff2.X));
- Normal.Normalize();
-
- Distance = -Vector.Dot(Normal, point1);
- }
- #endregion
-
- #region IEquatable<Plane> Members
- /// <summary>
- /// Equals
- /// </summary>
- /// <param name="other">Other</param>
- /// <returns>Value indicating the equality of two vectors</returns>
- public bool Equals(Plane other)
- {
- return Normal == other.Normal &&
- Distance == other.Distance;
- }
- #endregion
-
- #region IIntersects Members
- /// <summary>
- /// Intersects the current object with specified ray and returns true
- /// plus the intersectionPosition if the ray hits this object.
- /// </summary>
- /// <param name="ray">The ray to check with</param>
- /// <param name="intersectionPosition">The intersection vector</param>
- /// <returns>True if the ray intersected with this object</returns>
- public bool Intersects(Ray ray, out Vector intersectionPosition)
- {
- return ray.Intersects(this, out intersectionPosition);
- }
-
- /// <summary>
- /// Intersects the current object with specified plane and returns true
- /// plus the intersectionPosition if the plane intersects this object.
- /// </summary>
- /// <param name="plane">The plane to check with</param>
- /// <param name="intersectionPosition">The intersection vector</param>
- /// <returns>True if the plane intersected with this object</returns>
- public bool Intersects(Plane plane, out Vector intersectionPosition)
- {
- intersectionPosition = Vector.Zero;
- Vector direction = Vector.Cross(Normal, plane.Normal);
-
- // If direction is the zero vector, the planes are parallel
- // Intersects.None
- float denominator = Vector.Dot(direction, direction);
- // Denominator here is an extra check to tell us if they are parallel and
- // coincident. Otherwise we need to divide the point by the denominator.
- if (Math.Abs(denominator) < ZeroTolerance)
- {
- return false;
- }
- Vector temp = Distance * plane.Normal - plane.Distance * Normal;
- Vector point = Vector.Cross(temp, direction);
- intersectionPosition = point;
- return true;
- }
-
- /// <summary>
- /// Intersects the current object with the specified sphere.
- /// </summary>
- /// <param name="sphere">The sphere to check against</param>
- /// <returns>True if the sphere intersected with this object</returns>
- public bool Intersects(BoundingSphere sphere)
- {
- float objectsDistance = Vector.Dot(Normal, sphere.Center);
- objectsDistance += Distance;
- if (objectsDistance > sphere.Radius)
- {
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// Intersects the current object with the specified box.
- /// </summary>
- /// <param name="box">The box to check against</param>
- /// <returns>True if the box intersected with this object</returns>
- public bool Intersects(BoundingBox box)
- {
- //Finding out where are the minimum and maximum points
- Vector minPoint = Vector.Zero;
- Vector maxPoint = Vector.Zero;
- if (Normal.X >= 0)
- {
- maxPoint.X = box.Min.X;
- minPoint.X = box.Max.X;
- }
- else
- {
- maxPoint.X = box.Max.X;
- minPoint.X = box.Min.X;
- }
- if (Normal.Y >= 0)
- {
- maxPoint.Y = box.Min.Y;
- minPoint.Y = box.Max.Y;
- }
- else
- {
- maxPoint.Y = box.Max.Y;
- minPoint.Y = box.Min.Y;
- }
- if (Normal.Z >= 0)
- {
- maxPoint.Z = box.Min.Z;
- minPoint.Z = box.Max.Z;
- }
- else
- {
- maxPoint.Z = box.Max.Z;
- minPoint.Z = box.Min.Z;
- }
- // Calculating the Dot product to swap the point to the Min/max values
- // If Distance < Max or Distance > Min, then its falling somewhere,
- // otherwise it is not intersecting.
- float objectDistance = Vector.Dot(Normal, maxPoint);
- if (objectDistance > Distance)
- {
- return false;
- }
- objectDistance = Vector.Dot(Normal, minPoint);
-
- if (objectDistance < Distance)
- {
- return false;
- }
- return true;
- }
- #endregion
-
- #region ISaveLoadBinary Members
- /// <summary>
- /// Loads the plane from a binary stream (just the normal and distance).
- /// </summary>
- /// <param name="reader">reader</param>
- public void Load(BinaryReader reader)
- {
- Normal.Load(reader);
- Distance = reader.ReadSingle();
- }
-
- /// <summary>
- /// Saves the plane into a binary stream (just the normal and distance).
- /// </summary>
- /// <param name="writer">Writer</param>
- public void Save(BinaryWriter writer)
- {
- Normal.Save(writer);
- writer.Write(Distance);
- }
- #endregion
-
- #region op_Equality (Operator)
- /// <summary>
- /// Check for equality
- /// </summary>
- /// <param name="value1">Value1</param>
- /// <param name="value2">Value2</param>
- /// <returns>True if the values are equal, false otherwise</returns>
- public static bool operator ==(Plane value1, Plane value2)
- {
- return value1.Normal == value2.Normal &&
- value1.Distance == value2.Distance;
- }
- #endregion
-
- #region op_Inequality (Operator)
- /// <summary>
- /// Check for inequality
- /// </summary>
- /// <param name="value1">Value1</param>
- /// <param name="value2">Value2</param>
- /// <returns>True if the values are not equal, false otherwise</returns>
- public static bool operator !=(Plane value1, Plane value2)
- {
- return value1.Normal != value2.Normal ||
- value1.Distance != value2.Distance;
- }
- #endregion
-
- #region DotCoordinate (Public)
- /// <summary>
- /// Dot coordinate
- /// </summary>
- /// <param name="value">Value</param>
- /// <returns>Result of the dot calculation.</returns>
- public float DotCoordinate(Vector value)
- {
- float resultMultiply;
- Vector.Dot(ref Normal, ref value, out resultMultiply);
- return resultMultiply + Distance;
- }
-
- /// <summary>
- /// Dot coordinate
- /// </summary>
- /// <param name="result">result</param>
- /// <param name="value">value</param>
- public void DotCoordinate(ref Vector value, out float result)
- {
- Vector.Dot(ref Normal, ref value, out result);
- result += Distance;
- }
- #endregion
-
- #region GetDistance (Public)
- /// <summary>
- /// Get distance from the given position to the plane.
- /// </summary>
- /// <param name="position">Position vector to check with.</param>
- /// <returns>The shortest distance between the position and the plane.
- /// </returns>
- public float GetDistance(Vector position)
- {
- return Vector.Dot(Normal, position) + Distance;
- }
- #endregion
-
- #region Normalize (Public)
- /// <summary>
- /// Normalize
- /// </summary>
- public void Normalize()
- {
- float distanceSquared = Normal.LengthSquared;
- if (distanceSquared != 0)
- {
- float distanceInverse = 1.0f / MathHelper.Sqrt(distanceSquared);
- Normal.X *= distanceInverse;
- Normal.Y *= distanceInverse;
- Normal.Z *= distanceInverse;
- Distance *= distanceInverse;
- }
- }
- #endregion
-
- #region Intersects (Public)
- /// <summary>
- /// Determines whether a vector point is intersecting with a plane
- /// otherwise defines its position.
- /// </summary>
- /// <param name="point">The point</param>
- /// <returns></returns>
- public PlaneIntersectionType Intersects(Vector point)
- {
- float objectsDistance = Vector.Dot(Normal, point);
- objectsDistance += Distance;
- if (objectsDistance > 0f)
- {
- return PlaneIntersectionType.Front;
- }
- if (objectsDistance < 0f)
- {
- return PlaneIntersectionType.Back;
- }
- return PlaneIntersectionType.Intersecting;
- }
- #endregion
-
- #region GetHashCode (Public)
- /// <summary>
- /// Get hash code
- /// </summary>
- /// <returns>hash code</returns>
- public override int GetHashCode()
- {
- return Normal.GetHashCode() ^ Distance.GetHashCode();
- }
- #endregion
-
- #region Equals (Public)
- /// <summary>
- /// Equals
- /// </summary>
- /// <param name="obj">Object to compare</param>
- /// <returns>True if obj is a plane and equal to this plane.</returns>
- public override bool Equals(object obj)
- {
- return (obj is Plane)
- ? Equals((Plane)obj)
- : base.Equals(obj);
- }
- #endregion
-
- /// <summary>
- /// Tests
- /// </summary>
- internal class PlaneTests
- {
- #region CreatePlane
- /// <summary>
- /// Test CreatePlane
- /// </summary>
- [Test]
- public void CreatePlane()
- {
- Vector normalVector = new Vector(10f, 3f, 59f);
- Vector normalizedNormalVector = Vector.Normalize(normalVector);
-
- Plane testPlane = new Plane(normalVector.X, normalVector.Y,
- normalVector.Z, 2f);
- Assert.NearlyEqual(testPlane.Normal.X, normalizedNormalVector.X);
- Assert.NearlyEqual(testPlane.Normal.Y, normalizedNormalVector.Y);
- Assert.NearlyEqual(testPlane.Normal.Z, normalizedNormalVector.Z);
- Assert.NearlyEqual(testPlane.Distance, 2f);
-
- testPlane = new Plane(normalVector, 2f);
- Assert.NearlyEqual(testPlane.Normal.X, normalizedNormalVector.X);
- Assert.NearlyEqual(testPlane.Normal.Y, normalizedNormalVector.Y);
- Assert.NearlyEqual(testPlane.Normal.Z, normalizedNormalVector.Z);
- Assert.NearlyEqual(testPlane.Distance, 2f);
- }
- #endregion
-
- #region TestIntersects
- /// <summary>
- /// Plane Intersection Tests
- /// </summary>
- [Test]
- public void TestIntersects()
- {
- // planes intersecting each other
- Vector pt; // output point
- Plane p1 = new Plane(Vector.UnitY, 1);
- Plane p2 = new Plane(Vector.UnitX, 1);
- Plane p3 = new Plane(new Vector(0, 1, 0), 4);
- Ray ray1 = new Ray(Vector.Zero, new Vector(3, 3, 0));
- Ray ray2 = new Ray(new Vector(-1, -1, 0), new Vector(-5, -5, 0));
- BoundingBox box = new BoundingBox(Vector.Zero, new Vector(2, 2, 2));
- BoundingSphere sphere = new BoundingSphere(new Vector(1, 0, 0), 2);
- // Plane to Plane intersection
- Assert.True(p1.Intersects(p2, out pt));
- Assert.False(p1.Intersects(p3, out pt));
- // Plane to Ray intersection
- Assert.True(p1.Intersects(ray1, out pt));
- Assert.False(p3.Intersects(ray2, out pt));
- // Plane to sphere intersection
- Assert.True(p2.Intersects(sphere));
- Assert.False(p3.Intersects(sphere));
- // PLane to Box Intersection
- Assert.True(p1.Intersects(box));
- Assert.True(p2.Intersects(box));
- Assert.False(p3.Intersects(box));
- }
- #endregion
-
- #region Equality
- /// <summary>
- /// Test Equality
- /// </summary>
- [Test]
- public void Equality()
- {
- Plane testPlane1 = new Plane(10f, 3f, 59f, 2f);
- Plane testPlane2 = new Plane(7f, 10f, 59f, 15f);
- Plane testPlane3 = new Plane(7f, 10f, 59f, 15f);
- //Asserting Planes
- Assert.True(testPlane2 == testPlane3);
- Assert.True(testPlane1 != testPlane2);
- Assert.False(testPlane1 == testPlane2);
- Assert.True(testPlane2.Equals(testPlane3));
- Assert.True(testPlane2.Equals((object)testPlane3));
- Assert.False(testPlane1.Equals((object)testPlane3));
- }
- #endregion
-
- #region DotCoordinate
- /// <summary>
- /// Test DotCoordinate. Calculation:
- /// (N.X * value.X) + (N.Y * value.Y) + (N.Z * value.Z) + Distance
- /// </summary>
- [Test]
- public void DotCoordinate()
- {
- Plane testPlane = new Plane(10f, 3f, 59f, 2f);
- Vector dotVector = new Vector(5f, 4f, 1f);
-
- float checkResult = (testPlane.Normal.X * dotVector.X) +
- (testPlane.Normal.Y * dotVector.Y) +
- (testPlane.Normal.Z * dotVector.Z) +
- testPlane.Distance;
- float dotCoordinate = testPlane.DotCoordinate(dotVector);
- Assert.NearlyEqual(dotCoordinate, checkResult);
- }
- #endregion
- }
- }
- }