// MIT License - Copyright (C) The Mono.Xna Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
namespace CommonLang.Geometry
{
internal class PlaneHelper
{
///
/// Returns a value indicating what side (positive/negative) of a plane a point is
///
/// The point to check with
/// The plane to check against
/// Greater than zero if on the positive side, less than zero if on the negative size, 0 otherwise
public static float ClassifyPoint(ref Vector3 point, ref Plane plane)
{
return point.X * plane.Normal.X + point.Y * plane.Normal.Y + point.Z * plane.Normal.Z + plane.D;
}
///
/// Returns the perpendicular distance from a point to a plane
///
/// The point to check
/// The place to check
/// The perpendicular distance from the point to the plane
public static float PerpendicularDistance(ref Vector3 point, ref Plane plane)
{
// dist = (ax + by + cz + d) / sqrt(a*a + b*b + c*c)
return (float)Math.Abs((plane.Normal.X * point.X + plane.Normal.Y * point.Y + plane.Normal.Z * point.Z)
/ Math.Sqrt(plane.Normal.X * plane.Normal.X + plane.Normal.Y * plane.Normal.Y + plane.Normal.Z * plane.Normal.Z));
}
}
public struct Plane : IEquatable
{
#region Public Fields
public float D;
public Vector3 Normal;
#endregion Public Fields
#region Constructors
public Plane(Vector4 value)
: this(new Vector3(value.X, value.Y, value.Z), value.W)
{
}
public Plane(Vector3 normal, float d)
{
Normal = normal;
D = d;
}
public Plane(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 ab = b - a;
Vector3 ac = c - a;
Vector3 cross = Vector3.Cross(ab, ac);
Normal = Vector3.Normalize(cross);
D = -(Vector3.Dot(Normal, a));
}
public Plane(float a, float b, float c, float d)
: this(new Vector3(a, b, c), d)
{
}
#endregion Constructors
#region Public Methods
public float Dot(Vector4 value)
{
return ((((this.Normal.X * value.X) + (this.Normal.Y * value.Y)) + (this.Normal.Z * value.Z)) + (this.D * value.W));
}
public void Dot(ref Vector4 value, out float result)
{
result = (((this.Normal.X * value.X) + (this.Normal.Y * value.Y)) + (this.Normal.Z * value.Z)) + (this.D * value.W);
}
public float DotCoordinate(Vector3 value)
{
return ((((this.Normal.X * value.X) + (this.Normal.Y * value.Y)) + (this.Normal.Z * value.Z)) + this.D);
}
public void DotCoordinate(ref Vector3 value, out float result)
{
result = (((this.Normal.X * value.X) + (this.Normal.Y * value.Y)) + (this.Normal.Z * value.Z)) + this.D;
}
public float DotNormal(Vector3 value)
{
return (((this.Normal.X * value.X) + (this.Normal.Y * value.Y)) + (this.Normal.Z * value.Z));
}
public void DotNormal(ref Vector3 value, out float result)
{
result = ((this.Normal.X * value.X) + (this.Normal.Y * value.Y)) + (this.Normal.Z * value.Z);
}
///
/// Transforms a normalized plane by a matrix.
///
/// The normalized plane to transform.
/// The transformation matrix.
/// The transformed plane.
public static Plane Transform(Plane plane, Matrix matrix)
{
Plane result;
Transform(ref plane, ref matrix, out result);
return result;
}
///
/// Transforms a normalized plane by a matrix.
///
/// The normalized plane to transform.
/// The transformation matrix.
/// The transformed plane.
public static void Transform(ref Plane plane, ref Matrix matrix, out Plane result)
{
// See "Transforming Normals" in http://www.glprogramming.com/red/appendixf.html
// for an explanation of how this works.
Matrix transformedMatrix;
Matrix.Invert(ref matrix, out transformedMatrix);
Matrix.Transpose(ref transformedMatrix, out transformedMatrix);
var vector = new Vector4(plane.Normal, plane.D);
Vector4 transformedVector;
Vector4.Transform(ref vector, ref transformedMatrix, out transformedVector);
result = new Plane(transformedVector);
}
///
/// Transforms a normalized plane by a quaternion rotation.
///
/// The normalized plane to transform.
/// The quaternion rotation.
/// The transformed plane.
public static Plane Transform(Plane plane, Quaternion rotation)
{
Plane result;
Transform(ref plane, ref rotation, out result);
return result;
}
///
/// Transforms a normalized plane by a quaternion rotation.
///
/// The normalized plane to transform.
/// The quaternion rotation.
/// The transformed plane.
public static void Transform(ref Plane plane, ref Quaternion rotation, out Plane result)
{
Vector3.Transform(ref plane.Normal, ref rotation, out result.Normal);
result.D = plane.D;
}
public void Normalize()
{
float factor;
Vector3 normal = Normal;
Normal = Vector3.Normalize(Normal);
factor = (float)Math.Sqrt(Normal.X * Normal.X + Normal.Y * Normal.Y + Normal.Z * Normal.Z) /
(float)Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y + normal.Z * normal.Z);
D = D * factor;
}
public static Plane Normalize(Plane value)
{
Plane ret;
Normalize(ref value, out ret);
return ret;
}
public static void Normalize(ref Plane value, out Plane result)
{
float factor;
result.Normal = Vector3.Normalize(value.Normal);
factor = (float)Math.Sqrt(result.Normal.X * result.Normal.X + result.Normal.Y * result.Normal.Y + result.Normal.Z * result.Normal.Z) /
(float)Math.Sqrt(value.Normal.X * value.Normal.X + value.Normal.Y * value.Normal.Y + value.Normal.Z * value.Normal.Z);
result.D = value.D * factor;
}
public static bool operator !=(Plane plane1, Plane plane2)
{
return !plane1.Equals(plane2);
}
public static bool operator ==(Plane plane1, Plane plane2)
{
return plane1.Equals(plane2);
}
public override bool Equals(object other)
{
return (other is Plane) ? this.Equals((Plane)other) : false;
}
public bool Equals(Plane other)
{
return ((Normal == other.Normal) && (D == other.D));
}
public override int GetHashCode()
{
return Normal.GetHashCode() ^ D.GetHashCode();
}
public PlaneIntersectionType Intersects(BoundingBox box)
{
return box.Intersects(this);
}
public void Intersects(ref BoundingBox box, out PlaneIntersectionType result)
{
box.Intersects (ref this, out result);
}
public PlaneIntersectionType Intersects(BoundingFrustum frustum)
{
return frustum.Intersects(this);
}
public PlaneIntersectionType Intersects(BoundingSphere sphere)
{
return sphere.Intersects(this);
}
public void Intersects(ref BoundingSphere sphere, out PlaneIntersectionType result)
{
sphere.Intersects(ref this, out result);
}
internal PlaneIntersectionType Intersects(ref Vector3 point)
{
float distance;
DotCoordinate(ref point, out distance);
if (distance > 0)
return PlaneIntersectionType.Front;
if (distance < 0)
return PlaneIntersectionType.Back;
return PlaneIntersectionType.Intersecting;
}
internal string DebugDisplayString
{
get
{
return string.Concat(
this.Normal.DebugDisplayString, " ",
this.D.ToString()
);
}
}
public override string ToString()
{
return "{Normal:" + Normal + " D:" + D + "}";
}
#endregion
}
}