ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PointProjectionTools.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 <PointProjectionTools.h>
9 
10 // local
11 #include <CVMath.h>
12 #include <CVPointCloud.h>
13 #include <Delaunay2dMesh.h>
16 #include <Neighbourhood.h>
17 #include <ParallelSort.h>
18 #include <SimpleMesh.h>
19 
20 // system
21 #include <set>
22 
23 using namespace cloudViewer;
24 
26  GenericCloud* cloud,
27  PointCoordinateType radius,
28  unsigned char dim,
29  CCVector3* center,
30  GenericProgressCallback* progressCb) {
31  if (!cloud) return nullptr;
32 
33  unsigned char dim1 = (dim > 0 ? dim - 1 : 2);
34  unsigned char dim2 = (dim < 2 ? dim + 1 : 0);
35 
36  unsigned count = cloud->size();
37 
38  PointCloud* newCloud = new PointCloud();
39  if (!newCloud->reserve(count)) // not enough memory
40  return nullptr;
41 
42  // we compute cloud bounding box center if no center is specified
43  CCVector3 C;
44  if (!center) {
45  CCVector3 bbMin, bbMax;
46  cloud->getBoundingBox(bbMin, bbMax);
47  C = (bbMin + bbMax) / 2;
48  center = &C;
49  }
50 
51  NormalizedProgress nprogress(progressCb, count);
52  if (progressCb) {
53  if (progressCb->textCanBeEdited()) {
54  progressCb->setMethodTitle("Develop");
55  char buffer[32];
56  snprintf(buffer, 32, "Number of points = %u", count);
57  progressCb->setInfo(buffer);
58  }
59  progressCb->update(0);
60  progressCb->start();
61  }
62 
63  const CCVector3* Q = nullptr;
64  cloud->placeIteratorAtBeginning();
65  while ((Q = cloud->getNextPoint())) {
66  CCVector3 P = *Q - *center;
68  sqrt(P.u[dim1] * P.u[dim1] + P.u[dim2] * P.u[dim2]);
69  PointCoordinateType lon = atan2(P.u[dim1], P.u[dim2]);
70 
71  newCloud->addPoint(CCVector3(lon * radius, P.u[dim], u - radius));
72 
73  if (progressCb && !nprogress.oneStep()) {
74  break;
75  }
76  }
77 
78  if (progressCb) {
79  progressCb->stop();
80  }
81 
82  return newCloud;
83 }
84 
85 // deroule la liste sur un cone dont le centre est "center" et d'angle alpha en
86 // degres
88  GenericCloud* cloud,
89  unsigned char dim,
90  PointCoordinateType baseRadius,
91  float alpha,
92  const CCVector3& center,
93  GenericProgressCallback* progressCb) {
94  if (!cloud) return nullptr;
95 
96  unsigned count = cloud->size();
97 
98  PointCloud* outCloud = new PointCloud();
99  if (!outCloud->reserve(count)) // not enough memory
100  return nullptr;
101 
102  unsigned char dim1 = (dim > 0 ? dim - 1 : 2);
103  unsigned char dim2 = (dim < 2 ? dim + 1 : 0);
104 
105  float tan_alpha = tanf(cloudViewer::DegreesToRadians(alpha));
106  // float cos_alpha = cos(cloudViewer::DegreesToRadians(alpha));
107  // float sin_alpha = sin(cloudViewer::DegreesToRadians(alpha));
108  float q = 1.0f / (1.0f + tan_alpha * tan_alpha);
109 
110  cloud->placeIteratorAtBeginning();
111 
112  NormalizedProgress nprogress(progressCb, count);
113  if (progressCb) {
114  if (progressCb->textCanBeEdited()) {
115  progressCb->setMethodTitle("DevelopOnCone");
116  char buffer[32];
117  snprintf(buffer, 32, "Number of points = %u", count);
118  progressCb->setInfo(buffer);
119  }
120  progressCb->update(0);
121  progressCb->start();
122  }
123 
124  for (unsigned i = 0; i < count; i++) {
125  const CCVector3* Q = cloud->getNextPoint();
126  CCVector3 P = *Q - center;
127 
129  sqrt(P.u[dim1] * P.u[dim1] + P.u[dim2] * P.u[dim2]);
130  PointCoordinateType lon = atan2(P.u[dim1], P.u[dim2]);
131 
132  // projection sur le cone
133  PointCoordinateType z2 = (P.u[dim] + u * tan_alpha) * q;
134  PointCoordinateType x2 = z2 * tan_alpha;
135 // ordonnee
136 // #define ORTHO_CONIC_PROJECTION
137 #ifdef ORTHO_CONIC_PROJECTION
138  PointCoordinateType lat = sqrt(x2 * x2 + z2 * z2) * cos_alpha;
139  if (lat * z2 < 0.0) lat = -lat;
140 #else
141  PointCoordinateType lat = P.u[dim];
142 #endif
143  // altitude
144  PointCoordinateType dX = u - x2;
145  PointCoordinateType dZ = P.u[dim] - z2;
146  PointCoordinateType alt = sqrt(dX * dX + dZ * dZ);
147  // on regarde de quel cote de la surface du cone le resultat tombe par
148  // p.v.
149  if (x2 * P.u[dim] - z2 * u < 0) alt = -alt;
150 
151  outCloud->addPoint(CCVector3(lon * baseRadius, lat + center[dim], alt));
152 
153  if (progressCb && !nprogress.oneStep()) {
154  break;
155  }
156  }
157 
158  if (progressCb) {
159  progressCb->stop();
160  }
161 
162  return outCloud;
163 }
164 
166  GenericCloud* cloud,
167  Transformation& trans,
168  GenericProgressCallback* progressCb) {
169  assert(cloud);
170 
171  unsigned count = cloud->size();
172 
173  PointCloud* transformedCloud = new PointCloud();
174  if (!transformedCloud->reserve(count)) return nullptr; // not enough memory
175 
176  NormalizedProgress nprogress(progressCb, count);
177  if (progressCb) {
178  if (progressCb->textCanBeEdited()) {
179  progressCb->setMethodTitle("ApplyTransformation");
180  char buffer[32];
181  snprintf(buffer, 32, "Number of points = %u", count);
182  progressCb->setInfo(buffer);
183  }
184  progressCb->update(0);
185  progressCb->start();
186  }
187 
188  cloud->placeIteratorAtBeginning();
189  const CCVector3* P = cloud->getNextPoint();
190  while (P) {
191  // P' = s*R.P+T
192  CCVector3 newP = trans.apply(*P);
193 
194  transformedCloud->addPoint(newP);
195 
196  if (progressCb && !nprogress.oneStep()) {
197  break;
198  }
199 
200  P = cloud->getNextPoint();
201  }
202 
203  if (progressCb) {
204  progressCb->stop();
205  }
206 
207  return transformedCloud;
208 }
209 
211  GenericIndexedCloud* cloud,
212  Transformation& trans,
213  GenericProgressCallback* progressCb) {
214  assert(cloud);
215 
216  unsigned count = cloud->size();
217 
218  PointCloud* transformedCloud = new PointCloud();
219  if (!transformedCloud->reserve(count)) {
220  // not enough memory
221  delete transformedCloud;
222  return nullptr;
223  }
224 
225  bool withNormals = cloud->normalsAvailable();
226  if (withNormals) {
227  if (!transformedCloud->reserveNormals(count)) {
228  // not enough memory
229  delete transformedCloud;
230  return nullptr;
231  }
232  }
233 
234  NormalizedProgress nprogress(progressCb, count);
235  if (progressCb) {
236  if (progressCb->textCanBeEdited()) {
237  progressCb->setMethodTitle("ApplyTransformation");
238  char buffer[32];
239  snprintf(buffer, 32, "Number of points = %u", count);
240  progressCb->setInfo(buffer);
241  }
242  progressCb->update(0);
243  progressCb->start();
244  }
245 
246  cloud->placeIteratorAtBeginning();
247 
248  for (unsigned i = 0; i < count; ++i) {
249  const CCVector3* P = cloud->getPoint(i);
250 
251  // P' = s*R.P+T
252  CCVector3 newP = trans.apply(*P);
253  transformedCloud->addPoint(newP);
254 
255  if (withNormals) {
256  const CCVector3* N = cloud->getNormal(i);
257 
258  // N' = R.N
259  CCVector3 newN = (trans.R * (*N)).toPC();
260  transformedCloud->addNormal(newN);
261  }
262 
263  if (progressCb && !nprogress.oneStep()) {
264  break;
265  }
266  }
267 
268  if (progressCb) {
269  progressCb->stop();
270  }
271 
272  return transformedCloud;
273 }
274 
277  TRIANGULATION_TYPES type /*=DELAUNAY_2D_AXIS_ALIGNED*/,
278  PointCoordinateType maxEdgeLength /*=0*/,
279  unsigned char dim /*=0*/,
280  std::string& outputErrorStr) {
281  if (!cloud) {
282  outputErrorStr = "Invalid input cloud";
283  return nullptr;
284  }
285 
286  switch (type) {
288  if (dim > 2) {
289  outputErrorStr = "Invalid projection dimension";
290  return nullptr;
291  }
292  const unsigned char Z = static_cast<unsigned char>(dim);
293  const unsigned char X = Z == 2 ? 0 : Z + 1;
294  const unsigned char Y = X == 2 ? 0 : X + 1;
295 
296  unsigned count = cloud->size();
297  std::vector<CCVector2> the2DPoints;
298  try {
299  the2DPoints.resize(count);
300  } catch (... /*const std::bad_alloc&*/) // out of memory
301  {
302  outputErrorStr = "Not enough memory";
303  break;
304  }
305 
306  cloud->placeIteratorAtBeginning();
307  for (unsigned i = 0; i < count; ++i) {
308  const CCVector3* P = cloud->getPoint(i);
309  the2DPoints[i].x = P->u[X];
310  the2DPoints[i].y = P->u[Y];
311  }
312 
313  Delaunay2dMesh* dm = new Delaunay2dMesh();
314  if (!dm->buildMesh(the2DPoints, Delaunay2dMesh::USE_ALL_POINTS,
315  outputErrorStr)) {
316  delete dm;
317  return nullptr;
318  }
319  dm->linkMeshWith(cloud, false);
320 
321  // remove triangles with too long edges
322  if (maxEdgeLength > 0) {
323  dm->removeTrianglesWithEdgesLongerThan(maxEdgeLength);
324  if (dm->size() == 0) {
325  // no more triangles?
326  outputErrorStr = "No triangle left after pruning";
327  delete dm;
328  return nullptr;
329  }
330  }
331 
332  return static_cast<GenericIndexedMesh*>(dm);
333  } break;
335  Neighbourhood Yk(cloud);
338  outputErrorStr);
339  return mesh;
340  } break;
341  default:
342  // shouldn't happen
343  assert(false);
344  break;
345  }
346 
347  return nullptr;
348 }
349 
350 // Lexicographic sorting operator
351 inline bool LexicographicSort(const CCVector2& a, const CCVector2& b) {
352  return a.x < b.x || (a.x == b.x && a.y < b.y);
353 }
354 
356  std::vector<IndexedCCVector2>& points,
357  std::list<IndexedCCVector2*>& hullPoints) {
358  std::size_t n = points.size();
359 
360  // Sort points lexicographically
361  ParallelSort(points.begin(), points.end(), LexicographicSort);
362 
363  // Build lower hull
364  {
365  for (std::size_t i = 0; i < n; i++) {
366  while (hullPoints.size() >= 2) {
367  std::list<IndexedCCVector2*>::iterator itB = hullPoints.end();
368  --itB;
369  std::list<IndexedCCVector2*>::iterator itA = itB;
370  --itA;
371  if ((**itB - **itA).cross(points[i] - **itA) <= 0) {
372  hullPoints.pop_back();
373  } else {
374  break;
375  }
376  }
377 
378  try {
379  hullPoints.push_back(&points[i]);
380  } catch (const std::bad_alloc&) {
381  // not enough memory
382  return false;
383  }
384  }
385  }
386 
387  // Build upper hull
388  {
389  std::size_t t = hullPoints.size() + 1;
390  for (int i = static_cast<int>(n) - 2; i >= 0; i--) {
391  while (hullPoints.size() >= t) {
392  std::list<IndexedCCVector2*>::iterator itB = hullPoints.end();
393  --itB;
394  std::list<IndexedCCVector2*>::iterator itA = itB;
395  --itA;
396  if ((**itB - **itA).cross(points[i] - **itA) <= 0) {
397  hullPoints.pop_back();
398  } else {
399  break;
400  }
401  }
402 
403  try {
404  hullPoints.push_back(&points[i]);
405  } catch (const std::bad_alloc&) {
406  // not enough memory
407  return false;
408  }
409  }
410  }
411 
412  // remove last point if it's the same as the first one
413  if (hullPoints.size() > 1 &&
414  hullPoints.front()->x == hullPoints.back()->x &&
415  hullPoints.front()->y == hullPoints.back()->y) {
416  hullPoints.pop_back();
417  }
418 
419  return true;
420 }
421 
423  const CCVector2& B,
424  const CCVector2& C,
425  const CCVector2& D) {
426  CCVector2 AB = B - A;
427  CCVector2 AC = C - A;
428  CCVector2 AD = D - A;
429  PointCoordinateType cross_AB_AC = AB.cross(AC);
430  PointCoordinateType cross_AB_AD = AB.cross(AD);
431 
432  // both C and D are on the same side of AB?
433  if (cross_AB_AC * cross_AB_AD > 0) {
434  // no intersection
435  return false;
436  }
437 
438  CCVector2 CD = D - C;
439  CCVector2 CB = B - C;
440  PointCoordinateType cross_CD_CA = -CD.cross(AC);
441  PointCoordinateType cross_CD_CB = CD.cross(CB);
442 
443  // both A and B are on the same side of CD?
444  if (cross_CD_CA * cross_CD_CB > 0) {
445  // no intersection
446  return false;
447  }
448 
449  PointCoordinateType cross_AB_CD = AB.cross(CD);
450  if (std::abs(cross_AB_CD) != 0) // AB and CD are not parallel
451  {
452  // where do they intersect?
453  // PointCoordinateType v = cross_AB_AC/cross_AB_CD;
454  // assert(v >= 0 && v <= 1);
455 
456  return true;
457  } else // AB and CD are parallel (therefore they are colinear - see above
458  // tests)
459  {
460  PointCoordinateType dAB = AB.norm();
461 
462  PointCoordinateType dot_AB_AC = AB.dot(AC);
463  if (dot_AB_AC >= 0 && dot_AB_AC < dAB * AC.norm()) {
464  // C is between A and B
465  return true;
466  }
467 
468  PointCoordinateType dot_AB_AD = AB.dot(AD);
469  if (dot_AB_AD >= 0 && dot_AB_AD < dAB * AD.norm()) {
470  // D is between A and B
471  return true;
472  }
473 
474  // otherwise there's an intersection only if B and C are on both sides!
475  return (dot_AB_AC * dot_AB_AD < 0);
476  }
477 }
478 
479 // list of already used point to avoid hull's inner loops
485 };
486 
488 using VertexIterator = std::list<Vertex2D*>::iterator;
489 using ConstVertexIterator = std::list<Vertex2D*>::const_iterator;
490 
491 struct Edge {
492  Edge() : nearestPointIndex(0), nearestPointSquareDist(-1.0f) {}
493 
495  unsigned _nearestPointIndex,
496  float _nearestPointSquareDist)
497  : itA(A),
498  nearestPointIndex(_nearestPointIndex),
499  nearestPointSquareDist(_nearestPointSquareDist) {}
500 
501  // operator
502  inline bool operator<(const Edge& e) const {
503  return nearestPointSquareDist < e.nearestPointSquareDist;
504  }
505 
509 };
510 
512 
515  unsigned& minIndex,
516  const VertexIterator& itA,
517  const VertexIterator& itB,
518  const std::vector<Vertex2D>& points,
519  const std::vector<HullPointFlags>& pointFlags,
520  PointCoordinateType minSquareEdgeLength,
521  PointCoordinateType maxSquareEdgeLength,
522  bool allowLongerChunks = false) {
523  // look for the nearest point in the input set
524  PointCoordinateType minDist2 = -1;
525  CCVector2 AB = **itB - **itA;
526  PointCoordinateType squareLengthAB = AB.norm2();
527  unsigned pointCount = static_cast<unsigned>(points.size());
528  for (unsigned i = 0; i < pointCount; ++i) {
529  const Vertex2D& P = points[i];
530  if (pointFlags[P.index] != POINT_NOT_USED) continue;
531 
532  // skip the edge vertices!
533  if (P.index == (*itA)->index || P.index == (*itB)->index) continue;
534 
535  // we only consider 'inner' points
536  CCVector2 AP = P - **itA;
537  if (AB.x * AP.y - AB.y * AP.x < 0) {
538  continue;
539  }
540 
541  PointCoordinateType dot = AB.dot(AP); // = cos(PAB) * ||AP|| * ||AB||
542  if (dot >= 0 && dot <= squareLengthAB) {
543  CCVector2 HP = AP - AB * (dot / squareLengthAB);
544  PointCoordinateType dist2 = HP.norm2();
545  if (minDist2 < 0 || dist2 < minDist2) {
546  // the 'nearest' point must also be a valid candidate
547  //(i.e. at least one of the created edges is smaller than the
548  // original one and we don't create too small edges!)
549  PointCoordinateType squareLengthAP = AP.norm2();
550  PointCoordinateType squareLengthBP = (P - **itB).norm2();
551  if (squareLengthAP >= minSquareEdgeLength &&
552  squareLengthBP >= minSquareEdgeLength &&
553  (allowLongerChunks || (squareLengthAP < squareLengthAB ||
554  squareLengthBP < squareLengthAB))) {
555  minDist2 = dist2;
556  minIndex = i;
557  }
558  }
559  }
560  }
561  return (minDist2 < 0 ? minDist2 : minDist2 / squareLengthAB);
562 }
563 
565  std::vector<IndexedCCVector2>& points,
566  std::list<IndexedCCVector2*>& hullPoints,
567  PointCoordinateType maxSquareEdgeLength /*=0*/) {
568  // first compute the Convex hull
569  if (!extractConvexHull2D(points, hullPoints)) return false;
570 
571  // do we really need to compute the concave hull?
572  if (hullPoints.size() < 2 || maxSquareEdgeLength <= 0) return true;
573 
574  unsigned pointCount = static_cast<unsigned>(points.size());
575 
576  std::vector<HullPointFlags> pointFlags;
577  try {
578  pointFlags.resize(pointCount, POINT_NOT_USED);
579  } catch (...) {
580  // not enough memory
581  return false;
582  }
583 
584  // hack: compute the theoretical 'minimal' edge length
585  PointCoordinateType minSquareEdgeLength = 0;
586  {
587  CCVector2 minP, maxP;
588  for (std::size_t i = 0; i < pointCount; ++i) {
589  const IndexedCCVector2& P = points[i];
590  if (i) {
591  minP.x = std::min(P.x, minP.x);
592  minP.y = std::min(P.y, minP.y);
593  maxP.x = std::max(P.x, maxP.x);
594  maxP.y = std::max(P.y, maxP.y);
595  } else {
596  minP = maxP = P;
597  }
598  }
599  minSquareEdgeLength =
600  (maxP - minP).norm2() /
601  static_cast<PointCoordinateType>(
602  1.0e7); // 10^-7 of the max bounding rectangle side
603  minSquareEdgeLength =
604  std::min(minSquareEdgeLength, maxSquareEdgeLength / 10);
605 
606  // we remove very small edges
607  for (std::list<IndexedCCVector2*>::iterator itA = hullPoints.begin();
608  itA != hullPoints.end(); ++itA) {
609  std::list<IndexedCCVector2*>::iterator itB = itA;
610  ++itB;
611  if (itB == hullPoints.end()) itB = hullPoints.begin();
612  if ((**itB - **itA).norm2() < minSquareEdgeLength) {
613  pointFlags[(*itB)->index] = POINT_FROZEN;
614  hullPoints.erase(itB);
615  }
616  }
617 
618  if (hullPoints.size() < 2) {
619  // no more edges?!
620  return false;
621  }
622  }
623 
624  // we repeat the process until nothing changes!
625  // Warning: high STL containers usage ahead ;)
626  unsigned step = 0;
627  bool somethingHasChanged = true;
628  while (somethingHasChanged) {
629  try {
630  somethingHasChanged = false;
631  ++step;
632 
634  // for (std::size_t i=0; i<pointCount; ++i)
635  //{
636  // if (pointFlags[i] != POINT_FROZEN)
637  // pointFlags[i] = POINT_NOT_USED;
638  // }
639 
640  // build the initial edge list & flag the convex hull points
641  std::multiset<Edge> edges;
642  {
643  for (std::list<IndexedCCVector2*>::iterator itA =
644  hullPoints.begin();
645  itA != hullPoints.end(); ++itA) {
646  std::list<IndexedCCVector2*>::iterator itB = itA;
647  ++itB;
648  if (itB == hullPoints.end()) itB = hullPoints.begin();
649 
650  // we will only process the edges that are longer than the
651  // maximum specified length
652  if ((**itB - **itA).norm2() > maxSquareEdgeLength) {
653  unsigned nearestPointIndex = 0;
654  PointCoordinateType minSquareDist =
656  nearestPointIndex, itA, itB, points,
657  pointFlags, minSquareEdgeLength,
658  maxSquareEdgeLength, step > 1);
659 
660  if (minSquareDist >= 0) {
661  Edge e(itA, nearestPointIndex, minSquareDist);
662  edges.insert(e);
663  }
664  }
665 
666  pointFlags[(*itA)->index] = POINT_USED;
667  }
668  }
669 
670  while (!edges.empty()) {
671  // current edge (AB)
672  // this should be the edge with the nearest 'candidate'
673  Edge e = *edges.begin();
674  edges.erase(edges.begin());
675 
676  VertexIterator itA = e.itA;
677  VertexIterator itB = itA;
678  ++itB;
679  if (itB == hullPoints.end()) itB = hullPoints.begin();
680 
681  // nearest point
682  const Vertex2D& P = points[e.nearestPointIndex];
683  if (pointFlags[P.index] != POINT_NOT_USED) {
684  // assert(false); //DGM: in fact it happens!
685  break;
686  }
687 
688  // check that we don't create too small edges!
689  // CCVector2 AP = (P-**itA);
690  // CCVector2 PB = (**itB-P);
691  // PointCoordinateType squareLengthAP = (P-**itA).norm2();
692  // PointCoordinateType squareLengthPB = (**itB-P).norm2();
695  // assert( squareLengthAP < e.squareLength || squareLengthPB <
696  // e.squareLength ); if (squareLengthAP < minSquareEdgeLength ||
697  // squareLengthPB < minSquareEdgeLength)
698  //{
699  // pointFlags[P.index] = POINT_IGNORED;
700  // edges.push(e); //retest the edge!
701  // }
702 
703  // last check: the new segments must not intersect with the
704  // actual hull!
705  bool intersect = false;
706  // if (false)
707  {
708  for (VertexIterator itJ = hullPoints.begin(), itI = itJ++;
709  itI != hullPoints.end(); ++itI, ++itJ) {
710  if (itJ == hullPoints.end()) itJ = hullPoints.begin();
711 
712  if (((*itI)->index != (*itA)->index &&
713  (*itJ)->index != (*itA)->index &&
715  segmentIntersect(**itI, **itJ, **itA,
716  P)) ||
717  ((*itI)->index != (*itB)->index &&
718  (*itJ)->index != (*itB)->index &&
720  segmentIntersect(**itI, **itJ, P,
721  **itB))) {
722  intersect = true;
723  break;
724  }
725  }
726  }
727 
728  if (!intersect) {
729  // add point to concave hull
730  VertexIterator itP = hullPoints.insert(
731  itB == hullPoints.begin() ? hullPoints.end() : itB,
733 
734  // we won't use P anymore!
735  pointFlags[P.index] = POINT_USED;
736 
737  somethingHasChanged = true;
738 
739  // update all edges that were having 'P' as their nearest
740  // candidate as well
741  if (!edges.empty()) {
742  std::vector<VertexIterator> removed;
743  std::multiset<Edge>::const_iterator lastValidIt =
744  edges.end();
745  for (std::multiset<Edge>::const_iterator it =
746  edges.begin();
747  it != edges.end(); ++it) {
748  if ((*it).nearestPointIndex ==
749  e.nearestPointIndex) {
750  // we'll have to put them back afterwards!
751  removed.push_back((*it).itA);
752 
753  edges.erase(it);
754  if (edges.empty()) break;
755  if (lastValidIt != edges.end())
756  it = lastValidIt;
757  else
758  it = edges.begin();
759  } else {
760  lastValidIt = it;
761  }
762  }
763 
764  // update the removed edges info and put them back in
765  // the main list
766  for (std::size_t i = 0; i < removed.size(); ++i) {
767  VertexIterator itC = removed[i];
768  VertexIterator itD = itC;
769  ++itD;
770  if (itD == hullPoints.end())
771  itD = hullPoints.begin();
772 
773  unsigned nearestPointIndex = 0;
774  PointCoordinateType minSquareDist =
776  nearestPointIndex, itC, itD, points,
777  pointFlags, minSquareEdgeLength,
778  maxSquareEdgeLength);
779 
780  if (minSquareDist >= 0) {
781  Edge e(itC, nearestPointIndex, minSquareDist);
782  edges.insert(e);
783  }
784  }
785  }
786 
787  // we'll inspect the two new segments later (if necessary)
788  if ((P - **itA).norm2() > maxSquareEdgeLength) {
789  unsigned nearestPointIndex = 0;
790  PointCoordinateType minSquareDist =
791  FindNearestCandidate(nearestPointIndex, itA,
792  itP, points, pointFlags,
793  minSquareEdgeLength,
794  maxSquareEdgeLength);
795 
796  if (minSquareDist >= 0) {
797  Edge e(itA, nearestPointIndex, minSquareDist);
798  edges.insert(e);
799  }
800  }
801 
802  if ((**itB - P).norm2() > maxSquareEdgeLength) {
803  unsigned nearestPointIndex = 0;
804  PointCoordinateType minSquareDist =
805  FindNearestCandidate(nearestPointIndex, itP,
806  itB, points, pointFlags,
807  minSquareEdgeLength,
808  maxSquareEdgeLength);
809 
810  if (minSquareDist >= 0) {
811  Edge e(itP, nearestPointIndex, minSquareDist);
812  edges.insert(e);
813  }
814  }
815  }
816  }
817  } catch (...) {
818  // not enough memory
819  return false;
820  }
821  }
822 
823  return true;
824 }
825 
827  GenericIndexedCloudPersist& cloud) const {
828  for (unsigned i = 0; i < cloud.size(); ++i) {
829  CCVector3* P = const_cast<CCVector3*>(cloud.getPoint(i));
830  *P = apply(*P);
831  }
832 
833  if (cloud.normalsAvailable()) {
834  for (unsigned i = 0; i < cloud.size(); ++i) {
835  CCVector3* N = const_cast<CCVector3*>(cloud.getNormal(i));
836  *N = (R * (*N)).toPC();
837  }
838  }
839 }
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int count
int points
char type
#define ParallelSort
Definition: ParallelSort.h:33
void * X
Definition: SmallVector.cpp:45
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type dot(const Vector2Tpl &v) const
Dot product.
Definition: CVGeom.h:71
Type x
Definition: CVGeom.h:36
Type norm() const
Returns vector norm.
Definition: CVGeom.h:63
Type cross(const Vector2Tpl &v) const
Cross product.
Definition: CVGeom.h:76
Type norm2() const
Returns vector square norm.
Definition: CVGeom.h:61
Type y
Definition: CVGeom.h:36
A class to compute and handle a Delaunay 2D mesh on a subset of points.
virtual unsigned size() const override
Returns the number of triangles.
bool removeTrianglesWithEdgesLongerThan(PointCoordinateType maxEdgeLength)
Filters out the triangles based on their edge length.
virtual bool buildMesh(const std::vector< CCVector2 > &points2D, std::size_t pointCountToUse, std::string &outputErrorStr)
Build the Delaunay mesh on top a set of 2D points.
virtual void linkMeshWith(GenericIndexedCloud *aCloud, bool passOwnership=false)
Associate this mesh to a point cloud.
static constexpr int USE_ALL_POINTS
virtual void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax)=0
Returns the cloud bounding box.
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based and presistent access to points.
A generic 3D point cloud with index-based point access.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
virtual bool normalsAvailable() const
Returns whether normals are available.
virtual const CCVector3 * getNormal(unsigned index) const
If per-point normals are available, returns the one at a specific index.
A generic mesh with index-based vertex access.
virtual void stop()=0
Notifies the fact that the process has ended.
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
virtual void update(float percent)=0
Notifies the algorithm progress.
static constexpr bool DO_NOT_DUPLICATE_VERTICES
Definition: Neighbourhood.h:30
GenericIndexedMesh * triangulateOnPlane(bool duplicateVertices, PointCoordinateType maxEdgeLength, std::string &outputErrorStr)
Applies 2D Delaunay triangulation.
bool oneStep()
Increments total progress value of a single unit.
virtual bool reserve(unsigned newCapacity)
Reserves memory for the point database.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
bool reserveNormals(unsigned newCount)
Reserves memory to store the normals.
Definition: CVPointCloud.h:28
void addNormal(const CCVector3 &N)
Adds a normal.
Definition: CVPointCloud.h:61
static bool extractConvexHull2D(std::vector< IndexedCCVector2 > &points, std::list< IndexedCCVector2 * > &hullPoints)
Determines the convex hull of a set of points.
static PointCloud * applyTransformation(GenericCloud *cloud, Transformation &trans, GenericProgressCallback *progressCb=nullptr)
Applys a geometrical transformation to a point cloud.
static bool segmentIntersect(const CCVector2 &A, const CCVector2 &B, const CCVector2 &C, const CCVector2 &D)
Returns true if the AB and CD segments intersect each other.
static PointCloud * developCloudOnCone(GenericCloud *cloud, unsigned char dim, PointCoordinateType baseRadius, float alpha, const CCVector3 &center, GenericProgressCallback *progressCb=nullptr)
Develops a cone-shaped point cloud around its main axis.
static PointCloud * developCloudOnCylinder(GenericCloud *cloud, PointCoordinateType radius, unsigned char dim=2, CCVector3 *center=nullptr, GenericProgressCallback *progressCb=nullptr)
Develops a cylinder-shaped point cloud around its main axis.
static GenericIndexedMesh * computeTriangulation(GenericIndexedCloudPersist *cloud, TRIANGULATION_TYPES type, PointCoordinateType maxEdgeLength, unsigned char dim, std::string &outputErrorStr)
Applys a geometrical transformation to a single point.
static bool extractConcaveHull2D(std::vector< IndexedCCVector2 > &points, std::list< IndexedCCVector2 * > &hullPoints, PointCoordinateType maxSquareLength=0)
Determines the 'concave' hull of a set of points.
@ POINT_NOT_USED
bool LexicographicSort(const CCVector2 &a, const CCVector2 &b)
std::list< Vertex2D * >::iterator VertexIterator
std::list< Vertex2D * >::const_iterator ConstVertexIterator
static PointCoordinateType FindNearestCandidate(unsigned &minIndex, const VertexIterator &itA, const VertexIterator &itB, const std::vector< Vertex2D > &points, const std::vector< HullPointFlags > &pointFlags, PointCoordinateType minSquareEdgeLength, PointCoordinateType maxSquareEdgeLength, bool allowLongerChunks=false)
Finds the nearest (available) point to an edge.
__host__ __device__ float3 cross(float3 a, float3 b)
Definition: cutil_math.h:1295
__host__ __device__ float dot(float2 a, float2 b)
Definition: cutil_math.h:1119
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
int max(int a, int b)
Definition: cutil_math.h:48
::ccPointCloud PointCloud
Definition: PointCloud.h:19
Generic file read and write utility for python interface.
float DegreesToRadians(int degrees)
Convert degrees to radians.
Definition: CVMath.h:98
TRIANGULATION_TYPES
Triangulation types.
unsigned nearestPointIndex
VertexIterator itA
float nearestPointSquareDist
bool operator<(const Edge &e) const
Edge(const VertexIterator &A, unsigned _nearestPointIndex, float _nearestPointSquareDist)
A scaled geometrical transformation (scale + rotation + translation)
CCVector3d apply(const CCVector3d &P) const
Applies the transformation to a point.