|
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Globalization;
using System.Runtime.CompilerServices;
namespace System.Numerics
{
/// <summary>
/// A structure encapsulating a 3D Plane
/// </summary>
public struct Plane : IEquatable<Plane>
{
/// <summary>
/// The normal vector of the Plane.
/// </summary>
public Vector3 Normal;
/// <summary>
/// The distance of the Plane along its normal from the origin.
/// </summary>
public float D;
/// <summary>
/// Constructs a Plane from the X, Y, and Z components of its normal, and its distance from the origin on that normal.
/// </summary>
/// <param name="x">The X-component of the normal.</param>
/// <param name="y">The Y-component of the normal.</param>
/// <param name="z">The Z-component of the normal.</param>
/// <param name="d">The distance of the Plane along its normal from the origin.</param>
public Plane(float x, float y, float z, float d)
{
Normal = new Vector3(x, y, z);
this.D = d;
}
/// <summary>
/// Constructs a Plane from the given normal and distance along the normal from the origin.
/// </summary>
/// <param name="normal">The Plane's normal vector.</param>
/// <param name="d">The Plane's distance from the origin along its normal vector.</param>
public Plane(Vector3 normal, float d)
{
this.Normal = normal;
this.D = d;
}
/// <summary>
/// Constructs a Plane from the given Vector4.
/// </summary>
/// <param name="value">A vector whose first 3 elements describe the normal vector,
/// and whose W component defines the distance along that normal from the origin.</param>
public Plane(Vector4 value)
{
Normal = new Vector3(value.X, value.Y, value.Z);
D = value.W;
}
/// <summary>
/// Creates a Plane that contains the three given points.
/// </summary>
/// <param name="point1">The first point defining the Plane.</param>
/// <param name="point2">The second point defining the Plane.</param>
/// <param name="point3">The third point defining the Plane.</param>
/// <returns>The Plane containing the three points.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane CreateFromVertices(Vector3 point1, Vector3 point2, Vector3 point3)
{
if (Vector.IsHardwareAccelerated)
{
Vector3 a = point2 - point1;
Vector3 b = point3 - point1;
// N = Cross(a, b)
Vector3 n = Vector3.Cross(a, b);
Vector3 normal = Vector3.Normalize(n);
// D = - Dot(N, point1)
float d = -Vector3.Dot(normal, point1);
return new Plane(normal, d);
}
else
{
float ax = point2.X - point1.X;
float ay = point2.Y - point1.Y;
float az = point2.Z - point1.Z;
float bx = point3.X - point1.X;
float by = point3.Y - point1.Y;
float bz = point3.Z - point1.Z;
// N=Cross(a,b)
float nx = ay * bz - az * by;
float ny = az * bx - ax * bz;
float nz = ax * by - ay * bx;
// Normalize(N)
float ls = nx * nx + ny * ny + nz * nz;
float invNorm = 1.0f / (float)Math.Sqrt((double)ls);
Vector3 normal = new Vector3(
nx * invNorm,
ny * invNorm,
nz * invNorm);
return new Plane(
normal,
-(normal.X * point1.X + normal.Y * point1.Y + normal.Z * point1.Z));
}
}
/// <summary>
/// Creates a new Plane whose normal vector is the source Plane's normal vector normalized.
/// </summary>
/// <param name="value">The source Plane.</param>
/// <returns>The normalized Plane.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane Normalize(Plane value)
{
const float FLT_EPSILON = 1.192092896e-07f; // smallest such that 1.0+FLT_EPSILON != 1.0
if (Vector.IsHardwareAccelerated)
{
float normalLengthSquared = value.Normal.LengthSquared();
if (Math.Abs(normalLengthSquared - 1.0f) < FLT_EPSILON)
{
// It already normalized, so we don't need to farther process.
return value;
}
float normalLength = (float)Math.Sqrt(normalLengthSquared);
return new Plane(
value.Normal / normalLength,
value.D / normalLength);
}
else
{
float f = value.Normal.X * value.Normal.X + value.Normal.Y * value.Normal.Y + value.Normal.Z * value.Normal.Z;
if (Math.Abs(f - 1.0f) < FLT_EPSILON)
{
return value; // It already normalized, so we don't need to further process.
}
float fInv = 1.0f / (float)Math.Sqrt(f);
return new Plane(
value.Normal.X * fInv,
value.Normal.Y * fInv,
value.Normal.Z * fInv,
value.D * fInv);
}
}
/// <summary>
/// Transforms a normalized Plane by a Matrix.
/// </summary>
/// <param name="plane"> The normalized Plane to transform.
/// This Plane must already be normalized, so that its Normal vector is of unit length, before this method is called.</param>
/// <param name="matrix">The transformation matrix to apply to the Plane.</param>
/// <returns>The transformed Plane.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane Transform(Plane plane, Matrix4x4 matrix)
{
Matrix4x4 m;
Matrix4x4.Invert(matrix, out m);
float x = plane.Normal.X, y = plane.Normal.Y, z = plane.Normal.Z, w = plane.D;
return new Plane(
x * m.M11 + y * m.M12 + z * m.M13 + w * m.M14,
x * m.M21 + y * m.M22 + z * m.M23 + w * m.M24,
x * m.M31 + y * m.M32 + z * m.M33 + w * m.M34,
x * m.M41 + y * m.M42 + z * m.M43 + w * m.M44);
}
/// <summary>
/// Transforms a normalized Plane by a Quaternion rotation.
/// </summary>
/// <param name="plane"> The normalized Plane to transform.
/// This Plane must already be normalized, so that its Normal vector is of unit length, before this method is called.</param>
/// <param name="rotation">The Quaternion rotation to apply to the Plane.</param>
/// <returns>A new Plane that results from applying the rotation.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Plane Transform(Plane plane, Quaternion rotation)
{
// Compute rotation matrix.
float x2 = rotation.X + rotation.X;
float y2 = rotation.Y + rotation.Y;
float z2 = rotation.Z + rotation.Z;
float wx2 = rotation.W * x2;
float wy2 = rotation.W * y2;
float wz2 = rotation.W * z2;
float xx2 = rotation.X * x2;
float xy2 = rotation.X * y2;
float xz2 = rotation.X * z2;
float yy2 = rotation.Y * y2;
float yz2 = rotation.Y * z2;
float zz2 = rotation.Z * z2;
float m11 = 1.0f - yy2 - zz2;
float m21 = xy2 - wz2;
float m31 = xz2 + wy2;
float m12 = xy2 + wz2;
float m22 = 1.0f - xx2 - zz2;
float m32 = yz2 - wx2;
float m13 = xz2 - wy2;
float m23 = yz2 + wx2;
float m33 = 1.0f - xx2 - yy2;
float x = plane.Normal.X, y = plane.Normal.Y, z = plane.Normal.Z;
return new Plane(
x * m11 + y * m21 + z * m31,
x * m12 + y * m22 + z * m32,
x * m13 + y * m23 + z * m33,
plane.D);
}
/// <summary>
/// Calculates the dot product of a Plane and Vector4.
/// </summary>
/// <param name="plane">The Plane.</param>
/// <param name="value">The Vector4.</param>
/// <returns>The dot product.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Dot(Plane plane, Vector4 value)
{
return plane.Normal.X * value.X +
plane.Normal.Y * value.Y +
plane.Normal.Z * value.Z +
plane.D * value.W;
}
/// <summary>
/// Returns the dot product of a specified Vector3 and the normal vector of this Plane plus the distance (D) value of the Plane.
/// </summary>
/// <param name="plane">The plane.</param>
/// <param name="value">The Vector3.</param>
/// <returns>The resulting value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DotCoordinate(Plane plane, Vector3 value)
{
if (Vector.IsHardwareAccelerated)
{
return Vector3.Dot(plane.Normal, value) + plane.D;
}
else
{
return plane.Normal.X * value.X +
plane.Normal.Y * value.Y +
plane.Normal.Z * value.Z +
plane.D;
}
}
/// <summary>
/// Returns the dot product of a specified Vector3 and the Normal vector of this Plane.
/// </summary>
/// <param name="plane">The plane.</param>
/// <param name="value">The Vector3.</param>
/// <returns>The resulting dot product.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DotNormal(Plane plane, Vector3 value)
{
if (Vector.IsHardwareAccelerated)
{
return Vector3.Dot(plane.Normal, value);
}
else
{
return plane.Normal.X * value.X +
plane.Normal.Y * value.Y +
plane.Normal.Z * value.Z;
}
}
/// <summary>
/// Returns a boolean indicating whether the two given Planes are equal.
/// </summary>
/// <param name="value1">The first Plane to compare.</param>
/// <param name="value2">The second Plane to compare.</param>
/// <returns>True if the Planes are equal; False otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Plane value1, Plane value2)
{
return (value1.Normal.X == value2.Normal.X &&
value1.Normal.Y == value2.Normal.Y &&
value1.Normal.Z == value2.Normal.Z &&
value1.D == value2.D);
}
/// <summary>
/// Returns a boolean indicating whether the two given Planes are not equal.
/// </summary>
/// <param name="value1">The first Plane to compare.</param>
/// <param name="value2">The second Plane to compare.</param>
/// <returns>True if the Planes are not equal; False if they are equal.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Plane value1, Plane value2)
{
return (value1.Normal.X != value2.Normal.X ||
value1.Normal.Y != value2.Normal.Y ||
value1.Normal.Z != value2.Normal.Z ||
value1.D != value2.D);
}
/// <summary>
/// Returns a boolean indicating whether the given Plane is equal to this Plane instance.
/// </summary>
/// <param name="other">The Plane to compare this instance to.</param>
/// <returns>True if the other Plane is equal to this instance; False otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Plane other)
{
if (Vector.IsHardwareAccelerated)
{
return this.Normal.Equals(other.Normal) && this.D == other.D;
}
else
{
return (Normal.X == other.Normal.X &&
Normal.Y == other.Normal.Y &&
Normal.Z == other.Normal.Z &&
D == other.D);
}
}
/// <summary>
/// Returns a boolean indicating whether the given Object is equal to this Plane instance.
/// </summary>
/// <param name="obj">The Object to compare against.</param>
/// <returns>True if the Object is equal to this Plane; False otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Plane)
{
return Equals((Plane)obj);
}
return false;
}
/// <summary>
/// Returns a String representing this Plane instance.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
CultureInfo ci = CultureInfo.CurrentCulture;
return String.Format(ci, "{{Normal:{0} D:{1}}}", Normal.ToString(), D.ToString(ci));
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return Normal.GetHashCode() + D.GetHashCode();
}
}
}
|