ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
CVMiscTools.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "CVMiscTools.h"
9 
10 // local
11 #include "CVConst.h"
12 
13 // system
14 #include <algorithm>
15 #include <cassert>
16 #include <cstring>
17 
18 #ifdef USE_VLD
19 // VLD
20 #include <vld.h>
21 #endif
22 
23 /*** MACROS FOR TRIBOXOVERLAP ***/
24 /*** TRIBOXOVERLAP code is largely inspired from Tomas algorithm
25  http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tribox3.txt
26 **/
27 
28 #ifdef FINDMINMAX
29 #undef FINDMINMAX
30 #endif
31 #define FINDMINMAX(x0, x1, x2, minV, maxV) \
32  minV = maxV = x0; \
33  if (x1 < minV) \
34  minV = x1; \
35  else if (x1 > maxV) \
36  maxV = x1; \
37  if (x2 < minV) \
38  minV = x2; \
39  else if (x2 > maxV) \
40  maxV = x2;
41 
42 /*======================== X-tests ========================*/
43 #define AXISTEST_X01(a, b, fa, fb) \
44  minV = a * v0[1] - b * v0[2]; \
45  maxV = a * v2[1] - b * v2[2]; \
46  if (maxV < minV) std::swap(minV, maxV); \
47  rad = fa * boxhalfSize.y + fb * boxhalfSize.z; \
48  if (minV > rad || maxV < -rad) return 0;
49 #define AXISTEST_X2(a, b, fa, fb) \
50  minV = a * v0[1] - b * v0[2]; \
51  maxV = a * v1[1] - b * v1[2]; \
52  if (maxV < minV) std::swap(minV, maxV); \
53  rad = fa * boxhalfSize.y + fb * boxhalfSize.z; \
54  if (minV > rad || maxV < -rad) return 0;
55 
56 /*======================== Y-tests ========================*/
57 #define AXISTEST_Y02(a, b, fa, fb) \
58  minV = -a * v0[0] + b * v0[2]; \
59  maxV = -a * v2[0] + b * v2[2]; \
60  if (maxV < minV) std::swap(minV, maxV); \
61  rad = fa * boxhalfSize.x + fb * boxhalfSize.z; \
62  if (minV > rad || maxV < -rad) return 0;
63 #define AXISTEST_Y1(a, b, fa, fb) \
64  minV = -a * v0[0] + b * v0[2]; \
65  maxV = -a * v1[0] + b * v1[2]; \
66  if (maxV < minV) std::swap(minV, maxV); \
67  rad = fa * boxhalfSize.x + fb * boxhalfSize.z; \
68  if (minV > rad || maxV < -rad) return 0;
69 
70 /*======================== Z-tests ========================*/
71 #define AXISTEST_Z12(a, b, fa, fb) \
72  minV = a * v1[0] - b * v1[1]; \
73  maxV = a * v2[0] - b * v2[1]; \
74  if (maxV < minV) std::swap(minV, maxV); \
75  rad = fa * boxhalfSize.x + fb * boxhalfSize.y; \
76  if (minV > rad || maxV < -rad) return 0;
77 #define AXISTEST_Z0(a, b, fa, fb) \
78  minV = a * v0[0] - b * v0[1]; \
79  maxV = a * v1[0] - b * v1[1]; \
80  if (maxV < minV) std::swap(minV, maxV); \
81  rad = fa * boxhalfSize.x + fb * boxhalfSize.y; \
82  if (minV > rad || maxV < -rad) return 0;
83 
84 using namespace cloudViewer;
85 
86 /******** Geometry *********/
87 
89  CCVector3& dimMax,
90  double enlargeFactor /*=0.01*/) {
91  // get box max dimension
92  PointCoordinateType maxDD = 0;
93  {
94  CCVector3 diag = dimMax - dimMin;
95  maxDD = std::max(diag.x, diag.y);
96  maxDD = std::max(maxDD, diag.z);
97  }
98 
99  // enlarge it if necessary
100  if (enlargeFactor > 0)
101  maxDD = static_cast<PointCoordinateType>(static_cast<double>(maxDD) *
102  (1.0 + enlargeFactor));
103 
104  // build corresponding 'square' box
105  {
106  CCVector3 dd(maxDD, maxDD, maxDD);
107  CCVector3 md = dimMax + dimMin;
108 
109  dimMin = (md - dd) * static_cast<PointCoordinateType>(0.5);
110  dimMax = dimMin + dd;
111  }
112 }
113 
115  CCVector3& dimMax,
116  double coef) {
117  CCVector3 dd =
118  (dimMax - dimMin) * static_cast<PointCoordinateType>(1.0 + coef);
119  CCVector3 md = dimMax + dimMin;
120 
121  dimMin = (md - dd) * static_cast<PointCoordinateType>(0.5);
122  dimMax = dimMin + dd;
123 }
124 
125 /**** tribox3.c *****/
126 
127 bool CCMiscTools::TriBoxOverlap(const CCVector3& boxcenter,
128  const CCVector3& boxhalfSize,
129  const CCVector3* triverts[3]) {
130  /* use separating axis theorem to test overlap between triangle and box
131  */
132  /* need to test for overlap in these directions: */
133  /* 1) the {X,Y,Z}-directions (actually, since we use the AABB of the
134  * triangle */
135  /* we do not even need to test these) */
136  /* 2) normal of the triangle */
137  /* 3) crossproduct(edge from tri, {X,Y,Z}-direction) */
138  /* this gives 3x3=9 more tests */
139 
140  /* move everything so that the boxcenter is in (0,0,0) */
141  PointCoordinateType v0[3], v1[3], v2[3];
142  CCVector3::vsubstract(triverts[0]->u, boxcenter.u, v0);
143  CCVector3::vsubstract(triverts[1]->u, boxcenter.u, v1);
144  CCVector3::vsubstract(triverts[2]->u, boxcenter.u, v2);
145 
146  PointCoordinateType e0[3];
147  CCVector3::vsubstract(v1, v0, e0); /* compute triangle edge 0 */
148 
149  /* Bullet 3: */
150 
151  /* test the 9 tests first (this was faster) */
152  PointCoordinateType rad, fex, fey,
153  fez; // -NJMP- "d" local variable removed
154  // fex = std::abs(e0[0]);
155  fey = std::abs(e0[1]);
156  fez = std::abs(e0[2]);
157 
158  PointCoordinateType minV, maxV;
159  AXISTEST_X01(e0[2], e0[1], fez, fey);
160  fex = std::abs(e0[0]); // DGM: not necessary before!
161  AXISTEST_Y02(e0[2], e0[0], fez, fex);
162  AXISTEST_Z12(e0[1], e0[0], fey, fex);
163 
164  PointCoordinateType e1[3];
165  CCVector3::vsubstract(v2, v1, e1); /* compute triangle edge 1 */
166 
167  // fex = std::abs(e1[0]);
168  fey = std::abs(e1[1]);
169  fez = std::abs(e1[2]);
170 
171  AXISTEST_X01(e1[2], e1[1], fez, fey);
172  fex = std::abs(e1[0]); // DGM: not necessary before!
173  AXISTEST_Y02(e1[2], e1[0], fez, fex);
174  AXISTEST_Z0(e1[1], e1[0], fey, fex);
175 
176  PointCoordinateType e2[3];
177  CCVector3::vsubstract(v0, v2, e2); /* compute triangle edge 2 */
178 
179  // fex = std::abs(e2[0]);
180  fey = std::abs(e2[1]);
181  fez = std::abs(e2[2]);
182 
183  AXISTEST_X2(e2[2], e2[1], fez, fey);
184  fex = std::abs(e2[0]); // DGM: not necessary before!
185  AXISTEST_Y1(e2[2], e2[0], fez, fex);
186  AXISTEST_Z12(e2[1], e2[0], fey, fex);
187 
188  /* Bullet 1: */
189 
190  /* first test overlap in the {X,Y,Z}-directions */
191  /* find minV, maxV of the triangle each direction, and test for overlap in
192  */
193  /* that direction -- this is equivalent to testing a minimal AABB around */
194  /* the triangle against the AABB */
195 
196  /* test in 0-direction */
197  FINDMINMAX(v0[0], v1[0], v2[0], minV, maxV);
198  if (minV > boxhalfSize.x || maxV < -boxhalfSize.x) return false;
199 
200  /* test in 1-direction */
201  FINDMINMAX(v0[1], v1[1], v2[1], minV, maxV);
202  if (minV > boxhalfSize.y || maxV < -boxhalfSize.y) return false;
203 
204  /* test in 2-direction */
205  FINDMINMAX(v0[2], v1[2], v2[2], minV, maxV);
206  if (minV > boxhalfSize.z || maxV < -boxhalfSize.z) return false;
207 
208  /* Bullet 2: */
209  /* test if the box intersects the plane of the triangle */
210  /* compute plane equation of triangle: normal*0+d=0 */
211 
212  // PointCoordinateType normal[3];
213  CCVector3::vcross(e0, e1, /*normal*/ e2); // DGM: we use 'e2' instead of
214  // 'normal' to save heap memory
215  {
216  // PointCoordinateType vmin[3],vmax[3]; //DGM: we use e0 and e1 instead
217  // of vmin and vmax
218  if (/*normal*/ e2[0] > 0) {
219  /*vmin*/ e0[0] = -boxhalfSize.x - v0[0];
220  /*vmax*/ e1[0] = boxhalfSize.x - v0[0];
221  } else {
222  /*vmin*/ e0[0] = boxhalfSize.x - v0[0];
223  /*vmax*/ e1[0] = -boxhalfSize.x - v0[0];
224  }
225  if (/*normal*/ e2[1] > 0) {
226  /*vmin*/ e0[1] = -boxhalfSize.y - v0[1];
227  /*vmax*/ e1[1] = boxhalfSize.y - v0[1];
228  } else {
229  /*vmin*/ e0[1] = boxhalfSize.y - v0[1];
230  /*vmax*/ e1[1] = -boxhalfSize.y - v0[1];
231  }
232  if (/*normal*/ e2[2] > 0) {
233  /*vmin*/ e0[2] = -boxhalfSize.z - v0[2];
234  /*vmax*/ e1[2] = boxhalfSize.z - v0[2];
235  } else {
236  /*vmin*/ e0[2] = boxhalfSize.z - v0[2];
237  /*vmax*/ e1[2] = -boxhalfSize.z - v0[2];
238  }
239 
240  if (CCVector3::vdot(/*normal*/ e2, /*vmin*/ e0) > 0 ||
241  CCVector3::vdot(/*normal*/ e2, /*vmax*/ e1) < 0) {
242  return false;
243  }
244  }
245 
246  return true; /* box and triangle overlaps */
247 }
248 
250  const CCVector3d& boxhalfSize,
251  const CCVector3d triverts[3]) {
252  /* use separating axis theorem to test overlap between triangle and box
253  */
254  /* need to test for overlap in these directions: */
255  /* 1) the {X,Y,Z}-directions (actually, since we use the AABB of the
256  * triangle */
257  /* we do not even need to test these) */
258  /* 2) normal of the triangle */
259  /* 3) crossproduct(edge from tri, {X,Y,Z}-direction) */
260  /* this gives 3x3=9 more tests */
261 
262  /* move everything so that the boxcenter is in (0,0,0) */
263  double v0[3], v1[3], v2[3];
264  CCVector3d::vsubstract(triverts[0].u, boxcenter.u, v0);
265  CCVector3d::vsubstract(triverts[1].u, boxcenter.u, v1);
266  CCVector3d::vsubstract(triverts[2].u, boxcenter.u, v2);
267 
268  double e0[3];
269  CCVector3d::vsubstract(v1, v0, e0); /* compute triangle edge 0 */
270 
271  /* Bullet 3: */
272 
273  /* test the 9 tests first (this was faster) */
274  double rad, fex, fey, fez; // -NJMP- "d" local variable removed
275  // fex = std::abs(e0[0]);
276  fey = std::abs(e0[1]);
277  fez = std::abs(e0[2]);
278 
279  double minV, maxV;
280  AXISTEST_X01(e0[2], e0[1], fez, fey);
281  fex = std::abs(e0[0]); // DGM: not necessary before!
282  AXISTEST_Y02(e0[2], e0[0], fez, fex);
283  AXISTEST_Z12(e0[1], e0[0], fey, fex);
284 
285  double e1[3];
286  CCVector3d::vsubstract(v2, v1, e1); /* compute triangle edge 1 */
287 
288  // fex = std::abs(e1[0]);
289  fey = std::abs(e1[1]);
290  fez = std::abs(e1[2]);
291 
292  AXISTEST_X01(e1[2], e1[1], fez, fey);
293  fex = std::abs(e1[0]); // DGM: not necessary before!
294  AXISTEST_Y02(e1[2], e1[0], fez, fex);
295  AXISTEST_Z0(e1[1], e1[0], fey, fex);
296 
297  double e2[3];
298  CCVector3d::vsubstract(v0, v2, e2); /* compute triangle edge 2 */
299 
300  // fex = std::abs(e2[0]);
301  fey = std::abs(e2[1]);
302  fez = std::abs(e2[2]);
303 
304  AXISTEST_X2(e2[2], e2[1], fez, fey);
305  fex = std::abs(e2[0]); // DGM: not necessary before!
306  AXISTEST_Y1(e2[2], e2[0], fez, fex);
307  AXISTEST_Z12(e2[1], e2[0], fey, fex);
308 
309  /* Bullet 1: */
310 
311  /* first test overlap in the {X,Y,Z}-directions */
312  /* find minV, maxV of the triangle each direction, and test for overlap in
313  */
314  /* that direction -- this is equivalent to testing a minimal AABB around */
315  /* the triangle against the AABB */
316 
317  /* test in 0-direction */
318  FINDMINMAX(v0[0], v1[0], v2[0], minV, maxV);
319  if (minV > boxhalfSize.x || maxV < -boxhalfSize.x) return false;
320 
321  /* test in 1-direction */
322  FINDMINMAX(v0[1], v1[1], v2[1], minV, maxV);
323  if (minV > boxhalfSize.y || maxV < -boxhalfSize.y) return false;
324 
325  /* test in 2-direction */
326  FINDMINMAX(v0[2], v1[2], v2[2], minV, maxV);
327  if (minV > boxhalfSize.z || maxV < -boxhalfSize.z) return false;
328 
329  /* Bullet 2: */
330  /* test if the box intersects the plane of the triangle */
331  /* compute plane equation of triangle: normal*0+d=0 */
332 
333  // double normal[3];
334  CCVector3d::vcross(e0, e1, /*normal*/ e2); // DGM: we use 'e2' instead of
335  // 'normal' to save heap memory
336  {
337  // double vmin[3],vmax[3]; //DGM: we use e0 and e1 instead of vmin and
338  // vmax
339  if (/*normal*/ e2[0] > 0) {
340  /*vmin*/ e0[0] = -boxhalfSize.x - v0[0];
341  /*vmax*/ e1[0] = boxhalfSize.x - v0[0];
342  } else {
343  /*vmin*/ e0[0] = boxhalfSize.x - v0[0];
344  /*vmax*/ e1[0] = -boxhalfSize.x - v0[0];
345  }
346  if (/*normal*/ e2[1] > 0) {
347  /*vmin*/ e0[1] = -boxhalfSize.y - v0[1];
348  /*vmax*/ e1[1] = boxhalfSize.y - v0[1];
349  } else {
350  /*vmin*/ e0[1] = boxhalfSize.y - v0[1];
351  /*vmax*/ e1[1] = -boxhalfSize.y - v0[1];
352  }
353  if (/*normal*/ e2[2] > 0) {
354  /*vmin*/ e0[2] = -boxhalfSize.z - v0[2];
355  /*vmax*/ e1[2] = boxhalfSize.z - v0[2];
356  } else {
357  /*vmin*/ e0[2] = boxhalfSize.z - v0[2];
358  /*vmax*/ e1[2] = -boxhalfSize.z - v0[2];
359  }
360 
361  if (CCVector3d::vdot(/*normal*/ e2, /*vmin*/ e0) > 0 ||
362  CCVector3d::vdot(/*normal*/ e2, /*vmax*/ e1) < 0) {
363  return false;
364  }
365  }
366 
367  return true; /* box and triangle overlaps */
368 }
369 
371  CCVector3& X,
372  CCVector3& Y) {
373  CCVector3 Nunit = N;
374  Nunit.normalize();
375 
376  // we create a first vector orthogonal to the input one
377  X = Nunit.orthogonal(); // X is also normalized
378 
379  // we deduce the orthogonal vector to the input one and X
380  Y = N.cross(X);
381  // Y.normalize(); //should already be normalized!
382 }
383 
385  CCVector3d& X,
386  CCVector3d& Y) {
387  CCVector3d Nunit = N;
388  Nunit.normalize();
389 
390  // we create a first vector orthogonal to the input one
391  X = Nunit.orthogonal(); // X is also normalized
392 
393  // we deduce the orthogonal vector to the input one and X
394  Y = N.cross(X);
395  // Y.normalize(); //should already be normalized!
396 }
#define AXISTEST_Y02(a, b, fa, fb)
Definition: CVMiscTools.cpp:57
#define AXISTEST_Z12(a, b, fa, fb)
Definition: CVMiscTools.cpp:71
#define AXISTEST_Y1(a, b, fa, fb)
Definition: CVMiscTools.cpp:63
#define AXISTEST_X2(a, b, fa, fb)
Definition: CVMiscTools.cpp:49
#define AXISTEST_X01(a, b, fa, fb)
Definition: CVMiscTools.cpp:43
#define FINDMINMAX(x0, x1, x2, minV, maxV)
Definition: CVMiscTools.cpp:31
#define AXISTEST_Z0(a, b, fa, fb)
Definition: CVMiscTools.cpp:77
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
void * X
Definition: SmallVector.cpp:45
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
Vector3Tpl orthogonal() const
Returns a normalized vector which is orthogonal to this one.
Definition: CVGeom.h:433
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
static void vsubstract(const PointCoordinateType p[], const PointCoordinateType q[], PointCoordinateType r[])
Definition: CVGeom.h:550
static PointCoordinateType vdot(const PointCoordinateType p[], const PointCoordinateType q[])
Definition: CVGeom.h:520
static void vcross(const PointCoordinateType p[], const PointCoordinateType q[], PointCoordinateType r[])
Definition: CVGeom.h:528
static void ComputeBaseVectors(const CCVector3 &N, CCVector3 &X, CCVector3 &Y)
Computes base vectors for a given 3D plane.
static bool TriBoxOverlap(const CCVector3 &boxcenter, const CCVector3 &boxhalfsize, const CCVector3 *triverts[3])
Ovelap test between a 3D box and a triangle.
static void EnlargeBox(CCVector3 &dimMin, CCVector3 &dimMax, double coef)
Proportionally enlarges a 3D box.
static bool TriBoxOverlapd(const CCVector3d &boxcenter, const CCVector3d &boxhalfsize, const CCVector3d triverts[3])
Ovelap test between a 3D box and a triangle (double version)
static void MakeMinAndMaxCubical(CCVector3 &dimMin, CCVector3 &dimMax, double enlargeFactor=0.01)
Transforms a 3D box into a 3D cube.
Definition: CVMiscTools.cpp:88
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
Generic file read and write utility for python interface.