using System; using System.Diagnostics; using System.Runtime.CompilerServices; using Unity.IL2CPP.CompilerServices; namespace Unity.Mathematics.Geometry { /// /// A plane represented by a normal vector and a distance along the normal from the origin. /// /// /// A plane splits the 3D space in half. The normal vector points to the positive half and the other half is /// considered negative. /// [DebuggerDisplay("{Normal}, {Distance}")] [Serializable] [Il2CppEagerStaticClassConstruction] public struct Plane { /// /// A plane in the form Ax + By + Cz + Dw = 0. /// /// /// Stores the plane coefficients A, B, C, D where (A, B, C) is a unit normal vector and D is the distance /// from the origin. A plane stored with a unit normal vector is called a normalized plane. /// public float4 NormalAndDistance; /// /// Constructs a Plane from arbitrary coefficients A, B, C, D of the plane equation Ax + By + Cz + Dw = 0. /// /// /// The constructed plane will be the normalized form of the plane specified by the given coefficients. /// /// Coefficient A from plane equation. /// Coefficient B from plane equation. /// Coefficient C from plane equation. /// Coefficient D from plane equation. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Plane(float coefficientA, float coefficientB, float coefficientC, float coefficientD) { NormalAndDistance = Normalize(new float4(coefficientA, coefficientB, coefficientC, coefficientD)); } /// /// Constructs a plane with a normal vector and distance from the origin. /// /// /// The constructed plane will be the normalized form of the plane specified by the inputs. /// /// A non-zero vector that is perpendicular to the plane. It may be any length. /// Distance from the origin along the normal. A negative value moves the plane in the /// same direction as the normal while a positive value moves it in the opposite direction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Plane(float3 normal, float distance) { NormalAndDistance = Normalize(new float4(normal, distance)); } /// /// Constructs a plane with a normal vector and a point that lies in the plane. /// /// /// The constructed plane will be the normalized form of the plane specified by the inputs. /// /// A non-zero vector that is perpendicular to the plane. It may be any length. /// A point that lies in the plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Plane(float3 normal, float3 pointInPlane) : this(normal, -math.dot(normal, pointInPlane)) { } /// /// Constructs a plane with two vectors and a point that all lie in the plane. /// /// /// The constructed plane will be the normalized form of the plane specified by the inputs. /// /// A non-zero vector that lies in the plane. It may be any length. /// A non-zero vector that lies in the plane. It may be any length and must not be a scalar multiple of . /// A point that lies in the plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Plane(float3 vector1InPlane, float3 vector2InPlane, float3 pointInPlane) : this(math.cross(vector1InPlane, vector2InPlane), pointInPlane) { } /// /// Creates a normalized Plane directly without normalization cost. /// /// /// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors /// by calling this function. /// /// A non-zero vector that is perpendicular to the plane. It must be unit length. /// Distance from the origin along the normal. A negative value moves the plane in the /// same direction as the normal while a positive value moves it in the opposite direction. /// Normalized Plane constructed from given inputs. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Plane CreateFromUnitNormalAndDistance(float3 unitNormal, float distance) { return new Plane { NormalAndDistance = new float4(unitNormal, distance) }; } /// /// Creates a normalized Plane without normalization cost. /// /// /// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors /// by calling this function. /// /// A non-zero vector that is perpendicular to the plane. It must be unit length. /// A point that lies in the plane. /// Normalized Plane constructed from given inputs. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Plane CreateFromUnitNormalAndPointInPlane(float3 unitNormal, float3 pointInPlane) { return new Plane { NormalAndDistance = new float4(unitNormal, -math.dot(unitNormal, pointInPlane)) }; } /// /// Get/set the normal vector of the plane. /// /// /// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but /// (A, B, C) is not unit length, then you must normalize the plane by calling . /// public float3 Normal { get => NormalAndDistance.xyz; set => NormalAndDistance.xyz = value; } /// /// Get/set the distance of the plane from the origin. May be a negative value. /// /// /// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but /// (A, B, C) is not unit length, then you must normalize the plane by calling . /// public float Distance { get => NormalAndDistance.w; set => NormalAndDistance.w = value; } /// /// Normalizes the given Plane. /// /// Plane to normalize. /// Normalized Plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Plane Normalize(Plane plane) { return new Plane { NormalAndDistance = Normalize(plane.NormalAndDistance) }; } /// /// Normalizes the plane represented by the given plane coefficients. /// /// /// The plane coefficients are A, B, C, D and stored in that order in the . /// /// Plane coefficients A, B, C, D stored in x, y, z, w (respectively). /// Normalized plane coefficients. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float4 Normalize(float4 planeCoefficients) { float recipLength = math.rsqrt(math.lengthsq(planeCoefficients.xyz)); return new Plane { NormalAndDistance = planeCoefficients * recipLength }; } /// /// Get the signed distance from the point to the plane. /// /// /// Plane must be normalized prior to calling this function. Distance is positive if point is on side of the /// plane the normal points to, negative if on the opposite side and zero if the point lies in the plane. /// Avoid comparing equality with 0.0f when testing if a point lies exactly in the plane and use an approximate /// comparison instead. /// /// Point to find the signed distance with. /// Signed distance of the point from the plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public float SignedDistanceToPoint(float3 point) { CheckPlaneIsNormalized(); return math.dot(NormalAndDistance, new float4(point, 1.0f)); } /// /// Projects the given point onto the plane. /// /// /// Plane must be normalized prior to calling this function. The result is the position closest to the point /// that still lies in the plane. /// /// Point to project onto the plane. /// Projected point that's inside the plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public float3 Projection(float3 point) { CheckPlaneIsNormalized(); return point - Normal * SignedDistanceToPoint(point); } /// /// Flips the plane so the normal points in the opposite direction. /// public Plane Flipped => new Plane { NormalAndDistance = -NormalAndDistance }; /// /// Implicitly converts a to . /// /// Plane to convert. /// A representing the plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator float4(Plane plane) => plane.NormalAndDistance; [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckPlaneIsNormalized() { float ll = math.lengthsq(Normal.xyz); const float lowerBound = 0.999f * 0.999f; const float upperBound = 1.001f * 1.001f; if (ll < lowerBound || ll > upperBound) { throw new System.ArgumentException("Plane must be normalized. Call Plane.Normalize() to normalize plane."); } } } }