1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    UnitTestLine.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 <cmath>
17 #include <limits>
18 
19 #include "vtkLine.h"
20 #include "vtkMath.h"
21 #include "vtkMinimalStandardRandomSequence.h"
22 #include "vtkSmartPointer.h"
23 
24 namespace
25 {
26 const double EPSILON = 1.e-6;
27 
GenerateIntersectingLineSegments(vtkMinimalStandardRandomSequence * seq,double * a1,double * a2,double * b1,double * b2,double & u,double & v)28 void GenerateIntersectingLineSegments(vtkMinimalStandardRandomSequence* seq, double* a1, double* a2,
29   double* b1, double* b2, double& u, double& v)
30 {
31   // Generate two line segments ((a1,a2) and (b1,b2)) that intersect, and set
32   // u and v as the parametric points of intersection on the two respective
33   // lines. First, an intersection is generated. Two lines are then generated
34   // that cross through this intersection.
35 
36   double intersection[3];
37   for (unsigned i = 0; i < 3; i++)
38   {
39     intersection[i] = seq->GetValue();
40     seq->Next();
41     a1[i] = seq->GetValue();
42     seq->Next();
43     b1[i] = seq->GetValue();
44     seq->Next();
45   }
46 
47   intersection[0] = 1.;
48   intersection[1] = intersection[2] = 0.;
49   a1[0] = a1[1] = a1[2] = 0.;
50   b1[0] = b1[1] = 1.;
51   b1[2] = 0.;
52 
53   double t1 = seq->GetValue();
54   seq->Next();
55   double t2 = seq->GetValue();
56   seq->Next();
57 
58   double lenA = 0.;
59   double lenB = 0.;
60   double lenA1toIntersection = 0.;
61   double lenB1toIntersection = 0.;
62 
63   for (unsigned i = 0; i < 3; i++)
64   {
65     a2[i] = a1[i] + (intersection[i] - a1[i]) * (1. + t1);
66     b2[i] = b1[i] + (intersection[i] - b1[i]) * (1. + t2);
67     lenA += (a2[i] - a1[i]) * (a2[i] - a1[i]);
68     lenB += (b2[i] - b1[i]) * (b2[i] - b1[i]);
69     lenA1toIntersection += (a1[i] - intersection[i]) * (a1[i] - intersection[i]);
70     lenB1toIntersection += (b1[i] - intersection[i]) * (b1[i] - intersection[i]);
71   }
72 
73   lenA = sqrt(lenA);
74   lenB = sqrt(lenB);
75   lenA1toIntersection = sqrt(lenA1toIntersection);
76   lenB1toIntersection = sqrt(lenB1toIntersection);
77 
78   u = lenA1toIntersection / lenA;
79   v = lenB1toIntersection / lenB;
80 }
81 
RandomSphere(vtkMinimalStandardRandomSequence * seq,const double radius,const double * offset,double * value)82 void RandomSphere(
83   vtkMinimalStandardRandomSequence* seq, const double radius, const double* offset, double* value)
84 {
85   // Generate a point within a sphere.
86 
87   double theta = 2. * vtkMath::Pi() * seq->GetValue();
88   seq->Next();
89   double phi = vtkMath::Pi() * seq->GetValue();
90   seq->Next();
91   value[0] = radius * cos(theta) * sin(phi) + offset[0];
92   value[1] = radius * sin(theta) * sin(phi) + offset[1];
93   value[2] = radius * cos(phi) + offset[2];
94 }
95 
GenerateNonintersectingLineSegments(vtkMinimalStandardRandomSequence * seq,double * a1,double * a2,double * b1,double * b2)96 void GenerateNonintersectingLineSegments(
97   vtkMinimalStandardRandomSequence* seq, double* a1, double* a2, double* b1, double* b2)
98 {
99   // Generate two line segments ((a1,a2) and (b1,b2)) that do not intersect.
100   // The endpoints of each line segment are generated from two non-overlapping
101   // spheres, and the two spheres for each line segment are physically displaced
102   // as well.
103 
104   static const double center[4][3] = { { 0., 0., 0. }, { 1., 0., 0. }, { 0., 1., 0. },
105     { 1., 1., 0. } };
106 
107   static const double radius = 0.5 - 1.e-6;
108 
109   RandomSphere(seq, radius, center[0], a1);
110   RandomSphere(seq, radius, center[1], a2);
111   RandomSphere(seq, radius, center[2], b1);
112   RandomSphere(seq, radius, center[3], b2);
113 }
114 
GenerateColinearLineSegments(vtkMinimalStandardRandomSequence * seq,double * a1,double * a2,double * b1,double * b2)115 void GenerateColinearLineSegments(
116   vtkMinimalStandardRandomSequence* seq, double* a1, double* a2, double* b1, double* b2)
117 {
118   // Generate two line segments ((a1,a2) and (b1,b2)) that are colinear.
119 
120   for (unsigned i = 0; i < 3; i++)
121   {
122     a1[i] = seq->GetValue();
123     seq->Next();
124     a2[i] = seq->GetValue();
125     seq->Next();
126     double tmp = seq->GetValue();
127     seq->Next();
128     b1[i] = a1[i] + tmp;
129     b2[i] = a2[i] + tmp;
130   }
131 }
132 
GenerateLinesAtKnownDistance(vtkMinimalStandardRandomSequence * seq,double & lineDist,double * a1,double * a2,double * b1,double * b2,double * a12,double * b12,double & u,double & v)133 void GenerateLinesAtKnownDistance(vtkMinimalStandardRandomSequence* seq, double& lineDist,
134   double* a1, double* a2, double* b1, double* b2, double* a12, double* b12, double& u, double& v)
135 {
136   // Generate two lines ((a1,a2) and (b1,b2)) set a known distance (lineDist)
137   // apart. the parameter and value of the closest points for lines a and b are
138   // a12, u and b12, v, respectively.
139 
140   double v1[3], v2[3], v3[3];
141   for (unsigned i = 0; i < 3; i++)
142   {
143     v1[i] = seq->GetValue();
144     seq->Next();
145     v2[i] = seq->GetValue();
146     seq->Next();
147   }
148   vtkMath::Cross(v1, v2, v3);
149   vtkMath::Normalize(v1);
150   vtkMath::Normalize(v2);
151   vtkMath::Normalize(v3);
152 
153   double a1_to_a12 = .1 + seq->GetValue();
154   seq->Next();
155   double a12_to_a2 = .1 + seq->GetValue();
156   seq->Next();
157   double b1_to_b12 = .1 + seq->GetValue();
158   seq->Next();
159   double b12_to_b2 = .1 + seq->GetValue();
160   seq->Next();
161 
162   lineDist = seq->GetValue();
163   seq->Next();
164 
165   for (unsigned i = 0; i < 3; i++)
166   {
167     a12[i] = seq->GetValue();
168     seq->Next();
169     b12[i] = a12[i] + lineDist * v3[i];
170     a1[i] = a12[i] - a1_to_a12 * v1[i];
171     a2[i] = a12[i] + a12_to_a2 * v1[i];
172     b1[i] = b12[i] - b1_to_b12 * v2[i];
173     b2[i] = b12[i] + b12_to_b2 * v2[i];
174   }
175   u = a1_to_a12 / (a1_to_a12 + a12_to_a2);
176   v = b1_to_b12 / (b1_to_b12 + b12_to_b2);
177 }
178 
GenerateLineAtKnownDistance(vtkMinimalStandardRandomSequence * seq,double * a1,double * a2,double * p,double & dist)179 void GenerateLineAtKnownDistance(
180   vtkMinimalStandardRandomSequence* seq, double* a1, double* a2, double* p, double& dist)
181 {
182   // Generate a line (a1,a2) set a known distance (dist) from a generated point
183   // p.
184 
185   double v1[3], v2[3], v3[3];
186   for (unsigned i = 0; i < 3; i++)
187   {
188     v1[i] = seq->GetValue();
189     seq->Next();
190     v2[i] = seq->GetValue();
191     seq->Next();
192   }
193   vtkMath::Cross(v1, v2, v3);
194   vtkMath::Normalize(v1);
195   vtkMath::Normalize(v2);
196   vtkMath::Normalize(v3);
197 
198   double a1_to_a12 = .1 + seq->GetValue();
199   seq->Next();
200   double a12_to_a2 = .1 + seq->GetValue();
201   seq->Next();
202 
203   dist = seq->GetValue();
204   seq->Next();
205 
206   double nearest[3];
207   for (unsigned i = 0; i < 3; i++)
208   {
209     nearest[i] = seq->GetValue();
210     seq->Next();
211     p[i] = nearest[i] + dist * v3[i];
212     a1[i] = nearest[i] - a1_to_a12 * v1[i];
213     a2[i] = nearest[i] + a12_to_a2 * v1[i];
214   }
215 }
216 
PointToLineSegment(double * p1,double * p2,double * p,double * pn,double & u)217 double PointToLineSegment(double* p1, double* p2, double* p, double* pn, double& u)
218 {
219   // Helper function that computes the distance from a point to a line segment.
220   // It is not used to actually test the vtkLine function for the same purpose,
221   // but rather to determine correct values for these test functions.
222 
223   u = 0.;
224   double dist2 = 0.;
225   for (unsigned i = 0; i < 3; i++)
226   {
227     u += (p[i] - p1[i]) * (p2[i] - p1[i]);
228     dist2 += (p2[i] - p1[i]) * (p2[i] - p1[i]);
229   }
230   u /= dist2;
231 
232   if (u <= 0.)
233   {
234     for (unsigned i = 0; i < 3; i++)
235     {
236       u = 0.;
237       pn[i] = p1[i];
238     }
239   }
240   else if (u >= 1.)
241   {
242     u = 1.;
243     for (unsigned i = 0; i < 3; i++)
244     {
245       pn[i] = p2[i];
246     }
247   }
248   else
249   {
250     for (unsigned i = 0; i < 3; i++)
251     {
252       pn[i] = p1[i] + u * (p2[i] - p1[i]);
253     }
254   }
255 
256   double dist = 0.;
257   for (unsigned i = 0; i < 3; i++)
258   {
259     dist += (p[i] - pn[i]) * (p[i] - pn[i]);
260   }
261   return std::sqrt(dist);
262 }
263 
GenerateLineSegmentsAtKnownDistance(vtkMinimalStandardRandomSequence * seq,double & lineDist,double * a1,double * a2,double * b1,double * b2,double * a12,double * b12,double & u,double & v)264 void GenerateLineSegmentsAtKnownDistance(vtkMinimalStandardRandomSequence* seq, double& lineDist,
265   double* a1, double* a2, double* b1, double* b2, double* a12, double* b12, double& u, double& v)
266 {
267   // Generate two line segments ((a1,a2) and (b1,b2)) set a known distance
268   // (lineDist) apart. the parameter and value of the closest points for lines
269   // a and b are a12, u and b12, v, respectively.
270 
271   GenerateLinesAtKnownDistance(seq, lineDist, a1, a2, b1, b2, a12, b12, u, v);
272 
273   double modify = seq->GetValue();
274   seq->Next();
275 
276   if (modify < 0.25)
277   {
278     double t = seq->GetValue();
279     seq->Next();
280 
281     for (unsigned i = 0; i < 3; i++)
282     {
283       a12[i] = a2[i] = a1[i] + (a12[i] - a1[i]) * t;
284     }
285 
286     u = 1.;
287     lineDist = PointToLineSegment(b1, b2, a2, b12, v);
288   }
289   else if (modify < 0.5)
290   {
291     double t = seq->GetValue();
292     seq->Next();
293 
294     for (unsigned i = 0; i < 3; i++)
295     {
296       b12[i] = b2[i] = b1[i] + (b12[i] - b1[i]) * t;
297     }
298 
299     lineDist = PointToLineSegment(a1, a2, b2, a12, u);
300     v = 1.;
301   }
302 }
303 
TestLineIntersection_PositiveResult(vtkMinimalStandardRandomSequence * seq,unsigned nTests)304 int TestLineIntersection_PositiveResult(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
305 {
306   double a1[3], a2[3], b1[3], b2[3], u, v;
307 
308   for (unsigned i = 0; i < nTests; i++)
309   {
310     GenerateIntersectingLineSegments(seq, a1, a2, b1, b2, u, v);
311 
312     double uv[2];
313     int returnValue = vtkLine::Intersection(a1, a2, b1, b2, uv[0], uv[1]);
314 
315     if (returnValue != vtkLine::Intersect)
316     {
317       return EXIT_FAILURE;
318     }
319 
320     if (std::fabs(u - uv[0]) > EPSILON && std::fabs(v - uv[1]) > EPSILON)
321     {
322       return EXIT_FAILURE;
323     }
324   }
325 
326   return EXIT_SUCCESS;
327 }
328 
TestLineIntersection_NegativeResult(vtkMinimalStandardRandomSequence * seq,unsigned nTests)329 int TestLineIntersection_NegativeResult(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
330 {
331   double a1[3], a2[3], b1[3], b2[3], u, v;
332 
333   for (unsigned i = 0; i < nTests; i++)
334   {
335     GenerateNonintersectingLineSegments(seq, a1, a2, b1, b2);
336 
337     int returnValue = vtkLine::Intersection(a1, a2, b1, b2, u, v);
338 
339     if (returnValue != vtkLine::NoIntersect)
340     {
341       return EXIT_FAILURE;
342     }
343   }
344 
345   return EXIT_SUCCESS;
346 }
347 
TestLineIntersectionAbsTol_PositiveResult(vtkMinimalStandardRandomSequence * seq,unsigned nTests)348 int TestLineIntersectionAbsTol_PositiveResult(
349   vtkMinimalStandardRandomSequence* seq, unsigned nTests)
350 {
351   double a1[3], a2[3], b1[3], b2[3], u, v;
352 
353   for (unsigned i = 0; i < nTests; i++)
354   {
355     GenerateIntersectingLineSegments(seq, a1, a2, b1, b2, u, v);
356 
357     double uv[2];
358     int returnValue =
359       vtkLine::Intersection(a1, a2, b1, b2, uv[0], uv[1], 1.0e-06, vtkLine::Absolute);
360 
361     if (returnValue != vtkLine::Intersect)
362     {
363       return EXIT_FAILURE;
364     }
365 
366     if (std::fabs(u - uv[0]) > EPSILON && std::fabs(v - uv[1]) > EPSILON)
367     {
368       return EXIT_FAILURE;
369     }
370   }
371 
372   return EXIT_SUCCESS;
373 }
374 
TestLineIntersectionAbsTol_NegativeResult(vtkMinimalStandardRandomSequence * seq,unsigned nTests)375 int TestLineIntersectionAbsTol_NegativeResult(
376   vtkMinimalStandardRandomSequence* seq, unsigned nTests)
377 {
378   double a1[3], a2[3], b1[3], b2[3], u, v;
379 
380   for (unsigned i = 0; i < nTests; i++)
381   {
382     GenerateNonintersectingLineSegments(seq, a1, a2, b1, b2);
383 
384     int returnValue = vtkLine::Intersection(a1, a2, b1, b2, u, v, 1.0e-06, vtkLine::Absolute);
385 
386     if (returnValue != vtkLine::NoIntersect)
387     {
388       return EXIT_FAILURE;
389     }
390   }
391 
392   return EXIT_SUCCESS;
393 }
394 
TestLineIntersection_ColinearResult(vtkMinimalStandardRandomSequence * seq,unsigned nTests)395 int TestLineIntersection_ColinearResult(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
396 {
397   double a1[3], a2[3], b1[3], b2[3], u, v;
398 
399   for (unsigned i = 0; i < nTests; i++)
400   {
401     GenerateColinearLineSegments(seq, a1, a2, b1, b2);
402 
403     int returnValue = vtkLine::Intersection(a1, a2, b1, b2, u, v);
404 
405     if (returnValue != vtkLine::OnLine)
406     {
407       return EXIT_FAILURE;
408     }
409   }
410 
411   return EXIT_SUCCESS;
412 }
413 
TestLineIntersection(vtkMinimalStandardRandomSequence * seq,unsigned nTests)414 int TestLineIntersection(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
415 {
416   if (TestLineIntersection_PositiveResult(seq, nTests) == EXIT_FAILURE)
417   {
418     return EXIT_FAILURE;
419   }
420   if (TestLineIntersection_NegativeResult(seq, nTests) == EXIT_FAILURE)
421   {
422     return EXIT_FAILURE;
423   }
424   if (TestLineIntersectionAbsTol_PositiveResult(seq, nTests) == EXIT_FAILURE)
425   {
426     return EXIT_FAILURE;
427   }
428   if (TestLineIntersectionAbsTol_NegativeResult(seq, nTests) == EXIT_FAILURE)
429   {
430     return EXIT_FAILURE;
431   }
432   if (TestLineIntersection_ColinearResult(seq, nTests) == EXIT_FAILURE)
433   {
434     return EXIT_FAILURE;
435   }
436   return EXIT_SUCCESS;
437 }
438 
TestDistanceBetweenLines(vtkMinimalStandardRandomSequence * seq,unsigned nTests)439 int TestDistanceBetweenLines(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
440 {
441   double a1[3], a2[3], b1[3], b2[3], a12[3], b12[3], u, v, dist;
442 
443   for (unsigned i = 0; i < nTests; i++)
444   {
445     GenerateLinesAtKnownDistance(seq, dist, a1, a2, b1, b2, a12, b12, u, v);
446 
447     double p1[3], p2[3], t1, t2;
448     double d = vtkLine::DistanceBetweenLines(a1, a2, b1, b2, p1, p2, t1, t2);
449 
450     if (std::fabs(dist * dist - d) > EPSILON)
451     {
452       return EXIT_FAILURE;
453     }
454 
455     for (unsigned j = 0; j < 3; j++)
456     {
457       if (std::fabs(a12[j] - p1[j]) > EPSILON || std::fabs(b12[j] - p2[j]) > EPSILON)
458       {
459         return EXIT_FAILURE;
460       }
461     }
462 
463     if (std::fabs(u - t1) > EPSILON || std::fabs(v - t2) > EPSILON)
464     {
465       return EXIT_FAILURE;
466     }
467   }
468 
469   return EXIT_SUCCESS;
470 }
471 
TestDistanceBetweenLineSegments(vtkMinimalStandardRandomSequence * seq,unsigned nTests)472 int TestDistanceBetweenLineSegments(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
473 {
474   double a1[3], a2[3], b1[3], b2[3], a12[3], b12[3], u, v, dist;
475 
476   for (unsigned i = 0; i < nTests; i++)
477   {
478     GenerateLineSegmentsAtKnownDistance(seq, dist, a1, a2, b1, b2, a12, b12, u, v);
479 
480     double p1[3], p2[3], t1, t2;
481     double d = vtkLine::DistanceBetweenLineSegments(a1, a2, b1, b2, p1, p2, t1, t2);
482 
483     if (std::fabs(dist * dist - d) > EPSILON)
484     {
485       return EXIT_FAILURE;
486     }
487 
488     for (unsigned j = 0; j < 3; j++)
489     {
490       if (std::fabs(a12[j] - p1[j]) > EPSILON || std::fabs(b12[j] - p2[j]) > EPSILON)
491       {
492         return EXIT_FAILURE;
493       }
494     }
495 
496     if (std::fabs(u - t1) > EPSILON || std::fabs(v - t2) > EPSILON)
497     {
498       return EXIT_FAILURE;
499     }
500   }
501 
502   return EXIT_SUCCESS;
503 }
504 
TestDistanceToLine(vtkMinimalStandardRandomSequence * seq,unsigned nTests)505 int TestDistanceToLine(vtkMinimalStandardRandomSequence* seq, unsigned nTests)
506 {
507   const double epsilon = 256 * std::numeric_limits<double>::epsilon();
508 
509   double a1[3], a2[3], p[3], dist;
510 
511   for (unsigned i = 0; i < nTests; i++)
512   {
513     GenerateLineAtKnownDistance(seq, a1, a2, p, dist);
514 
515     double d = vtkLine::DistanceToLine(p, a1, a2);
516 
517     if (std::fabs(dist * dist - d) > epsilon)
518     {
519       return EXIT_FAILURE;
520     }
521   }
522 
523   return EXIT_SUCCESS;
524 }
525 }
526 
UnitTestLine(int,char * [])527 int UnitTestLine(int, char*[])
528 {
529   vtkMinimalStandardRandomSequence* sequence = vtkMinimalStandardRandomSequence::New();
530 
531   sequence->SetSeed(1);
532 
533   const unsigned nTest = 1.e4;
534 
535   std::cout << "Testing vtkLine::vtkLine::Intersection" << std::endl;
536   if (TestLineIntersection(sequence, nTest) == EXIT_FAILURE)
537   {
538     return EXIT_FAILURE;
539   }
540   std::cout << "Testing vtkLine::DistanceBetweenLines" << std::endl;
541   if (TestDistanceBetweenLines(sequence, nTest) == EXIT_FAILURE)
542   {
543     return EXIT_FAILURE;
544   }
545   std::cout << "Testing vtkLine::DistanceBetweenLineSegments" << std::endl;
546   if (TestDistanceBetweenLineSegments(sequence, nTest) == EXIT_FAILURE)
547   {
548     return EXIT_FAILURE;
549   }
550   std::cout << "Testing vtkLine::DistanceToLine" << std::endl;
551   if (TestDistanceToLine(sequence, nTest) == EXIT_FAILURE)
552   {
553     return EXIT_FAILURE;
554   }
555 
556   sequence->Delete();
557   return EXIT_SUCCESS;
558 }
559