HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_Convex.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_Convex.h ( UT Library, C++)
7  *
8  * COMMENTS: This code does general polygon convexing. It's down here
9  * in UT since there are places other than the GEO lib which
10  * may need to use convexing code (i.e. the RE lib, or UI).
11  * This code convexes 2D polygons.
12  */
13 
14 #ifndef __UT_Convex__
15 #define __UT_Convex__
16 
17 #include "UT_API.h"
18 #include "UT_NonCopyable.h"
19 #include "UT_Vector3.h"
20 #include "UT_Vector2.h"
21 #include "UT_Array.h"
22 #include <SYS/SYS_Types.h>
23 #include <stdlib.h>
24 
25 class UT_ConvexPoint;
26 class UT_Interrupt;
27 
29 public:
31  {
32  myData = 0;
33  myConvexInfo = 0;
34  myConvexSize = 0;
35  myConvexEntries = 0;
36  myPointList = 0;
37  myPointSize = 0;
38  }
39  virtual ~UT_Convex()
40  {
41  if (myConvexInfo) free(myConvexInfo);
42  if (myPointList) free(myPointList);
43  }
44 
46 
47  bool isConcave();
48  void convex(exint maxsides, bool allowinterrupt = true,
49  bool avoiddegeneracy = false);
50 
51  virtual void initialize();
52  /// It's the sub-class responsibility to fill these out...
53  virtual int getPointCount() const = 0;
54  virtual void getPoint(int num, float &x, float &y) const = 0;
55 
56  /// beginPolygon will be told when the last polygon is being added
57  virtual void beginPolygon(int npoints, bool lastone) = 0;
58  virtual void polyVertex(int num) = 0;
59  virtual void endPolygon() = 0;
60 
61  /// If the last polygon to be added won't be added after all, e.g. if it is
62  /// degenerate and avoiddegeneracy is true, the earlyTermination() method
63  /// will be called. This is so that the caller can clean up properly, since
64  /// beginPolygon will *not* have been called with lastone==true.
65  /// By default, this method does nothing.
66  virtual void earlyTermination();
67 
68  /// Returns true if splitting the specified quad along the line from
69  /// point 0 to point 2 is possible
70  static inline bool canSplitQuad02(const UT_Vector3 &p0,
71  const UT_Vector3 &p1,
72  const UT_Vector3 &p2,
73  const UT_Vector3 &p3)
74  {
75  const UT_Vector3 v0 = p1 - p0;
76  const UT_Vector3 v1 = p2 - p1;
77  const UT_Vector3 v2 = p3 - p2;
78  const UT_Vector3 v3 = p0 - p3;
79  // Okay if normals at 1 and 3 point in the same direction
80  return ((cross(v0, v1)).dot(cross(v2, v3)) > 0);
81  }
82 
83  /// Returns true if the specified quad is convex.
84  /// NOTE: This may return false negatives for highly non-planar quads,
85  /// since the definition of convex breaks down.
86  static inline bool isQuadConvex(const UT_Vector3 &p0,
87  const UT_Vector3 &p1,
88  const UT_Vector3 &p2,
89  const UT_Vector3 &p3)
90  {
91  const UT_Vector3 v0 = p1 - p0;
92  const UT_Vector3 v1 = p2 - p1;
93  const UT_Vector3 v2 = p3 - p2;
94  const UT_Vector3 v3 = p0 - p3;
95  // Convex if normals at 1 and 3 point in the same direction
96  // and normals at 0 and 2 point in the same direction
97  return (cross(v0, v1).dot(cross(v2, v3)) > 0)
98  && (cross(v1, v2).dot(cross(v3, v0)) > 0);
99  }
100 
101  /// Relative size below which a triangle or diagonal counts as degenerate.
102  /// A template so that it is always inlined, never imported from the DLL.
103  template <typename T = fpreal>
104  static constexpr T smallAngleTolerance() { return T(1e-6); }
105 
106  /// Returns true if, for triangles (a,b,c) and (c,b,d) sharing edge b-c,
107  /// the diagonal a-d gives the larger smallest angle. This is the "Avoid
108  /// Small Angles" test of SOP_Divide and handles degenerate input.
109  /*
110  * D D
111  * / \ /|\
112  * / 1 \ / | \
113  * / \ / | \
114  * C-------B flips to C 3 | 2 B
115  * \ / \ | /
116  * \ 0 / \ | /
117  * \ / \|/
118  * A A
119  */
120  template <typename T>
121  static bool shouldFlipEdge(const UT_Vector2T<T> &a,
122  const UT_Vector2T<T> &b,
123  const UT_Vector2T<T> &c,
124  const UT_Vector2T<T> &d)
125  {
126  const fpreal tol = smallAngleTolerance();
127 
128  // First check if a flip is even possible
129  // If sign(ca X cd) != sign(bd x ba), can't flip
130  UT_Vector2T<T> ca = a-c;
131  UT_Vector2T<T> cd = d-c;
132  UT_Vector2T<T> bd = d-b;
133  UT_Vector2T<T> ba = a-b;
134  fpreal ccross = cross(ca, cd);
135  fpreal bcross = cross(bd, ba);
136  fpreal lca = ca.length();
137  fpreal lcd = cd.length();
138  fpreal lbd = bd.length();
139  fpreal lba = ba.length();
140  fpreal cscale = lca * lcd;
141  fpreal bscale = lbd * lba;
142  // If c is definitely negative and b is definitely positive,
143  // or c is definitely positive and b is definitely negative,
144  // can't flip, since one angle is reflex.
145  if ((ccross < -tol * cscale && bcross > tol * bscale) ||
146  (ccross > tol * cscale && bcross < -tol * bscale))
147  return false;
148 
149  fpreal perim = lca + lcd + lbd + lba;
150  fpreal lsmall = tol * perim;
151 
152  // Compute diagonals
153  UT_Vector2T<T> ad = d-a;
154  UT_Vector2T<T> bc = c-b;
155  fpreal lad = ad.length();
156  fpreal lbc = bc.length();
157 
158  // Handle near-degenerate diagonals by
159  // choosing the zero-length diagonal.
160  // Having the = handles the case of exactly zero perimeter.
161  if (lbc <= lsmall)
162  return false;
163  if (lad <= lsmall)
164  return true;
165 
166  // In all other degenerate point cases, flipping will
167  // result in just as bad a triangle, so don't bother.
168  if (SYSmin(lba, lca, lbd, lcd) <= lsmall)
169  return false;
170 
171  // Don't need to consider angles CAB or BDC,
172  // because the condition is always determined by
173  // ABC, CBD, DCB, BCA; CAD, DAB, BDA, ADC
174  // Since these angles are always <= 90 degrees, (or close enough
175  // in the case of roundoff with near-degeneracies), the (absolute)
176  // sine of the angles suffices for this comparison.
177  // Will use: |sin(XYZ)| = 2*area(XYZ)/(|XY|*|YZ|)
178  fpreal area2x2overad = SYSabs(bcross) / lad;
179  fpreal area3x2overad = SYSabs(ccross) / lad;
180  // Compute both crosses for the other two triangles
181  // One could be eliminated subtracting from the total area,
182  // but need absolute areas even if negative area from roundoff.
183  fpreal across = cross(ba, ca); // same as ab X ac
184  fpreal dcross = cross(cd, bd); // same as dc X db
185  fpreal area0x2overbc = SYSabs(across) / lbc;
186  fpreal area1x2overbc = SYSabs(dcross) / lbc;
187 
188  fpreal min01 = SYSmin(area0x2overbc / lba,
189  area0x2overbc / lca,
190  area1x2overbc / lbd,
191  area1x2overbc / lcd);
192  fpreal min23 = SYSmin(area2x2overad / lba,
193  area2x2overad / lbd,
194  area3x2overad / lca,
195  area3x2overad / lcd);
196  return min23 > min01;
197  }
198 
199  /// Flips edges of the triangulation tripts (three corner indices per
200  /// triangle) until the smallest angles are maximized. pts(i) is the 2D
201  /// position of corner i. on_flip(tri, tri2, i, j) is called before each
202  /// flip of tri's edge i, shared with tri2's edge j.
203  template <typename PTS, typename TRIS, typename ON_FLIP>
204  static void flipEdgesToAvoidSmallAngles(const PTS &pts, TRIS &tripts,
205  ON_FLIP &&on_flip)
206  {
207  const int ntripts = int(tripts.size());
208  const int ntris = ntripts/3;
209 
210  // For a triangle ABC, the adjacent triangles are in the
211  // order of the triangles on the opposite side of:
212  // AB, then BC, then CA. -1 if no opposite triangle
213  // The integer for a triangle's edge is the index
214  // of the other triangle's edge, which points back.
215  UT_Array<int> adjtrioff;
216  adjtrioff.setSize(ntripts);
217  for (int i = 0; i < ntripts; ++i)
218  adjtrioff(i) = -1;
219  for (int tri = 0; tri < ntris; ++tri)
220  {
221  int trioff = 3 * tri;
222  for (int i = 0; i < 3; ++i)
223  {
224  // Don't revisit edges
225  if (adjtrioff(trioff + i) >= 0)
226  continue;
227  // Edge points are i and (i+1)%3
228  int pti = tripts(trioff + i);
229  int ptip1 = tripts(trioff + (i + 1) % 3);
230  bool found = false;
231  // Search all following triangles' edges
232  for (int tri2 = tri+1; tri2 < ntris && !found; ++tri2)
233  {
234  int tri2off = 3 * tri2;
235  for (int j = 0; j < 3; ++j)
236  {
237  int ptj = tripts(tri2off + j);
238  int ptjp1 = tripts(tri2off + (j + 1) % 3);
239  // Opposite edge is in opposite direction
240  if (ptjp1 == pti && ptj == ptip1)
241  {
242  adjtrioff(trioff + i) = tri2off + j;
243  adjtrioff(tri2off + j) = trioff + i;
244  found = true;
245  break;
246  }
247  }
248  }
249  }
250  }
251 
252  // Loop through the edges until none need flipping
253  bool edgesflipped;
254  int64 count = 0;
255  do
256  {
257  edgesflipped = false;
258  for (int tri = 0; tri < ntris; ++tri)
259  {
260  int trioff = 3 * tri;
261  for (int i = 0; i < 3; ++i)
262  {
263  if (adjtrioff(trioff + i) < 0)
264  continue;
265 
266  int j = adjtrioff(trioff + i) % 3;
267  int tri2off = adjtrioff(trioff + i) - j;
268  int tri2 = tri2off / 3;
269 
270  int a = tripts(trioff + ((i + 2) % 3));
271  int b = tripts(trioff + i);
272  int c = tripts(trioff + ((i + 1) % 3));
273  int d = tripts(tri2off + ((j + 2) % 3));
274 
275  if (!shouldFlipEdge(pts(a), pts(b), pts(c), pts(d)))
276  continue;
277 
278  /* Flip the edge
279  *
280  * j+2 j+2 i+1
281  * / \ /|\
282  * / \ / | \
283  * / tri2\ / t|t \
284  * j-------j+1 flips to j r|r i
285  * i+1\ tri /i \ i|i /
286  * \ / \2| /
287  * \ / \|/
288  * i+2 j+1 i+2
289  */
290 
291  // Update the caller's geometry
292  on_flip(tri, tri2, i, j);
293 
294  // Vertex at i+1 must be changed to vertex at j+2
295  // Vertex at j+1 must be changed to vertex at i+2
296  tripts(trioff + (i + 1) % 3) = tripts(tri2off + (j + 2) % 3);
297  tripts(tri2off + (j + 1) % 3) = tripts(trioff + (i + 2) % 3);
298 
299  // Edge i is where edge j+1 was
300  int nowacrossedgei = adjtrioff(tri2off + (j + 1) % 3);
301  adjtrioff(trioff + i) = nowacrossedgei;
302  if (nowacrossedgei >= 0)
303  adjtrioff(nowacrossedgei) = trioff + i;
304  // Edge j is where edge i+1 was
305  int nowacrossedgej = adjtrioff(trioff + (i + 1) % 3);
306  adjtrioff(tri2off + j) = nowacrossedgej;
307  if (nowacrossedgej >= 0)
308  adjtrioff(nowacrossedgej) = tri2off + j;
309  // Edges i+1 and j+1 are between the two triangles
310  adjtrioff(trioff + (i + 1) % 3) = tri2off + (j + 1) % 3;
311  adjtrioff(tri2off + (j + 1) % 3) = trioff + (i + 1) % 3;
312 
313  edgesflipped = true;
314 
315  // Edge i+1 is now the one between these two triangles,
316  // so there's no need to check it next.
317  // All 3 edges will be checked again on the next pass anyway.
318  break;
319  }
320  }
321  ++count;
322  } while (edgesflipped && count < int64(ntris) * ntris);
323  }
324 
325  /// As above, without a flip callback.
326  template <typename PTS, typename TRIS>
327  static void flipEdgesToAvoidSmallAngles(const PTS &pts, TRIS &tripts)
328  {
329  flipEdgesToAvoidSmallAngles(pts, tripts, [](int, int, int, int) {});
330  }
331 
332  virtual int64 getMemoryUsage(bool inclusive) const;
333 
334 private:
335  UT_ConvexPoint *polySetup(bool avoiddegeneracy);
336  int leftOrRight(int v1, int v2, int v3, bool &double_back);
337  int leftOrRight(int v1, int v2, int v3)
338  {
339  bool double_back;
340  return leftOrRight(v1, v2, v3, double_back);
341  }
342  uint leftMost(uint a, uint b, int ccwise);
343  bool testConcave(uint first, uint last);
344  bool arePointsSame(uint i, uint j);
345  bool isDegenerate(uint first, uint last);
346  bool addPolygon(
347  UT_Interrupt *boss, uint first,
348  uint last, bool lastone,
349  bool avoiddegeneracy);
350  void addTriangle(uint a, uint b, uint c,
351  bool lastone);
352  void doSplit(uint first, uint newfirst,
353  uint newlast, uint last);
354  bool cornerTest(int side, uint la, uint lb,
355  uint first, uint last);
356  bool insideOut(uint k, uint la,
357  uint lb, uint first, uint last) const;
358  uint chooseCoincidentIntruder(
359  uint l, uint k1, uint k2,
360  uint first, uint last, uint ccwise);
361  uint findClosestInRange(uint lb, uint la,
362  uint ls, uint first, uint last);
363  uint testIntersect(uint l, uint lb, uint la,
364  uint first, uint last, int &ccwise);
365 
366 protected:
367  /// NOTE: You *must* set myData to NULL before
368  /// calling isConcave() or convex() !
369  UT_ConvexPoint *myData;
370 
371 private:
372  UT_ConvexPoint *myConvexInfo;
373  uint myConvexSize;
374  uint myConvexEntries;
375  int *myPointList;
376  uint myPointSize;
377 
378  /// NOTE: The only purpose of this bool is because isConvex() must call
379  /// polySetup(true), so if there were degenerate points and
380  /// convex(__, __, false) is then called, it needs to know to call
381  /// polySetup(false) to get those omitted points.
382  bool myAvoidDegeneracyPrev;
383 };
384 
385 #endif
386 
GLint first
Definition: glcorearb.h:405
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
static void flipEdgesToAvoidSmallAngles(const PTS &pts, TRIS &tripts, ON_FLIP &&on_flip)
Definition: UT_Convex.h:204
static bool isQuadConvex(const UT_Vector3 &p0, const UT_Vector3 &p1, const UT_Vector3 &p2, const UT_Vector3 &p3)
Definition: UT_Convex.h:86
int64 exint
Definition: SYS_Types.h:125
SIM_DerScalar dot(const SIM_DerVector3 &rhs) const
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
#define SYSabs(a)
Definition: SYS_Math.h:1954
#define UT_API
Definition: UT_API.h:14
GLint y
Definition: glcorearb.h:103
UT_Convex()
Definition: UT_Convex.h:30
GLfloat GLfloat GLfloat v2
Definition: glcorearb.h:818
GLfloat GLfloat GLfloat GLfloat v3
Definition: glcorearb.h:819
2D Vector class.
Definition: UT_Vector2.h:162
void setSize(exint newsize)
Definition: UT_Array.h:690
static bool shouldFlipEdge(const UT_Vector2T< T > &a, const UT_Vector2T< T > &b, const UT_Vector2T< T > &c, const UT_Vector2T< T > &d)
Definition: UT_Convex.h:121
constexpr SYS_FORCE_INLINE T length() const noexcept
Definition: UT_Vector2.h:297
fpreal64 dot(const CE_VectorT< T > &a, const CE_VectorT< T > &b)
Definition: CE_Vector.h:138
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
long long int64
Definition: SYS_Types.h:116
UT_ConvexPoint * myData
Definition: UT_Convex.h:369
static constexpr T smallAngleTolerance()
Definition: UT_Convex.h:104
OPENVDB_API void initialize()
Global registration of native Grid, Transform, Metadata and Point attribute types. Also initializes blosc (if enabled).
Definition: logging.h:294
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
GLint GLenum GLint x
Definition: glcorearb.h:409
virtual ~UT_Convex()
Definition: UT_Convex.h:39
GLfloat v0
Definition: glcorearb.h:816
__hostdev__ uint64_t last(uint32_t i) const
Definition: NanoVDB.h:5976
GLint j
Definition: glad.h:2733
fpreal64 fpreal
Definition: SYS_Types.h:283
GLfloat GLfloat v1
Definition: glcorearb.h:817
static void flipEdgesToAvoidSmallAngles(const PTS &pts, TRIS &tripts)
As above, without a flip callback.
Definition: UT_Convex.h:327
static bool canSplitQuad02(const UT_Vector3 &p0, const UT_Vector3 &p1, const UT_Vector3 &p2, const UT_Vector3 &p3)
Definition: UT_Convex.h:70
#define SYSmin(a, b)
Definition: SYS_Math.h:1953
unsigned int uint
Definition: SYS_Types.h:45
SIM_DerVector3 cross(const SIM_DerVector3 &lhs, const SIM_DerVector3 &rhs)
GLint GLsizei count
Definition: glcorearb.h:405