GteIntrConvexPolygonPlane.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 
10 #include <Mathematics/GteTIQuery.h>
11 #include <Mathematics/GteFIQuery.h>
13 #include <Mathematics/GteVector3.h>
14 #include <limits>
15 #include <list>
16 #include <vector>
17 
18 // The plane is defined by Dot(N,X) = c, where N is a plane normal, c is the
19 // corresponding plane constant, and X is any point on the plane. The
20 // algorithm is based on computing the signed heights h[i] = Dot(N,V[i]) - c
21 // for the vertex positions V[i]. The algorithm does not require N to be
22 // unit length; in this case, the h[i] are scaled signed heights. The
23 // configurations for the convex polygon and the plane are listed in the
24 // enumeration ConvexPolygonPlaneConfiguration.
25 //
26 // Let the polygon have M vertices and define F = {0,1,...,M-1} be the full
27 // set of indices. F can be treated as a cyclic list L by using modular
28 // arithmetic. Given an index i, the next index is iNext = (i+1)%M and the
29 // previous index is iPrev = (i+M-1)%M, where both iNext and iPrev are in F.
30 // The h[] values partition the list as L = {Z0,P,Z1,N} or L = {Z}, where P
31 // is a contiguous subset of positive values (possibly empty), N is a
32 // contiguous subset of negative values (possibly empty), Z0 and Z1 are
33 // singleton subsets of a zero value (possibly empty), and Z is a subset of
34 // all zero values. The classifications correspond to the various partitions.
35 // In the following, if a subset is referenced in L, it is nonempty.
36 // SPLIT_BY_PLANE, L = {P,N} or {Z0,P,N}, or {P,Z1,N} or {Z0,P,Z1,N}
37 // POSITIVE_SIDE_VERTEX, L = {Z0,P0}
38 // POSITIVE_SIDE_EDGE, L = {Z0,P,Z1}
39 // POSITIVE_SIDE_STRICT, L = {P}
40 // NEGATIVE_SIDE_VERTEX, L = {Z1,N}
41 // NEGATIVE_SIDE_EDGE, L = {Z0,Z1,N}
42 // NEGATIVE_SIDE_STRICT, L = {N}
43 // COINCIDENT_WITH_PLANE, L = {Z}
44 //
45 // When using floating-point arithmetic, rounding errors can lead to
46 // misclassification of the configuration. For example, the vertices might be
47 // close enough to the plane that some of the h[] signs are inaccurate, say,
48 // some are positive, some are negative, and 3 or more are zero. It might
49 // even be that the ordered list of h[] values alternate in sign frequently,
50 // leading to multiple subsets of positive, negative and zero values.
51 //
52 // The find-intersection query is designed to be somewhat robust to this
53 // in the case of SPLIT_BY_PLANE by attempting to partition L into one of
54 // the configurations mentioned for that case. The algorithm is to locate
55 // the vertex V[pmax] so that h[pmax] >= h[i] for all i and V[nmax] so that
56 // h[nmax] <= h[i] for all i. In the SPLIT_BY_PLANE case, it must be that
57 // h[pmax] > 0 > h[nmax].
58 //
59 // The largest contiguous subset of indices that contains pmax is constructed
60 // for which h[i] >= 0, say, be A = {i[0], i[1], ..., i[|A|-1]} where |A| is
61 // the number of elements in the set. The indices are consecutive in the
62 // modular sense, i[k+1] = (i[k]+1)%M. We know that h[nmax] < 0, so A is
63 // a strict subset of F; thus, h[(i[0]+M-1)%M] < 0 and h[(i[|A|-1]+1)%M] < 0.
64 // Define B = {j[0], j[1], ..., j[|B|-1]} = F - A, the set of polygon indices
65 // not in A, where |B| is the number of elements of B with |A|+|B| = M,
66 // j[0] = (i[|A|-1]+1)%M, j[|B|-1] = (i[0]+M-1)%M, and j[k+1] = (j[k]+1)%M
67 // for all other subindices k.
68 //
69 // Note that rounding errors can lead to h[i] = 0 for some indices in
70 // A - {i[0], i[|A|-1]}. They can also lead to h[i] >= 0 for some indices in
71 // B - {j[0], j[|B|-1]}. Regardless, we can partition F into into at most 4
72 // subsets. Let k0 = i[0] and k1 = i[|A|-1]; then N = B and
73 // h[k0] > 0, h[k1] > 0: P = A, L = {P,N}
74 // h[k0] > 0, h[k1] = 0: Z1 = {k1}, P = A-Z1, L = {P,Z1,N}
75 // h[k0] = 0, h[k1] > 0: Z0 = {k0}, P = A-Z0, L = {Z0,P,N}
76 // h[k0] = 0, h[k1] = 0: Z0 = {k0}, Z1 = {k1}, P = A-Z0-Z1, L = {Z0,P,Z1,N}
77 //
78 // As described, the algorithm has a bias in the following sense due to
79 // choosing to find the set A using the most positive h[]. An equivalent
80 // representation for the plane Dot(N,X) = c is Dot(-N,X) = -c. To stress the
81 // dependence of the algorithm on the plane equation, let A(N,c) be the set
82 // that contains the index for the most positive h[pmax] = Dot(N,V[pmax]) - c.
83 // Let nmax be the index for the most negative h[nmax] = Dot(N,V[nmax]) - c.
84 // Observe that A(-N,-c) is not necessarily the same as A(N,c). The index
85 // nmax generated by constructing A(N,c) becomes the pmax generated by
86 // constructing A(-N,-c). Starting with nmax, A(-N,-c) will include indices
87 // i for which h[i] = 0 (if any), and those indices were part of A(N,c); the
88 // construction of A(.,.) is a greedy algorithm. To avoid the bias and ensure
89 // that the algorithm satisfies A(N,c) = A(-N,-c), we will choose N or -N so
90 // that the component with absolute maximum value is positive; for example,
91 // if N = (1, -2, 0), then -N = (-1, 2, 0). The y-component has maximum
92 // absolute value, so we will use (-N,-c) for the plane even though the caller
93 // specified (N,c), and construct A using pmax.
94 
95 namespace gte
96 {
97 
99 {
100  // The polygon is split by the plane into two subpolygons, each
101  // of positive area. The intersection is a line segment.
103 
104  // The polygon is on the positive side of the plane but touches
105  // the plane in a single vertex.
107 
108  // The polygon is on the positive side of the plane but touches
109  // the plane in a single edge.
111 
112  // The polygon is on the positive side of the plane and does not
113  // intersect the plane.
115 
116  // The polygon is on the negative side of the plane but touches
117  // the plane in a single vertex.
119 
120  // The polygon is on the negative side of the plane but touches
121  // the plane in a single edge.
123 
124  // The polygon is on the negative side of the plane and does not
125  // intersect the plane.
127 
128  // The polygon lies in the plane.
130 
131  // The reported value when the input polygon does not have 3 or more
132  // vertices.
134 };
135 
136 template <typename Real>
137 class TIQuery<Real, std::vector<Vector3<Real>>, Plane3<Real>>
138 {
139 public:
140  struct Result
141  {
142  bool intersect;
144  };
145 
146  Result operator()(std::vector<Vector3<Real>> const& polygon, Plane3<Real> const& plane);
147 };
148 
149 template <typename Real>
150 class FIQuery<Real, std::vector<Vector3<Real>>, Plane3<Real>>
151 {
152 public:
153  struct Result
154  {
155  // The intersection is either empty, a single vertex, a single edge,
156  // or the polygon is coincident with the plane.
158  std::vector<Vector3<Real>> intersection;
159 
160  // If 'configuration' is POSITIVE_* or SPLIT_BY_PLANE, this polygon
161  // is the portion of the query input 'polygon' on the positive side
162  // of the plane (with possibly a vertex or edge on the plane).
163  std::vector<Vector3<Real>> positivePolygon;
164 
165  // If 'configuration' is NEGATIVE_* or SPLIT_BY_PLANE, this polygon
166  // is the portion of the query input 'polygon' on the negative side
167  // of the plane (with possibly a vertex or edge on the plane).
168  std::vector<Vector3<Real>> negativePolygon;
169  };
170 
171  Result operator()(std::vector<Vector3<Real>> const& polygon, Plane3<Real> const& plane);
172 
173 private:
174  void SplitPolygon(std::vector<Vector3<Real>> const& polygon,
175  std::vector<Real> const& height, int maxPosIndex, Result& result);
176 };
177 
178 
179 template <typename Real>
182  std::vector<Vector3<Real>> const& polygon, Plane3<Real> const& plane)
183 {
184  Result result;
185 
186  int const numVertices = static_cast<int>(polygon.size());
187  if (numVertices < 3)
188  {
189  // The convex polygon must have at least 3 vertices.
190  result.intersect = false;
191  result.configuration = INVALID_POLYGON;
192  return result;
193  }
194 
195  // Determine on which side of the plane the vertices live.
196  int numPositive = 0, numNegative = 0, numZero = 0;
197  for (int i = 0; i < numVertices; ++i)
198  {
199  Real height = Dot(plane.normal, polygon[i]) - plane.constant;
200  if (height >(Real)0)
201  {
202  ++numPositive;
203  }
204  else if (height < (Real)0)
205  {
206  ++numNegative;
207  }
208  else
209  {
210  ++numZero;
211  }
212  }
213 
214  if (numPositive > 0)
215  {
216  if (numNegative > 0)
217  {
218  // The polygon and plane intersect transversely.
219  result.intersect = true;
220  result.configuration = SPLIT_BY_PLANE;
221  }
222  else if (numZero > 0)
223  {
224  // The polygon touches the plane in a vertex or an edge.
225  result.intersect = true;
226  result.configuration =
227  (numZero == 1 ? POSITIVE_SIDE_VERTEX : POSITIVE_SIDE_EDGE);
228  }
229  else
230  {
231  // The polygon is strictly on the positive side of the plane.
232  result.intersect = false;
233  result.configuration = POSITIVE_SIDE_STRICT;
234  }
235  }
236  else if (numNegative > 0)
237  {
238  if (numZero > 0)
239  {
240  // The polygon touches the plane in a vertex or an edge.
241  result.intersect = true;
242  result.configuration =
243  (numZero == 1 ? NEGATIVE_SIDE_VERTEX : NEGATIVE_SIDE_EDGE);
244  }
245  else
246  {
247  // The polygon is strictly on the negative side of the plane.
248  result.intersect = false;
249  result.configuration = NEGATIVE_SIDE_STRICT;
250  }
251  }
252  else // numZero == numVertices
253  {
254  // The polygon is coincident with the plane.
255  result.intersect = true;
256  result.configuration = COINCIDENT_WITH_PLANE;
257  }
258 
259  return result;
260 }
261 
262 template <typename Real>
265  std::vector<Vector3<Real>> const& polygon, Plane3<Real> const& plane)
266 {
267  Result result;
268 
269  int const numVertices = static_cast<int>(polygon.size());
270  if (numVertices < 3)
271  {
272  // The convex polygon must have at least 3 vertices.
273  result.configuration = INVALID_POLYGON;
274  return result;
275  }
276 
277  // Determine on which side of the plane the vertices live. The index
278  // maxPosIndex stores the index of the vertex on the positive side of
279  // the plane that is farthest from the plane. The index maxNegIndex
280  // stores the index of the vertex on the negative side of the plane that
281  // is farthest from the plane. If one or the other such vertex does not
282  // exist, the corresponding index will remain its initial value of -1.
283  // positive side of the plane.
284  std::vector<Real> height(polygon.size());
285  std::vector<int> zeroHeightIndices;
286  int numPositive = 0, numNegative = 0;
287  Real maxPosHeight = -std::numeric_limits<Real>::max();
288  Real maxNegHeight = std::numeric_limits<Real>::max();
289  int maxPosIndex = -1, maxNegIndex = -1;
290  for (int i = 0; i < numVertices; ++i)
291  {
292  height[i] = Dot(plane.normal, polygon[i]) - plane.constant;
293  if (height[i] >(Real)0)
294  {
295  ++numPositive;
296  if (height[i] > maxPosHeight)
297  {
298  maxPosHeight = height[i];
299  maxPosIndex = i;
300  }
301  }
302  else if (height[i] < (Real)0)
303  {
304  ++numNegative;
305  if (height[i] < maxNegHeight)
306  {
307  maxNegHeight = height[i];
308  maxNegIndex = i;
309  }
310  }
311  else
312  {
313  zeroHeightIndices.push_back(i);
314  }
315  }
316 
317  if (numPositive > 0)
318  {
319  if (numNegative > 0)
320  {
321  // The polygon and plane intersect transversely.
322  result.configuration = SPLIT_BY_PLANE;
323 
324  // Read the preamble comments about why the plane normal and
325  // heights are processed this way.
326  int imax = 0;
327  Real cmax = fabs(plane.normal[0]);
328  Real cvalue = fabs(plane.normal[1]);
329  if (cvalue > cmax)
330  {
331  cmax = cvalue;
332  imax = 1;
333  }
334  cvalue = fabs(plane.normal[2]);
335  if (cvalue > cmax)
336  {
337  cmax = cvalue;
338  imax = 2;
339  }
340 
341  bool doSwap = (plane.normal[imax] < (Real)0);
342  if (doSwap)
343  {
344  for (auto& h : height)
345  {
346  h = -h;
347  }
348  std::swap(maxPosIndex, maxNegIndex);
349  }
350 
351  SplitPolygon(polygon, height, maxPosIndex, result);
352 
353  if (doSwap)
354  {
355  std::swap(result.positivePolygon, result.negativePolygon);
356  }
357  }
358  else
359  {
360  if (zeroHeightIndices.size() > 0)
361  {
362  // The polygon touches the plane in a vertex or an edge.
363  if (zeroHeightIndices.size() == 1)
364  {
365  result.configuration = POSITIVE_SIDE_VERTEX;
366  result.intersection.push_back(polygon[zeroHeightIndices[0]]);
367  }
368  else
369  {
370  result.configuration = POSITIVE_SIDE_EDGE;
371  result.intersection.push_back(polygon[zeroHeightIndices[0]]);
372  result.intersection.push_back(polygon[zeroHeightIndices[1]]);
373  }
374  }
375  else
376  {
377  // The polygon is strictly on the positive side of the plane.
378  result.configuration = POSITIVE_SIDE_STRICT;
379  }
380  result.positivePolygon = polygon;
381  }
382  }
383  else if (numNegative > 0)
384  {
385  if (zeroHeightIndices.size() > 0)
386  {
387  // The polygon touches the plane in a vertex or an edge.
388  if (zeroHeightIndices.size() == 1)
389  {
390  result.configuration = NEGATIVE_SIDE_VERTEX;
391  result.intersection.push_back(polygon[zeroHeightIndices[0]]);
392  }
393  else
394  {
395  result.configuration = NEGATIVE_SIDE_EDGE;
396  result.intersection.push_back(polygon[zeroHeightIndices[0]]);
397  result.intersection.push_back(polygon[zeroHeightIndices[1]]);
398  }
399  }
400  else
401  {
402  // The polygon is strictly on the negative side of the plane.
403  result.configuration = NEGATIVE_SIDE_STRICT;
404  }
405  result.negativePolygon = polygon;
406  }
407  else // numZero == numVertices
408  {
409  // The polygon is coincident with the plane.
410  result.configuration = COINCIDENT_WITH_PLANE;
411  result.intersection = polygon;
412  }
413 
414  return result;
415 }
416 
417 template <typename Real>
419  std::vector<Vector3<Real>> const& polygon, std::vector<Real> const& height,
420  int maxPosIndex, Result& result)
421 {
422  // Find the largest contiguous subset of indices for which height[i] >= 0.
423  int const numVertices = static_cast<int>(polygon.size());
424  std::list<Vector3<Real>> positiveList;
425  positiveList.push_back(polygon[maxPosIndex]);
426  int end0 = maxPosIndex, end0prev = -1;
427  for (int i = 0; i < numVertices; ++i)
428  {
429  end0prev = (end0 + numVertices - 1) % numVertices;
430  if (height[end0prev] >= (Real)0)
431  {
432  positiveList.push_front(polygon[end0prev]);
433  end0 = end0prev;
434  }
435  else
436  {
437  break;
438  }
439  }
440 
441  int end1 = maxPosIndex, end1next = -1;
442  for (int i = 0; i < numVertices; ++i)
443  {
444  end1next = (end1 + 1) % numVertices;
445  if (height[end1next] >= (Real)0)
446  {
447  positiveList.push_back(polygon[end1next]);
448  end1 = end1next;
449  }
450  else
451  {
452  break;
453  }
454  }
455 
456  int index = end1next;
457  std::list<Vector3<Real>> negativeList;
458  for (int i = 0; i < numVertices; ++i)
459  {
460  negativeList.push_back(polygon[index]);
461  index = (index + 1) % numVertices;
462  if (index == end0)
463  {
464  break;
465  }
466  }
467 
468  // Clip the polygon.
469  if (height[end0] > 0)
470  {
471  Real t = -height[end0prev] / (height[end0] - height[end0prev]);
472  Real omt = (Real)1 - t;
473  Vector3<Real> V = omt * polygon[end0prev] + t * polygon[end0];
474  positiveList.push_front(V);
475  negativeList.push_back(V);
476  result.intersection.push_back(V);
477  }
478  else
479  {
480  negativeList.push_back(polygon[end0]);
481  result.intersection.push_back(polygon[end0]);
482  }
483 
484  if (height[end1] > 0)
485  {
486  Real t = -height[end1next] / (height[end1] - height[end1next]);
487  Real omt = (Real)1 - t;
488  Vector3<Real> V = omt * polygon[end1next] + t * polygon[end1];
489  positiveList.push_back(V);
490  negativeList.push_front(V);
491  result.intersection.push_back(V);
492  }
493  else
494  {
495  negativeList.push_front(polygon[end1]);
496  result.intersection.push_back(polygon[end1]);
497  }
498 
499  result.positivePolygon.reserve(positiveList.size());
500  for (auto const& p : positiveList)
501  {
502  result.positivePolygon.push_back(p);
503  }
504 
505  result.negativePolygon.reserve(negativeList.size());
506  for (auto const& p : negativeList)
507  {
508  result.negativePolygon.push_back(p);
509  }
510 }
511 
512 }
DualQuaternion< Real > Dot(DualQuaternion< Real > const &d0, DualQuaternion< Real > const &d1)
GLdouble GLdouble t
Definition: glext.h:239
GLint GLsizei GLsizei height
Definition: glcorearb.h:98
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:1997
GLuint index
Definition: glcorearb.h:781
Result operator()(Type0 const &primitive0, Type1 const &primitive1)
GLfloat GLfloat p
Definition: glext.h:11668
GLuint64EXT * result
Definition: glext.h:10003


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:00