#### /Utilities/Datatypes/Plane.cs

#
C# | 471 lines | 295 code | 33 blank | 143 comment | 20 complexity | 1268be908a6bb440a190a9df5b04dbec MD5 | raw file
``````
using System;

using System.Diagnostics;

using System.IO;

using System.Runtime.InteropServices;

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;

{

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

/// <summary>

/// Loads the plane from a binary stream (just the normal and distance).

/// </summary>

{

}

/// <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

}

}

}

``````