GteDistSegmentSegment.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
11 #include <Mathematics/GteSegment.h>
12 
13 // Compute the closest points on the line segments P(s) = (1-s)*P0 + s*P1 and
14 // Q(t) = (1-t)*Q0 + t*Q1 for 0 <= s <= 1 and 0 <= t <= 1. The algorithm is
15 // robust even for nearly parallel segments. Effectively, it uses a conjugate
16 // gradient search for the minimum of the squared distance function, which
17 // avoids the numerical problems introduced by divisions in the case the
18 // minimum is located at an interior point of the domain. See the document
19 // http://www.geometrictools.com/Documentation/DistanceLine3Line3.pdf
20 // for details.
21 
22 namespace gte
23 {
24 
25 template <int N, typename Real>
26 class DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>
27 {
28 public:
29  struct Result
30  {
32  Real parameter[2];
33  Vector<N, Real> closest[2];
34  };
35 
36  Result operator()(Segment<N, Real> const& segment0,
37  Segment<N, Real> const& segment1);
38 
39  Result operator()(Vector<N, Real> const& P0, Vector<N, Real> const& P1,
40  Vector<N, Real> const& Q0, Vector<N, Real> const& Q1);
41 
42 private:
43  // Compute the root of h(z) = h0 + slope*z and clamp it to the interval
44  // [0,1]. It is required that for h1 = h(1), either (h0 < 0 and h1 > 0)
45  // or (h0 > 0 and h1 < 0).
46  Real GetClampedRoot(Real slope, Real h0, Real h1);
47 
48  // Compute the intersection of the line dR/ds = 0 with the domain [0,1]^2.
49  // The direction of the line dR/ds is conjugate to (1,0), so the algorithm
50  // for minimization is effectively the conjugate gradient algorithm for a
51  // quadratic function.
52  void ComputeIntersection(Real const sValue[2], int const classify[2],
53  int edge[2], Real end[2][2]);
54 
55  // Compute the location of the minimum of R on the segment of intersection
56  // for the line dR/ds = 0 and the domain [0,1]^2.
57  void ComputeMinimumParameters(int const edge[2], Real const end[2][2],
58  Real parameter[2]);
59 
60  // The coefficients of R(s,t), not including the constant term.
61  Real mA, mB, mC, mD, mE;
62 
63  // dR/ds(i,j) at the four corners of the domain
64  Real mF00, mF10, mF01, mF11;
65 
66  // dR/dt(i,j) at the four corners of the domain
67  Real mG00, mG10, mG01, mG11;
68 };
69 
70 // Template aliases for convenience.
71 template <int N, typename Real>
73 
74 template <typename Real>
76 
77 template <typename Real>
79 
80 
81 template <int N, typename Real>
82 typename DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::Result
83 DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::operator()(
84  Segment<N, Real> const& segment0, Segment<N, Real> const& segment1)
85 {
86  return operator()(segment0.p[0], segment0.p[1], segment1.p[0],
87  segment1.p[1]);
88 }
89 
90 template <int N, typename Real>
91 typename DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::Result
92 DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::operator()(
93  Vector<N, Real> const& P0, Vector<N, Real> const& P1,
94  Vector<N, Real> const& Q0, Vector<N, Real> const& Q1)
95 {
96  Result result;
97 
98  // The code allows degenerate line segments; that is, P0 and P1 can be
99  // the same point or Q0 and Q1 can be the same point. The quadratic
100  // function for squared distance between the segment is
101  // R(s,t) = a*s^2 - 2*b*s*t + c*t^2 + 2*d*s - 2*e*t + f
102  // for (s,t) in [0,1]^2 where
103  // a = Dot(P1-P0,P1-P0), b = Dot(P1-P0,Q1-Q0), c = Dot(Q1-Q0,Q1-Q0),
104  // d = Dot(P1-P0,P0-Q0), e = Dot(Q1-Q0,P0-Q0), f = Dot(P0-Q0,P0-Q0)
105  Vector<N, Real> P1mP0 = P1 - P0;
106  Vector<N, Real> Q1mQ0 = Q1 - Q0;
107  Vector<N, Real> P0mQ0 = P0 - Q0;
108  mA = Dot(P1mP0, P1mP0);
109  mB = Dot(P1mP0, Q1mQ0);
110  mC = Dot(Q1mQ0, Q1mQ0);
111  mD = Dot(P1mP0, P0mQ0);
112  mE = Dot(Q1mQ0, P0mQ0);
113 
114  mF00 = mD;
115  mF10 = mF00 + mA;
116  mF01 = mF00 - mB;
117  mF11 = mF10 - mB;
118 
119  mG00 = -mE;
120  mG10 = mG00 - mB;
121  mG01 = mG00 + mC;
122  mG11 = mG10 + mC;
123 
124  if (mA > (Real)0 && mC > (Real)0)
125  {
126  // Compute the solutions to dR/ds(s0,0) = 0 and dR/ds(s1,1) = 0. The
127  // location of sI on the s-axis is stored in classifyI (I = 0 or 1). If
128  // sI <= 0, classifyI is -1. If sI >= 1, classifyI is 1. If 0 < sI < 1,
129  // classifyI is 0. This information helps determine where to search for
130  // the minimum point (s,t). The fij values are dR/ds(i,j) for i and j in
131  // {0,1}.
132 
133  Real sValue[2];
134  sValue[0] = GetClampedRoot(mA, mF00, mF10);
135  sValue[1] = GetClampedRoot(mA, mF01, mF11);
136 
137  int classify[2];
138  for (int i = 0; i < 2; ++i)
139  {
140  if (sValue[i] <= (Real)0)
141  {
142  classify[i] = -1;
143  }
144  else if (sValue[i] >= (Real)1)
145  {
146  classify[i] = +1;
147  }
148  else
149  {
150  classify[i] = 0;
151  }
152  }
153 
154  if (classify[0] == -1 && classify[1] == -1)
155  {
156  // The minimum must occur on s = 0 for 0 <= t <= 1.
157  result.parameter[0] = (Real)0;
158  result.parameter[1] = GetClampedRoot(mC, mG00, mG01);
159  }
160  else if (classify[0] == +1 && classify[1] == +1)
161  {
162  // The minimum must occur on s = 1 for 0 <= t <= 1.
163  result.parameter[0] = (Real)1;
164  result.parameter[1] = GetClampedRoot(mC, mG10, mG11);
165  }
166  else
167  {
168  // The line dR/ds = 0 intersects the domain [0,1]^2 in a
169  // nondegenerate segment. Compute the endpoints of that segment,
170  // end[0] and end[1]. The edge[i] flag tells you on which domain
171  // edge end[i] lives: 0 (s=0), 1 (s=1), 2 (t=0), 3 (t=1).
172  int edge[2];
173  Real end[2][2];
174  ComputeIntersection(sValue, classify, edge, end);
175 
176  // The directional derivative of R along the segment of
177  // intersection is
178  // H(z) = (end[1][1]-end[1][0])*dR/dt((1-z)*end[0] + z*end[1])
179  // for z in [0,1]. The formula uses the fact that dR/ds = 0 on
180  // the segment. Compute the minimum of H on [0,1].
181  ComputeMinimumParameters(edge, end, result.parameter);
182  }
183  }
184  else
185  {
186  if (mA > (Real)0)
187  {
188  // The Q-segment is degenerate (Q0 and Q1 are the same point) and
189  // the quadratic is R(s,0) = a*s^2 + 2*d*s + f and has (half)
190  // first derivative F(t) = a*s + d. The closest P-point is
191  // interior to the P-segment when F(0) < 0 and F(1) > 0.
192  result.parameter[0] = GetClampedRoot(mA, mF00, mF10);
193  result.parameter[1] = (Real)0;
194  }
195  else if (mC > (Real)0)
196  {
197  // The P-segment is degenerate (P0 and P1 are the same point) and
198  // the quadratic is R(0,t) = c*t^2 - 2*e*t + f and has (half)
199  // first derivative G(t) = c*t - e. The closest Q-point is
200  // interior to the Q-segment when G(0) < 0 and G(1) > 0.
201  result.parameter[0] = (Real)0;
202  result.parameter[1] = GetClampedRoot(mC, mG00, mG01);
203  }
204  else
205  {
206  // P-segment and Q-segment are degenerate.
207  result.parameter[0] = (Real)0;
208  result.parameter[1] = (Real)0;
209  }
210  }
211 
212 
213  result.closest[0] =
214  ((Real)1 - result.parameter[0]) * P0 + result.parameter[0] * P1;
215  result.closest[1] =
216  ((Real)1 - result.parameter[1]) * Q0 + result.parameter[1] * Q1;
217  Vector<N, Real> diff = result.closest[0] - result.closest[1];
218  result.sqrDistance = Dot(diff, diff);
219  result.distance = Function<Real>::Sqrt(result.sqrDistance);
220  return result;
221 }
222 
223 template <int N, typename Real>
224 Real DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::GetClampedRoot(
225  Real slope, Real h0, Real h1)
226 {
227  // Theoretically, r is in (0,1). However, when the slope is nearly zero,
228  // then so are h0 and h1. Significant numerical rounding problems can
229  // occur when using floating-point arithmetic. If the rounding causes r
230  // to be outside the interval, clamp it. It is possible that r is in
231  // (0,1) and has rounding errors, but because h0 and h1 are both nearly
232  // zero, the quadratic is nearly constant on (0,1). Any choice of p
233  // should not cause undesirable accuracy problems for the final distance
234  // computation.
235  //
236  // NOTE: You can use bisection to recompute the root or even use
237  // bisection to compute the root and skip the division. This is generally
238  // slower, which might be a problem for high-performance applications.
239 
240  Real r;
241  if (h0 < (Real)0)
242  {
243  if (h1 > (Real)0)
244  {
245  r = -h0 / slope;
246  if (r > (Real)1)
247  {
248  r = (Real)0.5;
249  }
250  // The slope is positive and -h0 is positive, so there is no
251  // need to test for a negative value and clamp it.
252  }
253  else
254  {
255  r = (Real)1;
256  }
257  }
258  else
259  {
260  r = (Real)0;
261  }
262  return r;
263 }
264 
265 template <int N, typename Real>
266 void DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::ComputeIntersection(
267  Real const sValue[2], int const classify[2], int edge[2], Real end[2][2])
268 {
269  // The divisions are theoretically numbers in [0,1]. Numerical rounding
270  // errors might cause the result to be outside the interval. When this
271  // happens, it must be that both numerator and denominator are nearly
272  // zero. The denominator is nearly zero when the segments are nearly
273  // perpendicular. The numerator is nearly zero when the P-segment is
274  // nearly degenerate (mF00 = a is small). The choice of 0.5 should not
275  // cause significant accuracy problems.
276  //
277  // NOTE: You can use bisection to recompute the root or even use
278  // bisection to compute the root and skip the division. This is generally
279  // slower, which might be a problem for high-performance applications.
280 
281  if (classify[0] < 0)
282  {
283  edge[0] = 0;
284  end[0][0] = (Real)0;
285  end[0][1] = mF00 / mB;
286  if (end[0][1] < (Real)0 || end[0][1] > (Real)1)
287  {
288  end[0][1] = (Real)0.5;
289  }
290 
291  if (classify[1] == 0)
292  {
293  edge[1] = 3;
294  end[1][0] = sValue[1];
295  end[1][1] = (Real)1;
296  }
297  else // classify[1] > 0
298  {
299  edge[1] = 1;
300  end[1][0] = (Real)1;
301  end[1][1] = mF10 / mB;
302  if (end[1][1] < (Real)0 || end[1][1] > (Real)1)
303  {
304  end[1][1] = (Real)0.5;
305  }
306  }
307  }
308  else if (classify[0] == 0)
309  {
310  edge[0] = 2;
311  end[0][0] = sValue[0];
312  end[0][1] = (Real)0;
313 
314  if (classify[1] < 0)
315  {
316  edge[1] = 0;
317  end[1][0] = (Real)0;
318  end[1][1] = mF00 / mB;
319  if (end[1][1] < (Real)0 || end[1][1] > (Real)1)
320  {
321  end[1][1] = (Real)0.5;
322  }
323  }
324  else if (classify[1] == 0)
325  {
326  edge[1] = 3;
327  end[1][0] = sValue[1];
328  end[1][1] = (Real)1;
329  }
330  else
331  {
332  edge[1] = 1;
333  end[1][0] = (Real)1;
334  end[1][1] = mF10 / mB;
335  if (end[1][1] < (Real)0 || end[1][1] > (Real)1)
336  {
337  end[1][1] = (Real)0.5;
338  }
339  }
340  }
341  else // classify[0] > 0
342  {
343  edge[0] = 1;
344  end[0][0] = (Real)1;
345  end[0][1] = mF10 / mB;
346  if (end[0][1] < (Real)0 || end[0][1] > (Real)1)
347  {
348  end[0][1] = (Real)0.5;
349  }
350 
351  if (classify[1] == 0)
352  {
353  edge[1] = 3;
354  end[1][0] = sValue[1];
355  end[1][1] = (Real)1;
356  }
357  else
358  {
359  edge[1] = 0;
360  end[1][0] = (Real)0;
361  end[1][1] = mF00 / mB;
362  if (end[1][1] < (Real)0 || end[1][1] > (Real)1)
363  {
364  end[1][1] = (Real)0.5;
365  }
366  }
367  }
368 }
369 
370 template <int N, typename Real>
371 void DCPQuery<Real, Segment<N, Real>, Segment<N, Real>>::
372 ComputeMinimumParameters(int const edge[2], Real const end[2][2],
373  Real parameter[2])
374 {
375  Real delta = end[1][1] - end[0][1];
376  Real h0 = delta * (-mB * end[0][0] + mC * end[0][1] - mE);
377  if (h0 >= (Real)0)
378  {
379  if (edge[0] == 0)
380  {
381  parameter[0] = (Real)0;
382  parameter[1] = GetClampedRoot(mC, mG00, mG01);
383  }
384  else if (edge[0] == 1)
385  {
386  parameter[0] = (Real)1;
387  parameter[1] = GetClampedRoot(mC, mG10, mG11);
388  }
389  else
390  {
391  parameter[0] = end[0][0];
392  parameter[1] = end[0][1];
393  }
394  }
395  else
396  {
397  Real h1 = delta * (-mB * end[1][0] + mC * end[1][1] - mE);
398  if (h1 <= (Real)0)
399  {
400  if (edge[1] == 0)
401  {
402  parameter[0] = (Real)0;
403  parameter[1] = GetClampedRoot(mC, mG00, mG01);
404  }
405  else if (edge[1] == 1)
406  {
407  parameter[0] = (Real)1;
408  parameter[1] = GetClampedRoot(mC, mG10, mG11);
409  }
410  else
411  {
412  parameter[0] = end[1][0];
413  parameter[1] = end[1][1];
414  }
415  }
416  else // h0 < 0 and h1 > 0
417  {
418  Real z = std::min(std::max(h0 / (h0 - h1), (Real)0), (Real)1);
419  Real omz = (Real)1 - z;
420  parameter[0] = omz * end[0][0] + z * end[1][0];
421  parameter[1] = omz * end[0][1] + z * end[1][1];
422  }
423  }
424 }
425 
426 
427 }
GLsizei GLsizei GLfloat distance
Definition: glext.h:9704
static Real Sqrt(Real const &x)
Definition: GteFunctions.h:937
Result operator()(Type0 const &primitive0, Type1 const &primitive1)
GLuint GLuint end
Definition: glcorearb.h:470
DualQuaternion< Real > Dot(DualQuaternion< Real > const &d0, DualQuaternion< Real > const &d1)
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:843
GLboolean r
Definition: glcorearb.h:1217
GLuint64EXT * result
Definition: glext.h:10003


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:59