ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
SurfaceReconstructionBallPivoting.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 // CV_CORE_LIB
9 #include <IntersectionTest.h>
10 #include <Logging.h>
11 
12 // CV_DB_LIB
13 #include <Eigen/Dense>
14 #include <iostream>
15 #include <list>
16 #include <unordered_map>
17 #include <unordered_set>
18 
19 #include "ecvHObjectCaster.h"
20 #include "ecvKDTreeFlann.h"
21 #include "ecvMesh.h"
22 #include "ecvPointCloud.h"
23 
24 class BallPivotingVertex;
25 class BallPivotingEdge;
27 
29 typedef std::shared_ptr<BallPivotingEdge> BallPivotingEdgePtr;
30 typedef std::shared_ptr<BallPivotingTriangle> BallPivotingTrianglePtr;
31 
32 using namespace cloudViewer;
33 
35 public:
36  enum Type { Orphan = 0, Front = 1, Inner = 2 };
37 
39  const Eigen::Vector3d& point,
40  const Eigen::Vector3d& normal)
41  : idx_(idx), point_(point), normal_(normal), type_(Orphan) {}
42 
43  void UpdateType();
44 
45 public:
46  int idx_;
47  const Eigen::Vector3d& point_;
48  const Eigen::Vector3d& normal_;
49  std::unordered_set<BallPivotingEdgePtr> edges_;
51 };
52 
54 public:
55  enum Type { Border = 0, Front = 1, Inner = 2 };
56 
58  : source_(source), target_(target), type_(Type::Front) {}
59 
60  void AddAdjacentTriangle(BallPivotingTrianglePtr triangle);
61  BallPivotingVertexPtr GetOppositeVertex();
62 
63 public:
69 };
70 
72 public:
76  Eigen::Vector3d ball_center)
77  : vert0_(vert0),
78  vert1_(vert1),
79  vert2_(vert2),
80  ball_center_(ball_center) {}
81 
82 public:
86  Eigen::Vector3d ball_center_;
87 };
88 
90  if (edges_.empty()) {
91  type_ = Type::Orphan;
92  } else {
93  for (const BallPivotingEdgePtr& edge : edges_) {
94  if (edge->type_ != BallPivotingEdge::Type::Inner) {
95  type_ = Type::Front;
96  return;
97  }
98  }
99  type_ = Type::Inner;
100  }
101 }
102 
104  if (triangle != triangle0_ && triangle != triangle1_) {
105  if (triangle0_ == nullptr) {
106  triangle0_ = triangle;
107  type_ = Type::Front;
108  // update orientation
109  BallPivotingVertexPtr opp = GetOppositeVertex();
110  Eigen::Vector3d tr_norm =
111  (target_->point_ - source_->point_)
112  .cross(opp->point_ - source_->point_);
113  tr_norm /= tr_norm.norm();
114  Eigen::Vector3d pt_norm =
115  source_->normal_ + target_->normal_ + opp->normal_;
116  pt_norm /= pt_norm.norm();
117  if (pt_norm.dot(tr_norm) < 0) {
118  std::swap(target_, source_);
119  }
120  } else if (triangle1_ == nullptr) {
121  triangle1_ = triangle;
122  type_ = Type::Inner;
123  } else {
124  utility::LogDebug("!!! This case should not happen");
125  }
126  }
127 }
128 
130  if (triangle0_ != nullptr) {
131  if (triangle0_->vert0_->idx_ != source_->idx_ &&
132  triangle0_->vert0_->idx_ != target_->idx_) {
133  return triangle0_->vert0_;
134  } else if (triangle0_->vert1_->idx_ != source_->idx_ &&
135  triangle0_->vert1_->idx_ != target_->idx_) {
136  return triangle0_->vert1_;
137  } else {
138  return triangle0_->vert2_;
139  }
140  } else {
141  return nullptr;
142  }
143 }
144 
146 public:
148  : has_normals_(pcd.hasNormals()), kdtree_(pcd) {
149  ccPointCloud* baseVertices = new ccPointCloud("vertices");
150  assert(baseVertices);
151  baseVertices->setEnabled(false);
152  // DGM: no need to lock it as it is only used by one mesh!
153  baseVertices->setLocked(false);
154  mesh_ = std::make_shared<ccMesh>(baseVertices);
155  mesh_->addChild(baseVertices);
156  if (!baseVertices->reserveThePointsTable(pcd.size())) {
157  utility::LogError("[BallPivoting] not enough memory!");
158  }
159 
160  baseVertices->addPoints(pcd.getPoints());
161  if (pcd.hasNormals()) {
162  if (baseVertices->reserveTheNormsTable()) {
163  baseVertices->addNorms(pcd.getPointNormals());
164  }
165  }
166 
167  if (pcd.hasColors()) {
168  if (baseVertices->reserveTheRGBTable()) {
169  baseVertices->addRGBColors(pcd.getPointColors());
170  }
171  }
172 
173  for (size_t vidx = 0; vidx < pcd.size(); ++vidx) {
174  vertices.emplace_back(new BallPivotingVertex(
175  static_cast<int>(vidx), pcd.getEigenPoint(vidx),
176  pcd.getEigenNormal(vidx)));
177  }
178  }
179 
180  virtual ~BallPivoting() {
181  for (auto vert : vertices) {
182  delete vert;
183  }
184  }
185 
186  bool ComputeBallCenter(int vidx1,
187  int vidx2,
188  int vidx3,
189  double radius,
190  Eigen::Vector3d& center) {
191  const Eigen::Vector3d& v1 = vertices[vidx1]->point_;
192  const Eigen::Vector3d& v2 = vertices[vidx2]->point_;
193  const Eigen::Vector3d& v3 = vertices[vidx3]->point_;
194  double c = (v2 - v1).squaredNorm();
195  double b = (v1 - v3).squaredNorm();
196  double a = (v3 - v2).squaredNorm();
197 
198  double alpha = a * (b + c - a);
199  double beta = b * (a + c - b);
200  double gamma = c * (a + b - c);
201  double abg = alpha + beta + gamma;
202 
203  if (abg < 1e-16) {
204  return false;
205  }
206 
207  alpha = alpha / abg;
208  beta = beta / abg;
209  gamma = gamma / abg;
210 
211  Eigen::Vector3d circ_center = alpha * v1 + beta * v2 + gamma * v3;
212  double circ_radius2 = a * b * c;
213 
214  a = std::sqrt(a);
215  b = std::sqrt(b);
216  c = std::sqrt(c);
217  circ_radius2 = circ_radius2 /
218  ((a + b + c) * (b + c - a) * (c + a - b) * (a + b - c));
219 
220  double height = radius * radius - circ_radius2;
221  if (height >= 0.0) {
222  Eigen::Vector3d tr_norm = (v2 - v1).cross(v3 - v1);
223  tr_norm /= tr_norm.norm();
224  Eigen::Vector3d pt_norm = vertices[vidx1]->normal_ +
225  vertices[vidx2]->normal_ +
226  vertices[vidx3]->normal_;
227  pt_norm /= pt_norm.norm();
228  if (tr_norm.dot(pt_norm) < 0) {
229  tr_norm *= -1;
230  }
231 
232  height = sqrt(height);
233  center = circ_center + height * tr_norm;
234  return true;
235  }
236  return false;
237  }
238 
240  const BallPivotingVertexPtr& v1) {
241  for (BallPivotingEdgePtr edge0 : v0->edges_) {
242  for (BallPivotingEdgePtr edge1 : v1->edges_) {
243  if (edge0->source_->idx_ == edge1->source_->idx_ &&
244  edge0->target_->idx_ == edge1->target_->idx_) {
245  return edge0;
246  }
247  }
248  }
249  return nullptr;
250  }
251 
253  const BallPivotingVertexPtr& v1,
254  const BallPivotingVertexPtr& v2,
255  const Eigen::Vector3d& center) {
257  "[CreateTriangle] with v0.idx={}, v1.idx={}, v2.idx={}",
258  v0->idx_, v1->idx_, v2->idx_);
259  BallPivotingTrianglePtr triangle =
260  std::make_shared<BallPivotingTriangle>(v0, v1, v2, center);
261 
262  BallPivotingEdgePtr e0 = GetLinkingEdge(v0, v1);
263  if (e0 == nullptr) {
264  e0 = std::make_shared<BallPivotingEdge>(v0, v1);
265  }
266  e0->AddAdjacentTriangle(triangle);
267  v0->edges_.insert(e0);
268  v1->edges_.insert(e0);
269 
270  BallPivotingEdgePtr e1 = GetLinkingEdge(v1, v2);
271  if (e1 == nullptr) {
272  e1 = std::make_shared<BallPivotingEdge>(v1, v2);
273  }
274  e1->AddAdjacentTriangle(triangle);
275  v1->edges_.insert(e1);
276  v2->edges_.insert(e1);
277 
278  BallPivotingEdgePtr e2 = GetLinkingEdge(v2, v0);
279  if (e2 == nullptr) {
280  e2 = std::make_shared<BallPivotingEdge>(v2, v0);
281  }
282  e2->AddAdjacentTriangle(triangle);
283  v2->edges_.insert(e2);
284  v0->edges_.insert(e2);
285 
286  v0->UpdateType();
287  v1->UpdateType();
288  v2->UpdateType();
289 
290  Eigen::Vector3d face_normal =
291  ComputeFaceNormal(v0->point_, v1->point_, v2->point_);
292  if (face_normal.dot(v0->normal_) > -1e-16) {
293  mesh_->addTriangle(v0->idx_, v1->idx_, v2->idx_);
294  } else {
295  mesh_->addTriangle(v0->idx_, v2->idx_, v1->idx_);
296  }
297  mesh_->addTriangleNorm(face_normal);
298  }
299 
300  Eigen::Vector3d ComputeFaceNormal(const Eigen::Vector3d& v0,
301  const Eigen::Vector3d& v1,
302  const Eigen::Vector3d& v2) {
303  Eigen::Vector3d normal = (v1 - v0).cross(v2 - v0);
304  double norm = normal.norm();
305  if (norm > 0) {
306  normal /= norm;
307  }
308  return normal;
309  }
310 
312  const BallPivotingVertexPtr& v1,
313  const BallPivotingVertexPtr& v2) {
314  utility::LogDebug("[IsCompatible] v0.idx={}, v1.idx={}, v2.idx={}",
315  v0->idx_, v1->idx_, v2->idx_);
316  Eigen::Vector3d normal =
317  ComputeFaceNormal(v0->point_, v1->point_, v2->point_);
318  if (normal.dot(v0->normal_) < -1e-16) {
319  normal *= -1;
320  }
321  bool ret = normal.dot(v0->normal_) > -1e-16 &&
322  normal.dot(v1->normal_) > -1e-16 &&
323  normal.dot(v2->normal_) > -1e-16;
324  utility::LogDebug("[IsCompatible] retuns = {}", ret);
325  return ret;
326  }
327 
329  const BallPivotingEdgePtr& edge,
330  double radius,
331  Eigen::Vector3d& candidate_center) {
332  utility::LogDebug("[FindCandidateVertex] edge=({}, {}), radius={}",
333  edge->source_->idx_, edge->target_->idx_, radius);
334  BallPivotingVertexPtr src = edge->source_;
335  BallPivotingVertexPtr tgt = edge->target_;
336 
337  const BallPivotingVertexPtr opp = edge->GetOppositeVertex();
338  if (opp == nullptr) {
339  utility::LogError("edge->GetOppositeVertex() returns nullptr.");
340  assert(opp == nullptr);
341  }
342  utility::LogDebug("[FindCandidateVertex] edge=({}, {}), opp={}",
343  src->idx_, tgt->idx_, opp->idx_);
344  utility::LogDebug("[FindCandidateVertex] src={} => {}", src->idx_,
345  src->point_.transpose());
346  utility::LogDebug("[FindCandidateVertex] tgt={} => {}", tgt->idx_,
347  tgt->point_.transpose());
348  utility::LogDebug("[FindCandidateVertex] src={} => {}", opp->idx_,
349  opp->point_.transpose());
350 
351  Eigen::Vector3d mp = 0.5 * (src->point_ + tgt->point_);
352  utility::LogDebug("[FindCandidateVertex] edge=({}, {}), mp={}",
353  edge->source_->idx_, edge->target_->idx_,
354  mp.transpose());
355 
356  BallPivotingTrianglePtr triangle = edge->triangle0_;
357  const Eigen::Vector3d& center = triangle->ball_center_;
358  utility::LogDebug("[FindCandidateVertex] edge=({}, {}), center={}",
359  edge->source_->idx_, edge->target_->idx_,
360  center.transpose());
361 
362  Eigen::Vector3d v = tgt->point_ - src->point_;
363  v /= v.norm();
364 
365  Eigen::Vector3d a = center - mp;
366  a /= a.norm();
367 
368  std::vector<int> indices;
369  std::vector<double> dists2;
370  kdtree_.SearchRadius(mp, 2 * radius, indices, dists2);
371  utility::LogDebug("[FindCandidateVertex] found {} potential candidates",
372  indices.size());
373 
374  BallPivotingVertexPtr min_candidate = nullptr;
375  double min_angle = 2 * M_PI;
376  for (auto nbidx : indices) {
377  utility::LogDebug("[FindCandidateVertex] nbidx {:d}", nbidx);
378  const BallPivotingVertexPtr& candidate = vertices[nbidx];
379  if (candidate->idx_ == src->idx_ || candidate->idx_ == tgt->idx_ ||
380  candidate->idx_ == opp->idx_) {
382  "[FindCandidateVertex] candidate {:d} is a triangle "
383  "vertex of the edge",
384  candidate->idx_);
385  continue;
386  }
387  utility::LogDebug("[FindCandidateVertex] candidate={:d} => {}",
388  candidate->idx_, candidate->point_.transpose());
389 
390  bool coplanar =
392  src->point_, tgt->point_, opp->point_,
393  candidate->point_);
394  if (coplanar &&
396  LineSegmentsMinimumDistance(
397  mp, candidate->point_, src->point_,
398  opp->point_) < 1e-12 ||
400  LineSegmentsMinimumDistance(
401  mp, candidate->point_, tgt->point_,
402  opp->point_) < 1e-12)) {
404  "[FindCandidateVertex] candidate {:d} is interesecting "
405  "the existing triangle",
406  candidate->idx_);
407  continue;
408  }
409 
410  Eigen::Vector3d new_center;
411  if (!ComputeBallCenter(src->idx_, tgt->idx_, candidate->idx_,
412  radius, new_center)) {
414  "[FindCandidateVertex] candidate {:d} can not compute "
415  "ball",
416  candidate->idx_);
417  continue;
418  }
419  utility::LogDebug("[FindCandidateVertex] candidate {:d} center={}",
420  candidate->idx_, new_center.transpose());
421 
422  Eigen::Vector3d b = new_center - mp;
423  b /= b.norm();
425  "[FindCandidateVertex] candidate {:d} v={}, a={}, b={}",
426  candidate->idx_, v.transpose(), a.transpose(),
427  b.transpose());
428 
429  double cosinus = a.dot(b);
430  cosinus = std::min(cosinus, 1.0);
431  cosinus = std::max(cosinus, -1.0);
433  "[FindCandidateVertex] candidate {:d} cosinus={:f}",
434  candidate->idx_, cosinus);
435 
436  double angle = std::acos(cosinus);
437 
438  Eigen::Vector3d c = a.cross(b);
439  if (c.dot(v) < 0) {
440  angle = 2 * M_PI - angle;
441  }
442 
443  if (angle >= min_angle) {
445  "[FindCandidateVertex] candidate {:d} angle {:f} > "
446  "min_angle {:f}",
447  candidate->idx_, angle, min_angle);
448  continue;
449  }
450 
451  bool empty_ball = true;
452  for (auto nbidx2 : indices) {
453  const BallPivotingVertexPtr& nb = vertices[nbidx2];
454  if (nb->idx_ == src->idx_ || nb->idx_ == tgt->idx_ ||
455  nb->idx_ == candidate->idx_) {
456  continue;
457  }
458  if ((new_center - nb->point_).norm() < radius - 1e-16) {
460  "[FindCandidateVertex] candidate {:d} not an empty "
461  "ball",
462  candidate->idx_);
463  empty_ball = false;
464  break;
465  }
466  }
467 
468  if (empty_ball) {
469  utility::LogDebug("[FindCandidateVertex] candidate {:d} works",
470  candidate->idx_);
471  min_angle = angle;
472  min_candidate = vertices[nbidx];
473  candidate_center = new_center;
474  }
475  }
476 
477  if (min_candidate == nullptr) {
478  utility::LogDebug("[FindCandidateVertex] returns nullptr");
479  } else {
480  utility::LogDebug("[FindCandidateVertex] returns {:d}",
481  min_candidate->idx_);
482  }
483  return min_candidate;
484  }
485 
486  void ExpandTriangulation(double radius) {
487  utility::LogDebug("[ExpandTriangulation] radius={}", radius);
488  while (!edge_front_.empty()) {
489  BallPivotingEdgePtr edge = edge_front_.front();
490  edge_front_.pop_front();
491  if (edge->type_ != BallPivotingEdge::Front) {
492  continue;
493  }
494 
495  Eigen::Vector3d center;
496  BallPivotingVertexPtr candidate =
497  FindCandidateVertex(edge, radius, center);
498  if (candidate == nullptr ||
499  candidate->type_ == BallPivotingVertex::Type::Inner ||
500  !IsCompatible(candidate, edge->source_, edge->target_)) {
501  edge->type_ = BallPivotingEdge::Type::Border;
502  border_edges_.push_back(edge);
503  continue;
504  }
505 
506  BallPivotingEdgePtr e0 = GetLinkingEdge(candidate, edge->source_);
507  BallPivotingEdgePtr e1 = GetLinkingEdge(candidate, edge->target_);
508  if ((e0 != nullptr && e0->type_ != BallPivotingEdge::Type::Front) ||
509  (e1 != nullptr && e1->type_ != BallPivotingEdge::Type::Front)) {
510  edge->type_ = BallPivotingEdge::Type::Border;
511  border_edges_.push_back(edge);
512  continue;
513  }
514 
515  CreateTriangle(edge->source_, edge->target_, candidate, center);
516 
517  e0 = GetLinkingEdge(candidate, edge->source_);
518  e1 = GetLinkingEdge(candidate, edge->target_);
519  if (e0->type_ == BallPivotingEdge::Type::Front) {
520  edge_front_.push_front(e0);
521  }
522  if (e1->type_ == BallPivotingEdge::Type::Front) {
523  edge_front_.push_front(e1);
524  }
525  }
526  }
527 
529  const BallPivotingVertexPtr& v1,
530  const BallPivotingVertexPtr& v2,
531  const std::vector<int>& nb_indices,
532  double radius,
533  Eigen::Vector3d& center) {
535  "[TryTriangleSeed] v0.idx={}, v1.idx={}, v2.idx={}, "
536  "radius={}",
537  v0->idx_, v1->idx_, v2->idx_, radius);
538 
539  if (!IsCompatible(v0, v1, v2)) {
540  return false;
541  }
542 
543  BallPivotingEdgePtr e0 = GetLinkingEdge(v0, v2);
544  BallPivotingEdgePtr e1 = GetLinkingEdge(v1, v2);
545  if (e0 != nullptr && e0->type_ == BallPivotingEdge::Type::Inner) {
547  "[TryTriangleSeed] returns {} because e0 is inner edge",
548  false);
549  return false;
550  }
551  if (e1 != nullptr && e1->type_ == BallPivotingEdge::Type::Inner) {
553  "[TryTriangleSeed] returns {} because e1 is inner edge",
554  false);
555  return false;
556  }
557 
558  if (!ComputeBallCenter(v0->idx_, v1->idx_, v2->idx_, radius, center)) {
560  "[TryTriangleSeed] returns {} could not compute ball "
561  "center",
562  false);
563  return false;
564  }
565 
566  // test if no other point is within the ball
567  for (const auto& nbidx : nb_indices) {
568  const BallPivotingVertexPtr& v = vertices[nbidx];
569  if (v->idx_ == v0->idx_ || v->idx_ == v1->idx_ ||
570  v->idx_ == v2->idx_) {
571  continue;
572  }
573  if ((center - v->point_).norm() < radius - 1e-16) {
575  "[TryTriangleSeed] returns {} computed ball is not "
576  "empty",
577  false);
578  return false;
579  }
580  }
581 
582  utility::LogDebug("[TryTriangleSeed] returns {}", true);
583  return true;
584  }
585 
586  bool TrySeed(BallPivotingVertexPtr& v, double radius) {
587  utility::LogDebug("[TrySeed] with v.idx={}, radius={}", v->idx_,
588  radius);
589  std::vector<int> indices;
590  std::vector<double> dists2;
591  kdtree_.SearchRadius(v->point_, 2 * radius, indices, dists2);
592  if (indices.size() < 3u) {
593  return false;
594  }
595 
596  for (size_t nbidx0 = 0; nbidx0 < indices.size(); ++nbidx0) {
597  const BallPivotingVertexPtr& nb0 = vertices[indices[nbidx0]];
598  if (nb0->type_ != BallPivotingVertex::Type::Orphan) {
599  continue;
600  }
601  if (nb0->idx_ == v->idx_) {
602  continue;
603  }
604 
605  int candidate_vidx2 = -1;
606  Eigen::Vector3d center;
607  for (size_t nbidx1 = nbidx0 + 1; nbidx1 < indices.size();
608  ++nbidx1) {
609  const BallPivotingVertexPtr& nb1 = vertices[indices[nbidx1]];
610  if (nb1->type_ != BallPivotingVertex::Type::Orphan) {
611  continue;
612  }
613  if (nb1->idx_ == v->idx_) {
614  continue;
615  }
616  if (TryTriangleSeed(v, nb0, nb1, indices, radius, center)) {
617  candidate_vidx2 = nb1->idx_;
618  break;
619  }
620  }
621 
622  if (candidate_vidx2 >= 0) {
623  const BallPivotingVertexPtr& nb1 = vertices[candidate_vidx2];
624 
625  BallPivotingEdgePtr e0 = GetLinkingEdge(v, nb1);
626  if (e0 != nullptr &&
627  e0->type_ != BallPivotingEdge::Type::Front) {
628  continue;
629  }
630  BallPivotingEdgePtr e1 = GetLinkingEdge(nb0, nb1);
631  if (e1 != nullptr &&
632  e1->type_ != BallPivotingEdge::Type::Front) {
633  continue;
634  }
635  BallPivotingEdgePtr e2 = GetLinkingEdge(v, nb0);
636  if (e2 != nullptr &&
637  e2->type_ != BallPivotingEdge::Type::Front) {
638  continue;
639  }
640 
641  CreateTriangle(v, nb0, nb1, center);
642 
643  e0 = GetLinkingEdge(v, nb1);
644  e1 = GetLinkingEdge(nb0, nb1);
645  e2 = GetLinkingEdge(v, nb0);
646  if (e0->type_ == BallPivotingEdge::Type::Front) {
647  edge_front_.push_front(e0);
648  }
649  if (e1->type_ == BallPivotingEdge::Type::Front) {
650  edge_front_.push_front(e1);
651  }
652  if (e2->type_ == BallPivotingEdge::Type::Front) {
653  edge_front_.push_front(e2);
654  }
655 
656  if (edge_front_.size() > 0) {
658  "[TrySeed] edge_front_.size() > 0 => return "
659  "true");
660  return true;
661  }
662  }
663  }
664 
665  utility::LogDebug("[TrySeed] return false");
666  return false;
667  }
668 
669  void FindSeedTriangle(double radius) {
670  for (size_t vidx = 0; vidx < vertices.size(); ++vidx) {
671  utility::LogDebug("[FindSeedTriangle] with radius={}, vidx={}",
672  radius, vidx);
673  if (vertices[vidx]->type_ == BallPivotingVertex::Type::Orphan) {
674  if (TrySeed(vertices[vidx], radius)) {
675  ExpandTriangulation(radius);
676  }
677  }
678  }
679  }
680 
681  std::shared_ptr<ccMesh> Run(const std::vector<double>& radii) {
682  if (!has_normals_) {
683  utility::LogError("ReconstructBallPivoting requires normals");
684  }
685 
686  mesh_->resize(0);
687 
688  for (double radius : radii) {
689  utility::LogDebug("[Run] ################################");
690  utility::LogDebug("[Run] change to radius {:.4f}", radius);
691  if (radius <= 0) {
693  "got an invalid, negative radius as parameter");
694  }
695 
696  // update radius => update border edges
697  for (auto it = border_edges_.begin(); it != border_edges_.end();) {
698  BallPivotingEdgePtr edge = *it;
699  BallPivotingTrianglePtr triangle = edge->triangle0_;
701  "[Run] try edge {:d}-{:d} of triangle {:d}-{:d}-{:d}",
702  edge->source_->idx_, edge->target_->idx_,
703  triangle->vert0_->idx_, triangle->vert1_->idx_,
704  triangle->vert2_->idx_);
705 
706  Eigen::Vector3d center;
707  if (ComputeBallCenter(triangle->vert0_->idx_,
708  triangle->vert1_->idx_,
709  triangle->vert2_->idx_, radius, center)) {
710  utility::LogDebug("[Run] yes, we can work on this");
711  std::vector<int> indices;
712  std::vector<double> dists2;
713  kdtree_.SearchRadius(center, radius, indices, dists2);
714  bool empty_ball = true;
715  for (auto idx : indices) {
716  if (idx != triangle->vert0_->idx_ &&
717  idx != triangle->vert1_->idx_ &&
718  idx != triangle->vert2_->idx_) {
720  "[Run] but no, the ball is not empty");
721  empty_ball = false;
722  break;
723  }
724  }
725 
726  if (empty_ball) {
728  "[Run] yeah, add edge to edge_front_: {:d}",
729  edge_front_.size());
730  edge->type_ = BallPivotingEdge::Type::Front;
731  edge_front_.push_back(edge);
732  it = border_edges_.erase(it);
733  continue;
734  }
735  }
736  ++it;
737  }
738 
739  // do the reconstruction
740  if (edge_front_.empty()) {
741  FindSeedTriangle(radius);
742  } else {
743  ExpandTriangulation(radius);
744  }
745 
746  utility::LogDebug("[Run] mesh_ has {:d} triangles", mesh_->size());
747  utility::LogDebug("[Run] ################################");
748  }
749 
750  // do some cleaning
751  {
752  mesh_->shrinkToFit();
753  NormsIndexesTableType* normals = mesh_->getTriNormsTable();
754  if (normals) {
755  normals->shrink_to_fit();
756  }
757  }
758  return mesh_;
759  }
760 
761 private:
762  bool has_normals_;
764  std::list<BallPivotingEdgePtr> edge_front_;
765  std::list<BallPivotingEdgePtr> border_edges_;
766  std::vector<BallPivotingVertexPtr> vertices;
767  std::shared_ptr<ccMesh> mesh_;
768 };
769 
771  const ccPointCloud& pcd, const std::vector<double>& radii) {
772  BallPivoting bp(pcd);
773  return bp.Run(radii);
774 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
double normal[3]
int height
std::shared_ptr< BallPivotingEdge > BallPivotingEdgePtr
std::shared_ptr< BallPivotingTriangle > BallPivotingTrianglePtr
BallPivotingVertex * BallPivotingVertexPtr
void AddAdjacentTriangle(BallPivotingTrianglePtr triangle)
BallPivotingTrianglePtr triangle1_
BallPivotingTrianglePtr triangle0_
BallPivotingEdge(BallPivotingVertexPtr source, BallPivotingVertexPtr target)
BallPivotingVertexPtr GetOppositeVertex()
BallPivotingTriangle(BallPivotingVertexPtr vert0, BallPivotingVertexPtr vert1, BallPivotingVertexPtr vert2, Eigen::Vector3d ball_center)
BallPivotingVertex(int idx, const Eigen::Vector3d &point, const Eigen::Vector3d &normal)
std::unordered_set< BallPivotingEdgePtr > edges_
Eigen::Vector3d ComputeFaceNormal(const Eigen::Vector3d &v0, const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
void CreateTriangle(const BallPivotingVertexPtr &v0, const BallPivotingVertexPtr &v1, const BallPivotingVertexPtr &v2, const Eigen::Vector3d &center)
bool ComputeBallCenter(int vidx1, int vidx2, int vidx3, double radius, Eigen::Vector3d &center)
bool IsCompatible(const BallPivotingVertexPtr &v0, const BallPivotingVertexPtr &v1, const BallPivotingVertexPtr &v2)
BallPivotingEdgePtr GetLinkingEdge(const BallPivotingVertexPtr &v0, const BallPivotingVertexPtr &v1)
BallPivoting(const ccPointCloud &pcd)
void FindSeedTriangle(double radius)
void ExpandTriangulation(double radius)
BallPivotingVertexPtr FindCandidateVertex(const BallPivotingEdgePtr &edge, double radius, Eigen::Vector3d &candidate_center)
bool TrySeed(BallPivotingVertexPtr &v, double radius)
std::shared_ptr< ccMesh > Run(const std::vector< double > &radii)
bool TryTriangleSeed(const BallPivotingVertexPtr &v0, const BallPivotingVertexPtr &v1, const BallPivotingVertexPtr &v2, const std::vector< int > &nb_indices, double radius, Eigen::Vector3d &center)
Array of compressed 3D normals (single index)
static std::shared_ptr< ccMesh > CreateFromPointCloudBallPivoting(const ccPointCloud &pcd, const std::vector< double > &radii)
virtual void setLocked(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:117
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addRGBColors(const std::vector< ecvColor::Rgb > &colors)
void addNorms(const std::vector< CCVector3 > &Ns)
Eigen::Vector3d getEigenNormal(size_t index) const
std::vector< CCVector3 > getPointNormals() const
bool hasNormals() const override
Returns whether normals are enabled or not.
const ColorsTableType & getPointColors() const
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
bool hasColors() const override
Returns whether colors are enabled or not.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
std::vector< CCVector3 > & getPoints()
unsigned size() const override
Definition: PointCloudTpl.h:38
Eigen::Vector3d getEigenPoint(size_t index) const
void addPoints(const std::vector< CCVector3 > &points)
KDTree with FLANN for nearest neighbor search.
static bool PointsCoplanar(const Eigen::Vector3d &p0, const Eigen::Vector3d &p1, const Eigen::Vector3d &p2, const Eigen::Vector3d &p3)
Tests if the given four points all lie on the same plane.
double normals[3]
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
a[190]
const double * e
double e2[36]
Generic file read and write utility for python interface.
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
std::vector< PointCoordinateType > radii
Definition: qM3C2Tools.cpp:42