A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using System.Runtime.CompilerServices;
4using Unity.IL2CPP.CompilerServices;
5
6namespace Unity.Mathematics.Geometry
7{
8 /// <summary>
9 /// A plane represented by a normal vector and a distance along the normal from the origin.
10 /// </summary>
11 /// <remarks>
12 /// A plane splits the 3D space in half. The normal vector points to the positive half and the other half is
13 /// considered negative.
14 /// </remarks>
15 [DebuggerDisplay("{Normal}, {Distance}")]
16 [Serializable]
17 [Il2CppEagerStaticClassConstruction]
18 public struct Plane
19 {
20 /// <summary>
21 /// A plane in the form Ax + By + Cz + Dw = 0.
22 /// </summary>
23 /// <remarks>
24 /// Stores the plane coefficients A, B, C, D where (A, B, C) is a unit normal vector and D is the distance
25 /// from the origin. A plane stored with a unit normal vector is called a normalized plane.
26 /// </remarks>
27 public float4 NormalAndDistance;
28
29 /// <summary>
30 /// Constructs a Plane from arbitrary coefficients A, B, C, D of the plane equation Ax + By + Cz + Dw = 0.
31 /// </summary>
32 /// <remarks>
33 /// The constructed plane will be the normalized form of the plane specified by the given coefficients.
34 /// </remarks>
35 /// <param name="coefficientA">Coefficient A from plane equation.</param>
36 /// <param name="coefficientB">Coefficient B from plane equation.</param>
37 /// <param name="coefficientC">Coefficient C from plane equation.</param>
38 /// <param name="coefficientD">Coefficient D from plane equation.</param>
39 [MethodImpl(MethodImplOptions.AggressiveInlining)]
40 public Plane(float coefficientA, float coefficientB, float coefficientC, float coefficientD)
41 {
42 NormalAndDistance = Normalize(new float4(coefficientA, coefficientB, coefficientC, coefficientD));
43 }
44
45 /// <summary>
46 /// Constructs a plane with a normal vector and distance from the origin.
47 /// </summary>
48 /// <remarks>
49 /// The constructed plane will be the normalized form of the plane specified by the inputs.
50 /// </remarks>
51 /// <param name="normal">A non-zero vector that is perpendicular to the plane. It may be any length.</param>
52 /// <param name="distance">Distance from the origin along the normal. A negative value moves the plane in the
53 /// same direction as the normal while a positive value moves it in the opposite direction.</param>
54 [MethodImpl(MethodImplOptions.AggressiveInlining)]
55 public Plane(float3 normal, float distance)
56 {
57 NormalAndDistance = Normalize(new float4(normal, distance));
58 }
59
60 /// <summary>
61 /// Constructs a plane with a normal vector and a point that lies in the plane.
62 /// </summary>
63 /// <remarks>
64 /// The constructed plane will be the normalized form of the plane specified by the inputs.
65 /// </remarks>
66 /// <param name="normal">A non-zero vector that is perpendicular to the plane. It may be any length.</param>
67 /// <param name="pointInPlane">A point that lies in the plane.</param>
68 [MethodImpl(MethodImplOptions.AggressiveInlining)]
69 public Plane(float3 normal, float3 pointInPlane)
70 : this(normal, -math.dot(normal, pointInPlane))
71 {
72 }
73
74 /// <summary>
75 /// Constructs a plane with two vectors and a point that all lie in the plane.
76 /// </summary>
77 /// <remarks>
78 /// The constructed plane will be the normalized form of the plane specified by the inputs.
79 /// </remarks>
80 /// <param name="vector1InPlane">A non-zero vector that lies in the plane. It may be any length.</param>
81 /// <param name="vector2InPlane">A non-zero vector that lies in the plane. It may be any length and must not be a scalar multiple of <paramref name="vector1InPlane"/>.</param>
82 /// <param name="pointInPlane">A point that lies in the plane.</param>
83 [MethodImpl(MethodImplOptions.AggressiveInlining)]
84 public Plane(float3 vector1InPlane, float3 vector2InPlane, float3 pointInPlane)
85 : this(math.cross(vector1InPlane, vector2InPlane), pointInPlane)
86 {
87 }
88
89 /// <summary>
90 /// Creates a normalized Plane directly without normalization cost.
91 /// </summary>
92 /// <remarks>
93 /// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors
94 /// by calling this function.
95 /// </remarks>
96 /// <param name="unitNormal">A non-zero vector that is perpendicular to the plane. It must be unit length.</param>
97 /// <param name="distance">Distance from the origin along the normal. A negative value moves the plane in the
98 /// same direction as the normal while a positive value moves it in the opposite direction.</param>
99 /// <returns>Normalized Plane constructed from given inputs.</returns>
100 [MethodImpl(MethodImplOptions.AggressiveInlining)]
101 public static Plane CreateFromUnitNormalAndDistance(float3 unitNormal, float distance)
102 {
103 return new Plane { NormalAndDistance = new float4(unitNormal, distance) };
104 }
105
106 /// <summary>
107 /// Creates a normalized Plane without normalization cost.
108 /// </summary>
109 /// <remarks>
110 /// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors
111 /// by calling this function.
112 /// </remarks>
113 /// <param name="unitNormal">A non-zero vector that is perpendicular to the plane. It must be unit length.</param>
114 /// <param name="pointInPlane">A point that lies in the plane.</param>
115 /// <returns>Normalized Plane constructed from given inputs.</returns>
116 [MethodImpl(MethodImplOptions.AggressiveInlining)]
117 public static Plane CreateFromUnitNormalAndPointInPlane(float3 unitNormal, float3 pointInPlane)
118 {
119 return new Plane { NormalAndDistance = new float4(unitNormal, -math.dot(unitNormal, pointInPlane)) };
120 }
121
122 /// <summary>
123 /// Get/set the normal vector of the plane.
124 /// </summary>
125 /// <remarks>
126 /// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but
127 /// (A, B, C) is not unit length, then you must normalize the plane by calling <see cref="Normalize(Plane)"/>.
128 /// </remarks>
129 public float3 Normal
130 {
131 get => NormalAndDistance.xyz;
132 set => NormalAndDistance.xyz = value;
133 }
134
135 /// <summary>
136 /// Get/set the distance of the plane from the origin. May be a negative value.
137 /// </summary>
138 /// <remarks>
139 /// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but
140 /// (A, B, C) is not unit length, then you must normalize the plane by calling <see cref="Normalize(Plane)"/>.
141 /// </remarks>
142 public float Distance
143 {
144 get => NormalAndDistance.w;
145 set => NormalAndDistance.w = value;
146 }
147
148 /// <summary>
149 /// Normalizes the given Plane.
150 /// </summary>
151 /// <param name="plane">Plane to normalize.</param>
152 /// <returns>Normalized Plane.</returns>
153 [MethodImpl(MethodImplOptions.AggressiveInlining)]
154 public static Plane Normalize(Plane plane)
155 {
156 return new Plane { NormalAndDistance = Normalize(plane.NormalAndDistance) };
157 }
158
159 /// <summary>
160 /// Normalizes the plane represented by the given plane coefficients.
161 /// </summary>
162 /// <remarks>
163 /// The plane coefficients are A, B, C, D and stored in that order in the <see cref="float4"/>.
164 /// </remarks>
165 /// <param name="planeCoefficients">Plane coefficients A, B, C, D stored in x, y, z, w (respectively).</param>
166 /// <returns>Normalized plane coefficients.</returns>
167 [MethodImpl(MethodImplOptions.AggressiveInlining)]
168 public static float4 Normalize(float4 planeCoefficients)
169 {
170 float recipLength = math.rsqrt(math.lengthsq(planeCoefficients.xyz));
171 return new Plane { NormalAndDistance = planeCoefficients * recipLength };
172 }
173
174 /// <summary>
175 /// Get the signed distance from the point to the plane.
176 /// </summary>
177 /// <remarks>
178 /// Plane must be normalized prior to calling this function. Distance is positive if point is on side of the
179 /// plane the normal points to, negative if on the opposite side and zero if the point lies in the plane.
180 /// Avoid comparing equality with 0.0f when testing if a point lies exactly in the plane and use an approximate
181 /// comparison instead.
182 /// </remarks>
183 /// <param name="point">Point to find the signed distance with.</param>
184 /// <returns>Signed distance of the point from the plane.</returns>
185 [MethodImpl(MethodImplOptions.AggressiveInlining)]
186 public float SignedDistanceToPoint(float3 point)
187 {
188 CheckPlaneIsNormalized();
189 return math.dot(NormalAndDistance, new float4(point, 1.0f));
190 }
191
192 /// <summary>
193 /// Projects the given point onto the plane.
194 /// </summary>
195 /// <remarks>
196 /// Plane must be normalized prior to calling this function. The result is the position closest to the point
197 /// that still lies in the plane.
198 /// </remarks>
199 /// <param name="point">Point to project onto the plane.</param>
200 /// <returns>Projected point that's inside the plane.</returns>
201 [MethodImpl(MethodImplOptions.AggressiveInlining)]
202 public float3 Projection(float3 point)
203 {
204 CheckPlaneIsNormalized();
205 return point - Normal * SignedDistanceToPoint(point);
206 }
207
208 /// <summary>
209 /// Flips the plane so the normal points in the opposite direction.
210 /// </summary>
211 public Plane Flipped => new Plane { NormalAndDistance = -NormalAndDistance };
212
213 /// <summary>
214 /// Implicitly converts a <see cref="Plane"/> to <see cref="float4"/>.
215 /// </summary>
216 /// <param name="plane">Plane to convert.</param>
217 /// <returns>A <see cref="float4"/> representing the plane.</returns>
218 [MethodImpl(MethodImplOptions.AggressiveInlining)]
219 public static implicit operator float4(Plane plane) => plane.NormalAndDistance;
220
221 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
222 void CheckPlaneIsNormalized()
223 {
224 float ll = math.lengthsq(Normal.xyz);
225 const float lowerBound = 0.999f * 0.999f;
226 const float upperBound = 1.001f * 1.001f;
227
228 if (ll < lowerBound || ll > upperBound)
229 {
230 throw new System.ArgumentException("Plane must be normalized. Call Plane.Normalize() to normalize plane.");
231 }
232 }
233 }
234}