1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    UnitTestTriangleIntersection.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include <algorithm>
17 #include <array>
18 #include <cmath>
19 #include <sstream>
20 
21 #include "vtkLine.h"
22 #include "vtkMath.h"
23 #include "vtkMinimalStandardRandomSequence.h"
24 #include "vtkSmartPointer.h"
25 #include "vtkTriangle.h"
26 
27 #define VISUAL_DEBUG 0
28 
29 #ifdef VISUAL_DEBUG
30 #include <vtkActor.h>
31 #include <vtkCellArray.h>
32 #include <vtkDoubleArray.h>
33 #include <vtkPointData.h>
34 #include <vtkPolyData.h>
35 #include <vtkPolyDataMapper.h>
36 #include <vtkProperty.h>
37 #include <vtkRenderWindow.h>
38 #include <vtkRenderWindowInteractor.h>
39 #include <vtkRenderer.h>
40 
41 namespace
42 {
DrawTriangles(double * p1,double * q1,double * r1,double * p2,double * q2,double * r2)43 void DrawTriangles(double* p1, double* q1, double* r1, double* p2, double* q2, double* r2)
44 {
45   // Create a triangle
46   vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
47   vtkIdType pid[6];
48   pid[0] = points->InsertNextPoint(p1[0], p1[1], p1[2]);
49   pid[1] = points->InsertNextPoint(q1[0], q1[1], q1[2]);
50   pid[2] = points->InsertNextPoint(r1[0], r1[1], r1[2]);
51   pid[3] = points->InsertNextPoint(p2[0], p2[1], p2[2]);
52   pid[4] = points->InsertNextPoint(q2[0], q2[1], q2[2]);
53   pid[5] = points->InsertNextPoint(r2[0], r2[1], r2[2]);
54 
55   vtkSmartPointer<vtkCellArray> verts = vtkSmartPointer<vtkCellArray>::New();
56   for (int i = 0; i < 6; i++)
57   {
58     verts->InsertNextCell(1, &pid[i]);
59   }
60 
61   vtkSmartPointer<vtkTriangle> triangle1 = vtkSmartPointer<vtkTriangle>::New();
62   triangle1->GetPointIds()->SetId(0, 0);
63   triangle1->GetPointIds()->SetId(1, 1);
64   triangle1->GetPointIds()->SetId(2, 2);
65 
66   vtkSmartPointer<vtkTriangle> triangle2 = vtkSmartPointer<vtkTriangle>::New();
67   triangle2->GetPointIds()->SetId(0, 3);
68   triangle2->GetPointIds()->SetId(1, 4);
69   triangle2->GetPointIds()->SetId(2, 5);
70 
71   vtkSmartPointer<vtkCellArray> triangles = vtkSmartPointer<vtkCellArray>::New();
72   triangles->InsertNextCell(triangle1);
73   triangles->InsertNextCell(triangle2);
74 
75   vtkSmartPointer<vtkDoubleArray> pointIds = vtkSmartPointer<vtkDoubleArray>::New();
76   pointIds->SetNumberOfTuples(6);
77   pointIds->SetTuple1(0, 0.);
78   pointIds->SetTuple1(1, 1.);
79   pointIds->SetTuple1(2, 2.);
80   pointIds->SetTuple1(3, 3.);
81   pointIds->SetTuple1(4, 4.);
82   pointIds->SetTuple1(5, 5.);
83 
84   // Create a polydata object
85   vtkSmartPointer<vtkPolyData> trianglePolyData = vtkSmartPointer<vtkPolyData>::New();
86 
87   // Add the geometry and topology to the polydata
88   trianglePolyData->SetPoints(points);
89   trianglePolyData->SetVerts(verts);
90   trianglePolyData->SetPolys(triangles);
91   trianglePolyData->GetPointData()->SetScalars(pointIds);
92 
93   // Create mapper and actor
94   vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
95   mapper->SetInputData(trianglePolyData);
96   mapper->SetScalarRange(0., 5.);
97   vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
98   actor->SetMapper(mapper);
99   actor->GetProperty()->SetPointSize(5);
100 
101   // Create a renderer, render window, and an interactor
102   vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
103   vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
104   renderWindow->AddRenderer(renderer);
105   vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
106     vtkSmartPointer<vtkRenderWindowInteractor>::New();
107   renderWindowInteractor->SetRenderWindow(renderWindow);
108 
109   // Add the actors to the scene
110   renderer->AddActor(actor);
111   renderer->SetBackground(.1, .2, .4); // Background color dark blue
112 
113   // Render and interact
114   renderWindow->Render();
115   renderWindowInteractor->Start();
116 }
117 }
118 
119 #endif
120 
121 namespace
122 {
123 const double EPSILON = 1.e-6;
124 
125 const int VTK_NO_INTERSECTION = 0;
126 const int VTK_YES_INTERSECTION = 1;
127 
128 typedef vtkMinimalStandardRandomSequence vtkRandom;
129 
TriangleToString(double * t1,double * t2,double * t3)130 std::string TriangleToString(double* t1, double* t2, double* t3)
131 {
132   std::stringstream stream;
133   stream << "(" << t1[0] << "," << t1[1] << "," << t1[2] << ") (" << t2[0] << "," << t2[1] << ","
134          << t2[2] << ") (" << t3[0] << "," << t3[1] << "," << t3[2] << ")";
135   return stream.str();
136 }
137 
ProjectPointOntoPlane(double * o,double * n,double * p)138 void ProjectPointOntoPlane(double* o, double* n, double* p)
139 {
140   // Project point p onto a plane that crosses through point o and has normal n.
141 
142   double pMinuso[3];
143   vtkMath::Subtract(p, o, pMinuso);
144 
145   double dot = vtkMath::Dot(pMinuso, n);
146 
147   for (int i = 0; i < 3; i++)
148   {
149     p[i] -= dot * n[i];
150   }
151 }
152 
GeneratePointInPlane(vtkRandom * seq,double * o,double * n,double * p)153 void GeneratePointInPlane(vtkRandom* seq, double* o, double* n, double* p)
154 {
155   // Generate a point p that lies in a plane that crosses through point o and
156   // has normal n.
157 
158   for (int i = 0; i < 3; i++)
159   {
160     seq->Next();
161     p[i] = -1. + 2. * seq->GetValue();
162   }
163 
164   ProjectPointOntoPlane(o, n, p);
165 }
166 
ReflectPointThroughPlane(double * o,double * n,double * p)167 void ReflectPointThroughPlane(double* o, double* n, double* p)
168 {
169   // Reflect point p through a plane that crosses through point o and has normal
170   // n.
171 
172   double dot = 0.;
173   for (int i = 0; i < 3; i++)
174   {
175     dot += (p[i] - o[i]) * n[i];
176   }
177 
178   for (int i = 0; i < 3; i++)
179   {
180     p[i] -= dot * n[i];
181   }
182 }
183 
GeneratePointInHalfPlane(vtkRandom * seq,double * o,double * n,double * o2,double * n2,double * p)184 void GeneratePointInHalfPlane(
185   vtkRandom* seq, double* o, double* n, double* o2, double* n2, double* p)
186 {
187   // Generate a point p that lies in a plane that crosses through point o, has
188   // normal n, and lies on the positive side of the halfspace defined by point
189   // o1 and normal n2.
190 
191   GeneratePointInPlane(seq, o, n, p);
192 
193   double pMinuso2[3];
194   vtkMath::Subtract(p, o2, pMinuso2);
195 
196   if (vtkMath::Dot(n2, pMinuso2) < 0.)
197   {
198     ReflectPointThroughPlane(o2, n2, p);
199   }
200 }
201 
GenerateTriangleInPlane(vtkRandom * seq,double * o,double * n,double * t1,double * t2,double * t3)202 void GenerateTriangleInPlane(
203   vtkRandom* seq, double* o, double* n, double* t1, double* t2, double* t3)
204 {
205   do
206   {
207     GeneratePointInPlane(seq, o, n, t1);
208     GeneratePointInPlane(seq, o, n, t2);
209     GeneratePointInPlane(seq, o, n, t3);
210   } while (vtkTriangle::TriangleArea(t1, t2, t3) < EPSILON);
211 }
212 
GenerateTriangleInHalfPlane(vtkRandom * seq,double * o,double * n,double * o2,double * n2,double * t1,double * t2,double * t3)213 void GenerateTriangleInHalfPlane(
214   vtkRandom* seq, double* o, double* n, double* o2, double* n2, double* t1, double* t2, double* t3)
215 {
216   do
217   {
218     GeneratePointInHalfPlane(seq, o, n, o2, n2, t1);
219     GeneratePointInHalfPlane(seq, o, n, o2, n2, t2);
220     GeneratePointInHalfPlane(seq, o, n, o2, n2, t3);
221   } while (vtkTriangle::TriangleArea(t1, t2, t3) < EPSILON);
222 }
223 
RandomSphere(vtkRandom * seq,const double radius,const double * offset,double * value)224 void RandomSphere(vtkRandom* seq, const double radius, const double* offset, double* value)
225 {
226   // Generate a point on a sphere.
227 
228   seq->Next();
229   double theta = 2. * vtkMath::Pi() * seq->GetValue();
230   seq->Next();
231   double phi = vtkMath::Pi() * seq->GetValue();
232   value[0] = radius * cos(theta) * sin(phi) + offset[0];
233   value[1] = radius * sin(theta) * sin(phi) + offset[1];
234   value[2] = radius * cos(phi) + offset[2];
235 }
236 
TestNegativeResult(vtkRandom * seq,unsigned nTests)237 int TestNegativeResult(vtkRandom* seq, unsigned nTests)
238 {
239   double origin[3] = { 0., 0., 0. };
240   double n1[3], n2[3], o1[3], t1[3][3], t2[3][3];
241 
242   for (unsigned test = 0; test < nTests; test++)
243   {
244     RandomSphere(seq, 1, origin, n1);
245     RandomSphere(seq, 1, origin, n2);
246 
247     for (int i = 0; i < 3; i++)
248     {
249       seq->Next();
250       o1[i] = seq->GetValue();
251     }
252 
253     GenerateTriangleInPlane(seq, o1, n1, t1[0], t1[1], t1[2]);
254     double dividingPlaneOrigin[3];
255     for (int i = 0; i < 3; i++)
256     {
257       dividingPlaneOrigin[i] = t1[0][i] + EPSILON * n1[i];
258     }
259     GenerateTriangleInHalfPlane(seq, o1, n2, dividingPlaneOrigin, n1, t2[0], t2[1], t2[2]);
260 
261     int returnValue = vtkTriangle::TrianglesIntersect(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
262 
263     if (returnValue != VTK_NO_INTERSECTION)
264     {
265       std::cout << "Triangle " << TriangleToString(t1[0], t1[1], t1[2]) << std::endl;
266       std::cout << " intersects " << TriangleToString(t2[0], t2[1], t2[2]) << " and shouldn't."
267                 << std::endl;
268 #ifdef VISUAL_DEBUG
269       DrawTriangles(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
270 #endif
271       return EXIT_FAILURE;
272     }
273   }
274 
275   return EXIT_SUCCESS;
276 }
277 
TestCoplanarNegativeResult(vtkRandom * seq,unsigned nTests)278 int TestCoplanarNegativeResult(vtkRandom* seq, unsigned nTests)
279 {
280   double origin[3] = { 0., 0., 0. };
281   double n1[3], n2[3], nn2[3], o1[3], o2[3], t1[3][3], t2[3][3];
282 
283   for (unsigned test = 0; test < nTests; test++)
284   {
285     RandomSphere(seq, 1, origin, n1);
286     RandomSphere(seq, 1, origin, n2);
287 
288     n1[0] = n1[1] = 0.;
289     n1[2] = 1.;
290 
291     n2[0] = 1.;
292     n2[1] = n2[2] = 0.;
293 
294     double dot = vtkMath::Dot(n1, n2);
295 
296     for (int i = 0; i < 3; i++)
297     {
298       n2[i] -= dot * n1[i];
299       nn2[i] = -1. * n2[i];
300       seq->Next();
301       o1[i] = seq->GetValue();
302     }
303 
304     o1[0] = o1[1] = o1[2] = 1.;
305 
306     GeneratePointInPlane(seq, o1, n1, o2);
307 
308     o2[0] = o2[1] = 0.;
309     o2[2] = 1.;
310 
311     double dividingPlaneOrigin[3];
312     for (int i = 0; i < 3; i++)
313     {
314       dividingPlaneOrigin[i] = o2[i] + 10000. * EPSILON * nn2[i];
315     }
316 
317     GenerateTriangleInHalfPlane(seq, o1, n1, o2, n2, t1[0], t1[1], t1[2]);
318     GenerateTriangleInHalfPlane(seq, o1, n1, dividingPlaneOrigin, nn2, t2[0], t2[1], t2[2]);
319 
320     int returnValue = vtkTriangle::TrianglesIntersect(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
321 
322     if (returnValue != VTK_NO_INTERSECTION)
323     {
324       std::cout << "Triangle " << TriangleToString(t1[0], t1[1], t1[2]) << std::endl;
325       std::cout << " intersects " << TriangleToString(t2[0], t2[1], t2[2]) << " and shouldn't."
326                 << std::endl;
327 #ifdef VISUAL_DEBUG
328       DrawTriangles(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
329 #endif
330       return EXIT_FAILURE;
331     }
332   }
333 
334   return EXIT_SUCCESS;
335 }
336 
ProjectAlongRay(vtkRandom * seq,double * x0,double * x1,double * p)337 void ProjectAlongRay(vtkRandom* seq, double* x0, double* x1, double* p)
338 {
339   // Assign point p a value along the ray originating at x0 and passing through
340   // x1. The resulting line segment (x0, p) will cross through x1.
341 
342   double n[3];
343   vtkMath::Subtract(x1, x0, n);
344   vtkMath::Normalize(n);
345 
346   seq->Next();
347   double len = seq->GetValue();
348   for (int i = 0; i < 3; i++)
349   {
350     p[i] = x1[i] + len * n[i];
351   }
352 }
353 
GenerateOverlappingSegments(vtkRandom * seq,double * p1,double * p2,double * x1,double * x2,double * y1,double * y2)354 void GenerateOverlappingSegments(
355   vtkRandom* seq, double* p1, double* p2, double* x1, double* x2, double* y1, double* y2)
356 {
357   // Given a line through (p1,p2), generate line segments (x1,x2) and (y1,y2)
358   // that lie on the line and overlap.
359 
360   std::vector<double> random(4, 0.);
361   for (int i = 0; i < 4; i++)
362   {
363     seq->Next();
364     random[i] = seq->GetValue();
365   }
366   std::sort(random.begin(), random.end());
367 
368   seq->Next();
369   double sequence = seq->GetValue();
370 
371   double par[4]; // parametric values for x1,x2,y1,y2
372 
373   if (sequence < .25)
374   {
375     par[0] = random[0];
376     par[1] = random[2];
377     par[2] = random[1];
378     par[3] = random[3];
379   }
380   else if (sequence < .5)
381   {
382     par[0] = random[2];
383     par[1] = random[0];
384     par[2] = random[3];
385     par[3] = random[1];
386   }
387   else if (sequence < .75)
388   {
389     par[0] = random[0];
390     par[1] = random[3];
391     par[2] = random[1];
392     par[3] = random[2];
393   }
394   else
395   {
396     par[0] = random[1];
397     par[1] = random[2];
398     par[2] = random[0];
399     par[3] = random[3];
400   }
401 
402   for (int i = 0; i < 3; i++)
403   {
404     x1[i] = p1[i] + par[0] * p2[i];
405     x2[i] = p1[i] + par[1] * p2[i];
406     y1[i] = p1[i] + par[2] * p2[i];
407     y2[i] = p1[i] + par[3] * p2[i];
408   }
409 }
410 
RandomPoint(vtkRandom * seq,double * p)411 void RandomPoint(vtkRandom* seq, double* p)
412 {
413   // Set p to be a random point in the box (-1,1) x (-1,1) x (-1,1).
414   for (int i = 0; i < 3; i++)
415   {
416     seq->Next();
417     p[i] = -1. + 2. * seq->GetValue();
418   }
419 }
420 
TestPositiveResult(vtkRandom * seq,unsigned nTests)421 int TestPositiveResult(vtkRandom* seq, unsigned nTests)
422 {
423   double p1[3], p2[3], l1[2][3], l2[2][3], t1[3][3], t2[3][3];
424 
425   for (unsigned test = 0; test < nTests; test++)
426   {
427     RandomPoint(seq, p1);
428     RandomPoint(seq, p2);
429     RandomPoint(seq, t1[0]);
430     RandomPoint(seq, t2[0]);
431 
432     GenerateOverlappingSegments(seq, p1, p2, l1[0], l1[1], l2[0], l2[1]);
433 
434     ProjectAlongRay(seq, t1[0], l1[0], t1[1]);
435     ProjectAlongRay(seq, t1[0], l1[1], t1[2]);
436     ProjectAlongRay(seq, t2[0], l2[0], t2[1]);
437     ProjectAlongRay(seq, t2[0], l2[1], t2[2]);
438 
439     int returnValue = vtkTriangle::TrianglesIntersect(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
440 
441     if (returnValue != VTK_YES_INTERSECTION)
442     {
443       std::cout << "Triangle " << TriangleToString(t1[0], t1[1], t1[2]) << std::endl;
444       std::cout << " does not intersect " << TriangleToString(t2[0], t2[1], t2[2]) << " and should."
445                 << std::endl;
446 #ifdef VISUAL_DEBUG
447       DrawTriangles(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
448 #endif
449       return EXIT_FAILURE;
450     }
451   }
452 
453   return EXIT_SUCCESS;
454 }
455 
TestCoplanarPositiveResult(vtkRandom * seq,unsigned nTests)456 int TestCoplanarPositiveResult(vtkRandom* seq, unsigned nTests)
457 {
458   double p1[3], p2[3], l1[2][3], l2[2][3], t1[3][3], t2[3][3], orgn[3], n[3];
459   double v1[3], v2[3];
460 
461   for (unsigned test = 0; test < nTests; test++)
462   {
463     RandomPoint(seq, p1);
464     RandomPoint(seq, p2);
465     RandomPoint(seq, t1[0]);
466     RandomPoint(seq, t2[0]);
467 
468     GenerateOverlappingSegments(seq, p1, p2, l1[0], l1[1], l2[0], l2[1]);
469 
470     ProjectAlongRay(seq, t1[0], l1[0], t1[1]);
471     ProjectAlongRay(seq, t1[0], l1[1], t1[2]);
472     ProjectAlongRay(seq, t2[0], l2[0], t2[1]);
473     ProjectAlongRay(seq, t2[0], l2[1], t2[2]);
474 
475     RandomPoint(seq, orgn);
476 
477     if (vtkTriangle::TriangleArea(t1[0], t1[1], t1[2]) <
478       vtkTriangle::TriangleArea(t2[0], t2[1], t2[2]))
479     {
480       for (unsigned i = 0; i < 3; i++)
481       {
482         v1[i] = t1[1][i] - t1[0][i];
483         v2[i] = t1[2][i] - t1[0][i];
484       }
485       vtkMath::Cross(v1, v2, n);
486     }
487     else
488     {
489       for (unsigned i = 0; i < 3; i++)
490       {
491         v1[i] = t2[1][i] - t2[0][i];
492         v2[i] = t2[2][i] - t2[0][i];
493       }
494       vtkMath::Cross(v1, v2, n);
495     }
496     vtkMath::Normalize(n);
497 
498     for (unsigned i = 0; i < 3; i++)
499     {
500       ProjectPointOntoPlane(orgn, n, t1[i]);
501       ProjectPointOntoPlane(orgn, n, t2[i]);
502     }
503 
504     int returnValue = vtkTriangle::TrianglesIntersect(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
505 
506     if (returnValue != VTK_YES_INTERSECTION)
507     {
508       std::cout << "Triangle " << TriangleToString(t1[0], t1[1], t1[2]) << std::endl;
509       std::cout << " does not intersect " << TriangleToString(t2[0], t2[1], t2[2]) << " and should."
510                 << std::endl;
511       return EXIT_FAILURE;
512     }
513   }
514 
515   return EXIT_SUCCESS;
516 }
517 
factorial(int n)518 int factorial(int n)
519 {
520   return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
521 }
522 
TestReciprocalResult(vtkRandom * seq,unsigned nTests)523 int TestReciprocalResult(vtkRandom* seq, unsigned nTests)
524 {
525   std::array<std::array<double, 3>, 6> p;
526 
527   nTests /= factorial(6);
528 
529   for (unsigned test = 0; test < nTests; test++)
530   {
531     for (int i = 0; i < 6; i++)
532     {
533       RandomPoint(seq, &p[i][0]);
534     }
535     std::sort(std::begin(p), std::end(p));
536     do
537     {
538       int returnValue1 =
539         vtkTriangle::TrianglesIntersect(&p[0][0], &p[1][0], &p[2][0], &p[3][0], &p[4][0], &p[5][0]);
540       int returnValue2 =
541         vtkTriangle::TrianglesIntersect(&p[3][0], &p[4][0], &p[5][0], &p[0][0], &p[1][0], &p[2][0]);
542 
543       if (returnValue1 != returnValue2)
544       {
545         std::cout << "Triangles " << TriangleToString(&p[0][0], &p[1][0], &p[2][0]) << " and "
546                   << TriangleToString(&p[3][0], &p[4][0], &p[5][0])
547                   << " disagree about intersection." << std::endl;
548         std::cout << "return values: " << returnValue1 << " " << returnValue2 << std::endl;
549 #ifdef VISUAL_DEBUG
550         DrawTriangles(&p[0][0], &p[1][0], &p[2][0], &p[3][0], &p[4][0], &p[5][0]);
551 #endif
552         return EXIT_FAILURE;
553       }
554     } while (std::next_permutation(std::begin(p), std::end(p)));
555   }
556 
557   return EXIT_SUCCESS;
558 }
559 
TestIssue17092()560 int TestIssue17092()
561 {
562   // An instance where triangle intersection failed was reported here:
563   // https://gitlab.kitware.com/vtk/vtk/-/issues/17092. It was fixed here:
564   // https://gitlab.kitware.com/vtk/vtk/-/merge_requests/3886
565 
566   double t1[3][3] = { { 0., 0., 0. }, { 5., 0., 0. }, { 0., 5., 0. } };
567   double t2[3][3] = { { 10., 5., 0. }, { 5., 10., 0. }, { 1., 1., 0. } };
568 
569   int returnValue = vtkTriangle::TrianglesIntersect(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
570 
571   if (returnValue != VTK_YES_INTERSECTION)
572   {
573     std::cout << "Triangle " << TriangleToString(t1[0], t1[1], t1[2]) << std::endl;
574     std::cout << " does not intersect " << TriangleToString(t2[0], t2[1], t2[2]) << " and should."
575               << std::endl;
576     return EXIT_FAILURE;
577   }
578 
579   return EXIT_SUCCESS;
580 }
581 
TestMR4529()582 int TestMR4529()
583 {
584   // An instance where triangle intersection failed (along with its fix) was
585   // reported here: https://gitlab.kitware.com/vtk/vtk/-/merge_requests/4529
586 
587   double t1[3][3] = { { 1.751, -.993, 0. }, { -3.021, 2.885, 0. }, { 4.14, -4.025, 0. } };
588   double t2[3][3] = { { 1.751, -.5, 0. }, { 1.751, 1.326, 0. }, { -3.382, 2.276, 0. } };
589 
590   int returnValue = vtkTriangle::TrianglesIntersect(t1[0], t1[1], t1[2], t2[0], t2[1], t2[2]);
591 
592   if (returnValue != VTK_YES_INTERSECTION)
593   {
594     std::cout << "Triangle " << TriangleToString(t1[0], t1[1], t1[2]) << std::endl;
595     std::cout << " does not intersect " << TriangleToString(t2[0], t2[1], t2[2]) << " and should."
596               << std::endl;
597     return EXIT_FAILURE;
598   }
599 
600   return EXIT_SUCCESS;
601 }
602 
TestTriangleIntersection(vtkRandom * seq,unsigned nTests)603 int TestTriangleIntersection(vtkRandom* seq, unsigned nTests)
604 {
605   if (TestPositiveResult(seq, nTests) == EXIT_FAILURE)
606   {
607     return EXIT_FAILURE;
608   }
609   if (TestNegativeResult(seq, nTests) == EXIT_FAILURE)
610   {
611     return EXIT_FAILURE;
612   }
613   if (TestCoplanarPositiveResult(seq, nTests) == EXIT_FAILURE)
614   {
615     return EXIT_FAILURE;
616   }
617   if (TestCoplanarNegativeResult(seq, nTests) == EXIT_FAILURE)
618   {
619     return EXIT_FAILURE;
620   }
621   if (TestReciprocalResult(seq, nTests) == EXIT_FAILURE)
622   {
623     return EXIT_FAILURE;
624   }
625   if (TestIssue17092() == EXIT_FAILURE)
626   {
627     return EXIT_FAILURE;
628   }
629   if (TestMR4529() == EXIT_FAILURE)
630   {
631     return EXIT_FAILURE;
632   }
633   return EXIT_SUCCESS;
634 }
635 }
636 
UnitTestTriangleIntersection(int,char * [])637 int UnitTestTriangleIntersection(int, char*[])
638 {
639   vtkRandom* sequence = vtkRandom::New();
640 
641   sequence->SetSeed(2);
642 
643   const unsigned nTest = 1.e5;
644 
645   std::cout << "Testing vtkTriangle::TriangleIntersection" << std::endl;
646   if (TestTriangleIntersection(sequence, nTest) == EXIT_FAILURE)
647   {
648     return EXIT_FAILURE;
649   }
650 
651   sequence->Delete();
652   return EXIT_SUCCESS;
653 }
654