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