No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Plane.cs 11KB

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