// 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 { /// /// A structure encapsulating a 3D Plane /// public struct Plane : IEquatable { /// /// The normal vector of the Plane. /// public Vector3 Normal; /// /// The distance of the Plane along its normal from the origin. /// public float D; /// /// Constructs a Plane from the X, Y, and Z components of its normal, and its distance from the origin on that normal. /// /// The X-component of the normal. /// The Y-component of the normal. /// The Z-component of the normal. /// The distance of the Plane along its normal from the origin. public Plane(float x, float y, float z, float d) { Normal = new Vector3(x, y, z); this.D = d; } /// /// Constructs a Plane from the given normal and distance along the normal from the origin. /// /// The Plane's normal vector. /// The Plane's distance from the origin along its normal vector. public Plane(Vector3 normal, float d) { this.Normal = normal; this.D = d; } /// /// Constructs a Plane from the given Vector4. /// /// A vector whose first 3 elements describe the normal vector, /// and whose W component defines the distance along that normal from the origin. public Plane(Vector4 value) { Normal = new Vector3(value.X, value.Y, value.Z); D = value.W; } /// /// Creates a Plane that contains the three given points. /// /// The first point defining the Plane. /// The second point defining the Plane. /// The third point defining the Plane. /// The Plane containing the three points. [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)); } } /// /// Creates a new Plane whose normal vector is the source Plane's normal vector normalized. /// /// The source Plane. /// The normalized Plane. [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); } } /// /// Transforms a normalized Plane by a Matrix. /// /// 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. /// The transformation matrix to apply to the Plane. /// The transformed Plane. [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); } /// /// Transforms a normalized Plane by a Quaternion rotation. /// /// 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. /// The Quaternion rotation to apply to the Plane. /// A new Plane that results from applying the rotation. [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); } /// /// Calculates the dot product of a Plane and Vector4. /// /// The Plane. /// The Vector4. /// The dot product. [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; } /// /// Returns the dot product of a specified Vector3 and the normal vector of this Plane plus the distance (D) value of the Plane. /// /// The plane. /// The Vector3. /// The resulting value. [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; } } /// /// Returns the dot product of a specified Vector3 and the Normal vector of this Plane. /// /// The plane. /// The Vector3. /// The resulting dot product. [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; } } /// /// Returns a boolean indicating whether the two given Planes are equal. /// /// The first Plane to compare. /// The second Plane to compare. /// True if the Planes are equal; False otherwise. [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); } /// /// Returns a boolean indicating whether the two given Planes are not equal. /// /// The first Plane to compare. /// The second Plane to compare. /// True if the Planes are not equal; False if they are equal. [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); } /// /// Returns a boolean indicating whether the given Plane is equal to this Plane instance. /// /// The Plane to compare this instance to. /// True if the other Plane is equal to this instance; False otherwise. [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); } } /// /// Returns a boolean indicating whether the given Object is equal to this Plane instance. /// /// The Object to compare against. /// True if the Object is equal to this Plane; False otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is Plane) { return Equals((Plane)obj); } return false; } /// /// Returns a String representing this Plane instance. /// /// The string representation. public override string ToString() { CultureInfo ci = CultureInfo.CurrentCulture; return String.Format(ci, "{{Normal:{0} D:{1}}}", Normal.ToString(), D.ToString(ci)); } /// /// Returns the hash code for this instance. /// /// The hash code. public override int GetHashCode() { return Normal.GetHashCode() + D.GetHashCode(); } } }