ClipPoly.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Geometry/AABox.h>
  6. JPH_NAMESPACE_BEGIN
  7. /// Clip inPolygonToClip against the positive halfspace of plane defined by inPlaneOrigin and inPlaneNormal.
  8. /// inPlaneNormal does not need to be normalized.
  9. template <class VERTEX_ARRAY>
  10. void ClipPolyVsPlane(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inPlaneOrigin, Vec3Arg inPlaneNormal, VERTEX_ARRAY &outClippedPolygon)
  11. {
  12. JPH_ASSERT(inPolygonToClip.size() >= 2);
  13. JPH_ASSERT(outClippedPolygon.empty());
  14. // Determine state of last point
  15. Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1];
  16. float prev_num = (inPlaneOrigin - e1).Dot(inPlaneNormal);
  17. bool prev_inside = prev_num < 0.0f;
  18. // Loop through all vertices
  19. for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j)
  20. {
  21. // Check if second point is inside
  22. Vec3Arg e2 = inPolygonToClip[j];
  23. float num = (inPlaneOrigin - e2).Dot(inPlaneNormal);
  24. bool cur_inside = num < 0.0f;
  25. // In -> Out or Out -> In: Add point on clipping plane
  26. if (cur_inside != prev_inside)
  27. {
  28. // Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X
  29. Vec3 e12 = e2 - e1;
  30. float denom = e12.Dot(inPlaneNormal);
  31. if (denom != 0.0f)
  32. outClippedPolygon.push_back(e1 + (prev_num / denom) * e12);
  33. else
  34. cur_inside = prev_inside; // Edge is parallel to plane, treat point as if it were on the same side as the last point
  35. }
  36. // Point inside, add it
  37. if (cur_inside)
  38. outClippedPolygon.push_back(e2);
  39. // Update previous state
  40. prev_num = num;
  41. prev_inside = cur_inside;
  42. e1 = e2;
  43. }
  44. }
  45. /// Clip polygon versus polygon.
  46. /// Both polygons are assumed to be in counter clockwise order.
  47. /// @param inClippingPolygonNormal is used to create planes of all edges in inClippingPolygon against which inPolygonToClip is clipped, inClippingPolygonNormal does not need to be normalized
  48. /// @param inClippingPolygon is the polygon which inClippedPolygon is clipped against
  49. /// @param inPolygonToClip is the polygon that is clipped
  50. /// @param outClippedPolygon will contain clipped polygon when function returns
  51. template <class VERTEX_ARRAY>
  52. void ClipPolyVsPoly(const VERTEX_ARRAY &inPolygonToClip, const VERTEX_ARRAY &inClippingPolygon, Vec3Arg inClippingPolygonNormal, VERTEX_ARRAY &outClippedPolygon)
  53. {
  54. JPH_ASSERT(inPolygonToClip.size() >= 2);
  55. JPH_ASSERT(inClippingPolygon.size() >= 3);
  56. VERTEX_ARRAY tmp_vertices[2];
  57. int tmp_vertices_idx = 0;
  58. for (typename VERTEX_ARRAY::size_type i = 0; i < inClippingPolygon.size(); ++i)
  59. {
  60. // Get edge to clip against
  61. Vec3 clip_e1 = inClippingPolygon[i];
  62. Vec3 clip_e2 = inClippingPolygon[(i + 1) % inClippingPolygon.size()];
  63. Vec3 clip_normal = inClippingPolygonNormal.Cross(clip_e2 - clip_e1); // Pointing inward to the clipping polygon
  64. // Get source and target polygon
  65. const VERTEX_ARRAY &src_polygon = (i == 0)? inPolygonToClip : tmp_vertices[tmp_vertices_idx];
  66. tmp_vertices_idx ^= 1;
  67. VERTEX_ARRAY &tgt_polygon = (i == inClippingPolygon.size() - 1)? outClippedPolygon : tmp_vertices[tmp_vertices_idx];
  68. tgt_polygon.clear();
  69. // Clip against the edge
  70. ClipPolyVsPlane(src_polygon, clip_e1, clip_normal, tgt_polygon);
  71. // Break out if no polygon left
  72. if (tgt_polygon.size() < 3)
  73. {
  74. outClippedPolygon.clear();
  75. break;
  76. }
  77. }
  78. }
  79. /// Clip inPolygonToClip against an edge, the edge is projected on inPolygonToClip using inClippingEdgeNormal.
  80. /// The positive half space (the side on the edge in the direction of inClippingEdgeNormal) is cut away.
  81. template <class VERTEX_ARRAY>
  82. void ClipPolyVsEdge(const VERTEX_ARRAY &inPolygonToClip, Vec3Arg inEdgeVertex1, Vec3Arg inEdgeVertex2, Vec3Arg inClippingEdgeNormal, VERTEX_ARRAY &outClippedPolygon)
  83. {
  84. JPH_ASSERT(inPolygonToClip.size() >= 3);
  85. JPH_ASSERT(outClippedPolygon.empty());
  86. // Get normal that is perpendicular to the edge and the clipping edge normal
  87. Vec3 edge = inEdgeVertex2 - inEdgeVertex1;
  88. Vec3 edge_normal = inClippingEdgeNormal.Cross(edge);
  89. // Project vertices of edge on inPolygonToClip
  90. Vec3 polygon_normal = (inPolygonToClip[2] - inPolygonToClip[0]).Cross(inPolygonToClip[1] - inPolygonToClip[0]);
  91. float polygon_normal_len_sq = polygon_normal.LengthSq();
  92. Vec3 v1 = inEdgeVertex1 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex1) * polygon_normal / polygon_normal_len_sq;
  93. Vec3 v2 = inEdgeVertex2 + polygon_normal.Dot(inPolygonToClip[0] - inEdgeVertex2) * polygon_normal / polygon_normal_len_sq;
  94. Vec3 v12 = v2 - v1;
  95. float v12_len_sq = v12.LengthSq();
  96. // Determine state of last point
  97. Vec3 e1 = inPolygonToClip[inPolygonToClip.size() - 1];
  98. float prev_num = (inEdgeVertex1 - e1).Dot(edge_normal);
  99. bool prev_inside = prev_num < 0.0f;
  100. // Loop through all vertices
  101. for (typename VERTEX_ARRAY::size_type j = 0; j < inPolygonToClip.size(); ++j)
  102. {
  103. // Check if second point is inside
  104. Vec3 e2 = inPolygonToClip[j];
  105. float num = (inEdgeVertex1 - e2).Dot(edge_normal);
  106. bool cur_inside = num < 0.0f;
  107. // In -> Out or Out -> In: Add point on clipping plane
  108. if (cur_inside != prev_inside)
  109. {
  110. // Solve: (X - inPlaneOrigin) . inPlaneNormal = 0 and X = e1 + t * (e2 - e1) for X
  111. Vec3 e12 = e2 - e1;
  112. float denom = e12.Dot(edge_normal);
  113. Vec3 clipped_point = e1 + (prev_num / denom) * e12;
  114. // Project point on line segment v1, v2 so see if it falls outside if the edge
  115. float projection = (clipped_point - v1).Dot(v12);
  116. if (projection < 0.0f)
  117. outClippedPolygon.push_back(v1);
  118. else if (projection > v12_len_sq)
  119. outClippedPolygon.push_back(v2);
  120. else
  121. outClippedPolygon.push_back(clipped_point);
  122. }
  123. // Update previous state
  124. prev_num = num;
  125. prev_inside = cur_inside;
  126. e1 = e2;
  127. }
  128. }
  129. /// Clip polygon vs axis aligned box, inPolygonToClip is assume to be in counter clockwise order.
  130. /// Output will be stored in outClippedPolygon. Everything inside inAABox will be kept.
  131. template <class VERTEX_ARRAY>
  132. void ClipPolyVsAABox(const VERTEX_ARRAY &inPolygonToClip, const AABox &inAABox, VERTEX_ARRAY &outClippedPolygon)
  133. {
  134. JPH_ASSERT(inPolygonToClip.size() >= 2);
  135. VERTEX_ARRAY tmp_vertices[2];
  136. int tmp_vertices_idx = 0;
  137. for (int coord = 0; coord < 3; ++coord)
  138. for (int side = 0; side < 2; ++side)
  139. {
  140. // Get plane to clip against
  141. Vec3 origin = Vec3::sZero(), normal = Vec3::sZero();
  142. if (side == 0)
  143. {
  144. normal.SetComponent(coord, 1.0f);
  145. origin.SetComponent(coord, inAABox.mMin[coord]);
  146. }
  147. else
  148. {
  149. normal.SetComponent(coord, -1.0f);
  150. origin.SetComponent(coord, inAABox.mMax[coord]);
  151. }
  152. // Get source and target polygon
  153. const VERTEX_ARRAY &src_polygon = tmp_vertices_idx == 0? inPolygonToClip : tmp_vertices[tmp_vertices_idx & 1];
  154. tmp_vertices_idx++;
  155. VERTEX_ARRAY &tgt_polygon = tmp_vertices_idx == 6? outClippedPolygon : tmp_vertices[tmp_vertices_idx & 1];
  156. tgt_polygon.clear();
  157. // Clip against the edge
  158. ClipPolyVsPlane(src_polygon, origin, normal, tgt_polygon);
  159. // Break out if no polygon left
  160. if (tgt_polygon.size() < 3)
  161. {
  162. outClippedPolygon.clear();
  163. return;
  164. }
  165. // Flip normal
  166. normal = -normal;
  167. }
  168. }
  169. JPH_NAMESPACE_END