iMSTK
Interactive Medical Simulation Toolkit
CutHelp.h
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #pragma once
8 
9 #include "imstkMath.h"
10 #include "imstkTypes.h"
11 
12 #include <array>
13 
14 using namespace imstk;
15 
19 static bool
20 isIntersect(const double a, const double b, const double c, const double d)
21 {
22  return ((a <= d && a >= c) || (c <= b && c >= a)) ? true : false;
23 }
24 
29 static bool
30 segmentToPlane(const Vec3d& a, const Vec3d& b, const Vec3d& n, const Vec3d& planePt, Vec3d& intersectionPt)
31 {
32  Vec3d dir = b - a;
33  double length = dir.norm();
34  dir.normalize();
35  double denom = n.dot(dir);
36 
37  // Line is tangent, or nearly
38  if (std::abs(denom) < 0.00000001)
39  {
40  return false;
41  }
42 
43  double t = (planePt - a).dot(n) / denom;
44 
45  // Starts outside of plane
46  if (t < 0.0)
47  {
48  return false;
49  }
50  if (t > length)
51  {
52  return false;
53  }
54 
55  intersectionPt = a + t * dir;
56  return true;
57 }
58 
59 static void
60 orientTet(std::array<Vec3d, 4>& tet)
61 {
62  Vec3d a = tet[1] - tet[0];
63  Vec3d b = tet[2] - tet[0];
64  Vec3d c = tet[3] - tet[0];
65  if (a.cross(b).dot(c) < 0)
66  {
67  std::swap(tet[2], tet[3]);
68  }
69 }
70 
74 static bool
75 splitTet(const std::array<Vec3d, 4>& inputTetVerts,
76  const Vec3d& planePos, const Vec3d& planeNormal,
77  std::list<std::array<Vec3d, 4>>& resultTetVerts)
78 {
79  bool side[4];
80  int outCount = 0; // Num vertices that lie in front of plane
81  int inCount = 0; // Num vertices that lie behind plane
82  for (int j = 0; j < 4; j++)
83  {
84  const Vec3d& vert = inputTetVerts[j];
85  const double proj = (vert - planePos).dot(planeNormal);
86  if (proj >= 0)
87  {
88  outCount++;
89  side[j] = false;
90  }
91  if (proj < 0)
92  {
93  side[j] = true;
94  inCount++;
95  }
96  }
97 
98  // If all vertices lie on one side then its not intersecting
99  if (outCount == 0 || inCount == 0)
100  {
101  return false;
102  }
103 
104  Vec3d iPts[4];
105  std::pair<int, int> iEdges[4];
106  int iPtSize = 0;
107  // For every segment of the tetrahedron
108  for (int j = 0; j < 4; j++)
109  {
110  for (int k = j + 1; k < 4; k++)
111  {
112  // If that segment has verts on both sides of the plane, it crosses
113  if (side[j] != side[k])
114  {
115  // Compute intersection point
116  Vec3d iPt;
117  if (segmentToPlane(inputTetVerts[j], inputTetVerts[k], planeNormal, planePos, iPt))
118  {
119  iEdges[iPtSize] = std::pair<int, int>(j, k);
120  iPts[iPtSize] = iPt;
121  iPtSize++;
122  }
123  }
124  }
125  }
126 
127  // There are two cases
128  // either 3 verts on one side, 1 on the other
129  // or 2 verts on one side, 2 on the other
130  if (iPtSize == 3) // 3 intersection points
131  {
132  // Identify the vertex of the isolated vert
133  int isolatedVertId = -1;
134  int otherVertIds[3];
135  {
136  int falseCount = 0;
137  int trueCount = 0;
138  // All true but one
139  for (int i = 0; i < 4; i++)
140  {
141  trueCount += static_cast<int>(side[i]);
142  falseCount += static_cast<int>(!side[i]);
143  }
144 
145  // False is the ioslated vert
146  if (trueCount == 3 && falseCount == 1)
147  {
148  int otherVertSize = 0;
149  for (int i = 0; i < 4; i++)
150  {
151  if (side[i])
152  {
153  otherVertIds[otherVertSize++] = i;
154  }
155  else
156  {
157  isolatedVertId = i;
158  }
159  }
160  }
161  // True is the isolated vert
162  else // falseCount == 3 && trueCount == 1
163  {
164  int otherVertSize = 0;
165  for (int i = 0; i < 4; i++)
166  {
167  if (side[i])
168  {
169  isolatedVertId = i;
170  }
171  else
172  {
173  otherVertIds[otherVertSize++] = i;
174  }
175  }
176  }
177  }
178 
179  // Now that we've identified the points that lie on which sides we can generate our 4 tets
180  // we do so disregarding orientation
181 
182  // On one side of the plane we have a singular tet
183  // formed by the intersection verts on the plane
184  std::array<Vec3d, 4> tet1;
185  tet1[0] = iPts[0];
186  tet1[1] = iPts[1];
187  tet1[2] = iPts[2];
188  tet1[3] = inputTetVerts[isolatedVertId];
189  orientTet(tet1);
190  resultTetVerts.push_back(tet1);
191 
192  // On the other side of the plane we have a 6vert 5face polyhedron
193  // that is split into 3 tets here
194  // It's like a triangular prism, but not a prism.
195  // Two triangular faces, 3 quad faces
196  // One of the triangular faces is incident with the plane (formed by the 3 iPts)
197  std::array<Vec3d, 4> tet2;
198  tet2[0] = iPts[0];
199  tet2[1] = iPts[1];
200  tet2[2] = iPts[2];
201  tet2[3] = inputTetVerts[otherVertIds[0]];
202  orientTet(tet2);
203  resultTetVerts.push_back(tet2);
204 
205  std::array<Vec3d, 4> tet3;
206  tet3[0] = iPts[1];
207  tet3[1] = iPts[2];
208  tet3[2] = inputTetVerts[otherVertIds[0]];
209  tet3[3] = inputTetVerts[otherVertIds[2]];
210  orientTet(tet3);
211  resultTetVerts.push_back(tet3);
212 
213  std::array<Vec3d, 4> tet4;
214  tet4[0] = iPts[1];
215  tet4[1] = inputTetVerts[otherVertIds[0]];
216  tet4[2] = inputTetVerts[otherVertIds[1]];
217  tet4[3] = inputTetVerts[otherVertIds[2]];
218  orientTet(tet4);
219  resultTetVerts.push_back(tet4);
220  }
221  else // 4 intersection points
222  {
223  // Identify the 2 vertices on each side
224  int inVertIds[2];
225  int outVertIds[2];
226  int inVertSize = 0;
227  int outVertSize = 0;
228  for (int i = 0; i < 4; i++)
229  {
230  if (side[i])
231  {
232  outVertIds[outVertSize++] = i;
233  }
234  else
235  {
236  inVertIds[inVertSize++] = i;
237  }
238  }
239 
240  // Outer wedge
241  {
242  // We know we have two verts on one side. outVertIds[0] and [1]
243  // We have 4 verts on the intersecting quad
244  // 2 of these verts connect to outVertIds[0]
245  // the other 2 to outVertIds[1]
246  // forming two triangles (ends of the wedge)
247 
248  // We need to know which 2 verts correspond to outVertIds[0]
249  int iPts0[2];
250  int iPts0Size = 0;
251  int iPts1[2];
252  int iPts1Size = 0;
253  for (int i = 0; i < iPtSize; i++) // For every intersection point
254  {
255  if (iEdges[i].first == outVertIds[0]
256  || iEdges[i].second == outVertIds[0])
257  {
258  iPts0[iPts0Size++] = i;
259  }
260  else
261  {
262  iPts1[iPts1Size++] = i;
263  }
264  }
265 
266  std::array<Vec3d, 4> tet1;
267  tet1[0] = iPts[iPts0[0]];
268  tet1[1] = iPts[iPts0[1]];
269  tet1[2] = inputTetVerts[outVertIds[0]];
270  tet1[3] = inputTetVerts[outVertIds[1]];
271  orientTet(tet1);
272  resultTetVerts.push_back(tet1);
273 
274  std::array<Vec3d, 4> tet2;
275  tet2[0] = iPts[iPts0[0]];
276  tet2[1] = iPts[iPts0[1]];
277  tet2[2] = iPts[iPts1[0]];
278  tet2[3] = inputTetVerts[outVertIds[1]];
279  orientTet(tet2);
280  resultTetVerts.push_back(tet2);
281 
282  std::array<Vec3d, 4> tet3;
283  tet3[0] = iPts[iPts0[1]];
284  tet3[1] = iPts[iPts1[0]];
285  tet3[2] = iPts[iPts1[1]];
286  tet3[3] = inputTetVerts[outVertIds[1]];
287  orientTet(tet3);
288  resultTetVerts.push_back(tet3);
289  }
290  // Inner wedge
291  {
292  // We need to know which 2 verts correspond to outVertIds[0]
293  int iPts0[2];
294  int iPts0Size = 0;
295  int iPts1[2];
296  int iPts1Size = 0;
297  for (int i = 0; i < iPtSize; i++) // For every intersection point
298  {
299  if (iEdges[i].first == inVertIds[0]
300  || iEdges[i].second == inVertIds[0])
301  {
302  iPts0[iPts0Size++] = i;
303  }
304  else
305  {
306  iPts1[iPts1Size++] = i;
307  }
308  }
309 
310  std::array<Vec3d, 4> tet1;
311  tet1[0] = iPts[iPts0[0]];
312  tet1[1] = iPts[iPts0[1]];
313  tet1[2] = inputTetVerts[inVertIds[0]];
314  tet1[3] = inputTetVerts[inVertIds[1]];
315  orientTet(tet1);
316  resultTetVerts.push_back(tet1);
317 
318  std::array<Vec3d, 4> tet2;
319  tet2[0] = iPts[iPts0[0]];
320  tet2[1] = iPts[iPts0[1]];
321  tet2[2] = iPts[iPts1[0]];
322  tet2[3] = inputTetVerts[inVertIds[1]];
323  orientTet(tet2);
324  resultTetVerts.push_back(tet2);
325 
326  std::array<Vec3d, 4> tet3;
327  tet3[0] = iPts[iPts0[1]];
328  tet3[1] = iPts[iPts1[0]];
329  tet3[2] = iPts[iPts1[1]];
330  tet3[3] = inputTetVerts[inVertIds[1]];
331  orientTet(tet3);
332  resultTetVerts.push_back(tet3);
333  }
334  }
335  return true;
336 }
337 
349 static std::list<std::array<Vec3d, 4>>
350 split(const std::array<Vec3d, 4>& inputTetVerts,
351  const Vec3d& planeOrigin,
352  const Vec3d& u, const double width,
353  const Vec3d& v, const double height,
354  const Vec3d& n)
355 {
356  bool side[4];
357  Vec3d proj[4];
358  int outCount = 0; // Num vertices that lie in front of plane
359  int inCount = 0; // Num vertices that lie behind plane
360  for (int i = 0; i < 4; i++)
361  {
362  const Vec3d& vert = inputTetVerts[i];
363  proj[i][2] = (vert - planeOrigin).dot(n);
364  if (proj[i][2] >= 0)
365  {
366  outCount++;
367  side[i] = false;
368  }
369  if (proj[i][2] < 0)
370  {
371  side[i] = true;
372  inCount++;
373  }
374  }
375  // If all vertices lie on one side then it's not intersecting
376  if (outCount == 0 || inCount == 0)
377  {
378  return std::list<std::array<Vec3d, 4>>();
379  }
380 
381  // Next cull by projection of bounds on plane (in a SAT manner)
382  Vec2d min = Vec2d(IMSTK_DOUBLE_MAX, IMSTK_DOUBLE_MAX);
383  Vec2d max = Vec2d(IMSTK_DOUBLE_MIN, IMSTK_DOUBLE_MIN);
384  for (int i = 0; i < 4; i++)
385  {
386  // Project onto the basis of the plane
387  const Vec3d& vert = inputTetVerts[i];
388  proj[i][0] = (vert - planeOrigin).dot(u);
389  proj[i][1] = (vert - planeOrigin).dot(v);
390 
391  min[0] = std::min(proj[i][0], min[0]);
392  max[0] = std::max(proj[i][0], max[0]);
393 
394  min[1] = std::min(proj[i][1], min[1]);
395  max[1] = std::max(proj[i][1], max[1]);
396  }
397 
398  // If either range is not intersecting then the plane is not within the
399  // bounds of the finite plane/quad
400  if (!isIntersect(min[0], max[0], -width, width)
401  || !isIntersect(min[1], max[1], -height, height))
402  {
403  return std::list<std::array<Vec3d, 4>>();
404  }
405 
406  // Perform split with the plane and return resulting tets
407  std::list<std::array<Vec3d, 4>> newTets;
408  splitTet(inputTetVerts, planeOrigin, n, newTets);
409  return newTets;
410 }
411 
415 static bool
416 splitTest(const std::array<Vec3d, 4>& inputTetVerts,
417  const Vec3d& planeOrigin,
418  const Vec3d& u, const double width,
419  const Vec3d& v, const double height,
420  const Vec3d& n)
421 {
422  bool side[4];
423  Vec3d proj[4];
424  int outCount = 0; // Num vertices that lie in front of plane
425  int inCount = 0; // Num vertices that lie behind plane
426  for (int i = 0; i < 4; i++)
427  {
428  const Vec3d& vert = inputTetVerts[i];
429  proj[i][2] = (vert - planeOrigin).dot(n);
430  if (proj[i][2] >= 0)
431  {
432  outCount++;
433  side[i] = false;
434  }
435  if (proj[i][2] < 0)
436  {
437  side[i] = true;
438  inCount++;
439  }
440  }
441  // If all vertices lie on one side then it's not intersecting
442  if (outCount == 0 || inCount == 0)
443  {
444  return false;
445  }
446 
447  // Next cull by projection of bounds on plane (in a SAT manner)
448  Vec2d min = Vec2d(IMSTK_DOUBLE_MAX, IMSTK_DOUBLE_MAX);
449  Vec2d max = Vec2d(IMSTK_DOUBLE_MIN, IMSTK_DOUBLE_MIN);
450  for (int i = 0; i < 4; i++)
451  {
452  // Project onto the basis of the plane
453  const Vec3d& vert = inputTetVerts[i];
454  proj[i][0] = (vert - planeOrigin).dot(u);
455  proj[i][1] = (vert - planeOrigin).dot(v);
456 
457  min[0] = std::min(proj[i][0], min[0]);
458  max[0] = std::max(proj[i][0], max[0]);
459 
460  min[1] = std::min(proj[i][1], min[1]);
461  max[1] = std::max(proj[i][1], max[1]);
462  }
463 
464  // If either range is not intersecting then the plane is not within the
465  // bounds of the finite plane/quad
466  if (!isIntersect(min[0], max[0], -width, width)
467  || !isIntersect(min[1], max[1], -height, height))
468  {
469  return false;
470  }
471 
472  return true;
473 }
Compound Geometry.