// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Globalization; using System.Runtime.CompilerServices; namespace System.DoubleNumerics { /// /// 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 double 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(double x, double y, double z, double 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, double 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) { double ax = point2.X - point1.X; double ay = point2.Y - point1.Y; double az = point2.Z - point1.Z; double bx = point3.X - point1.X; double by = point3.Y - point1.Y; double bz = point3.Z - point1.Z; // N=Cross(a,b) double nx = ay * bz - az * by; double ny = az * bx - ax * bz; double nz = ax * by - ay * bx; // Normalize(N) double ls = nx * nx + ny * ny + nz * nz; double invNorm = 1.0 / (double)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 double FLT_EPSILON = 1.192092896e-07; // smallest such that 1.0+FLT_EPSILON != 1.0 double f = value.Normal.X * value.Normal.X + value.Normal.Y * value.Normal.Y + value.Normal.Z * value.Normal.Z; if (Math.Abs(f - 1.0) < FLT_EPSILON) { return value; // It already normalized, so we don't need to further process. } double fInv = 1.0 / (double)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); double 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. double x2 = rotation.X + rotation.X; double y2 = rotation.Y + rotation.Y; double z2 = rotation.Z + rotation.Z; double wx2 = rotation.W * x2; double wy2 = rotation.W * y2; double wz2 = rotation.W * z2; double xx2 = rotation.X * x2; double xy2 = rotation.X * y2; double xz2 = rotation.X * z2; double yy2 = rotation.Y * y2; double yz2 = rotation.Y * z2; double zz2 = rotation.Z * z2; double m11 = 1.0 - yy2 - zz2; double m21 = xy2 - wz2; double m31 = xz2 + wy2; double m12 = xy2 + wz2; double m22 = 1.0 - xx2 - zz2; double m32 = yz2 - wx2; double m13 = xz2 - wy2; double m23 = yz2 + wx2; double m33 = 1.0 - xx2 - yy2; double 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 double 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 double DotCoordinate(Plane plane, Vector3 value) { 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 double DotNormal(Plane plane, Vector3 value) { 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) { 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(); } } }