13 #include <Eigen/Dense>
16 #include <unordered_map>
17 #include <unordered_set>
36 enum Type { Orphan = 0, Front = 1, Inner = 2 };
39 const Eigen::Vector3d& point,
40 const Eigen::Vector3d&
normal)
41 : idx_(idx), point_(point), normal_(
normal), type_(Orphan) {}
49 std::unordered_set<BallPivotingEdgePtr>
edges_;
55 enum Type { Border = 0, Front = 1, Inner = 2 };
58 : source_(source), target_(target), type_(
Type::Front) {}
76 Eigen::Vector3d ball_center)
80 ball_center_(ball_center) {}
94 if (edge->type_ != BallPivotingEdge::Type::Inner) {
104 if (triangle != triangle0_ && triangle != triangle1_) {
105 if (triangle0_ ==
nullptr) {
106 triangle0_ = triangle;
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) {
120 }
else if (triangle1_ ==
nullptr) {
121 triangle1_ = triangle;
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_;
138 return triangle0_->vert2_;
148 : has_normals_(pcd.hasNormals()), kdtree_(pcd) {
150 assert(baseVertices);
154 mesh_ = std::make_shared<ccMesh>(baseVertices);
155 mesh_->addChild(baseVertices);
173 for (
size_t vidx = 0; vidx < pcd.
size(); ++vidx) {
181 for (
auto vert : vertices) {
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();
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;
211 Eigen::Vector3d circ_center = alpha * v1 + beta * v2 + gamma * v3;
212 double circ_radius2 =
a * b * c;
217 circ_radius2 = circ_radius2 /
218 ((
a + b + c) * (b + c -
a) * (c +
a - b) * (
a + b - c));
220 double height = radius * radius - circ_radius2;
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) {
233 center = circ_center +
height * tr_norm;
243 if (edge0->source_->idx_ == edge1->source_->idx_ &&
244 edge0->target_->idx_ == edge1->target_->idx_) {
255 const Eigen::Vector3d& center) {
257 "[CreateTriangle] with v0.idx={}, v1.idx={}, v2.idx={}",
260 std::make_shared<BallPivotingTriangle>(v0, v1, v2, center);
264 e0 = std::make_shared<BallPivotingEdge>(v0, v1);
266 e0->AddAdjacentTriangle(triangle);
272 e1 = std::make_shared<BallPivotingEdge>(v1, v2);
274 e1->AddAdjacentTriangle(triangle);
280 e2 = std::make_shared<BallPivotingEdge>(v2, v0);
282 e2->AddAdjacentTriangle(triangle);
290 Eigen::Vector3d face_normal =
292 if (face_normal.dot(v0->
normal_) > -1
e-16) {
297 mesh_->addTriangleNorm(face_normal);
301 const Eigen::Vector3d& v1,
302 const Eigen::Vector3d& v2) {
303 Eigen::Vector3d
normal = (v1 - v0).cross(v2 - v0);
304 double norm =
normal.norm();
331 Eigen::Vector3d& candidate_center) {
333 edge->source_->idx_, edge->target_->idx_, radius);
338 if (opp ==
nullptr) {
340 assert(opp ==
nullptr);
353 edge->source_->idx_, edge->target_->idx_,
357 const Eigen::Vector3d& center = triangle->ball_center_;
359 edge->source_->idx_, edge->target_->idx_,
365 Eigen::Vector3d
a = center - mp;
368 std::vector<int> indices;
369 std::vector<double> dists2;
370 kdtree_.SearchRadius(mp, 2 * radius, indices, dists2);
375 double min_angle = 2 *
M_PI;
376 for (
auto nbidx : indices) {
382 "[FindCandidateVertex] candidate {:d} is a triangle "
383 "vertex of the edge",
388 candidate->
idx_, candidate->
point_.transpose());
396 LineSegmentsMinimumDistance(
400 LineSegmentsMinimumDistance(
404 "[FindCandidateVertex] candidate {:d} is interesecting "
405 "the existing triangle",
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 "
420 candidate->
idx_, new_center.transpose());
422 Eigen::Vector3d b = new_center - mp;
425 "[FindCandidateVertex] candidate {:d} v={}, a={}, b={}",
426 candidate->
idx_, v.transpose(),
a.transpose(),
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);
436 double angle = std::acos(cosinus);
438 Eigen::Vector3d c =
a.cross(b);
440 angle = 2 *
M_PI - angle;
443 if (angle >= min_angle) {
445 "[FindCandidateVertex] candidate {:d} angle {:f} > "
447 candidate->
idx_, angle, min_angle);
451 bool empty_ball =
true;
452 for (
auto nbidx2 : indices) {
458 if ((new_center - nb->
point_).norm() < radius - 1
e-16) {
460 "[FindCandidateVertex] candidate {:d} not an empty "
472 min_candidate = vertices[nbidx];
473 candidate_center = new_center;
477 if (min_candidate ==
nullptr) {
481 min_candidate->
idx_);
483 return min_candidate;
488 while (!edge_front_.empty()) {
490 edge_front_.pop_front();
495 Eigen::Vector3d center;
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);
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);
515 CreateTriangle(edge->source_, edge->target_, candidate, center);
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);
522 if (e1->type_ == BallPivotingEdge::Type::Front) {
523 edge_front_.push_front(e1);
531 const std::vector<int>& nb_indices,
533 Eigen::Vector3d& center) {
535 "[TryTriangleSeed] v0.idx={}, v1.idx={}, v2.idx={}, "
539 if (!IsCompatible(v0, v1, v2)) {
545 if (e0 !=
nullptr && e0->type_ == BallPivotingEdge::Type::Inner) {
547 "[TryTriangleSeed] returns {} because e0 is inner edge",
551 if (e1 !=
nullptr && e1->type_ == BallPivotingEdge::Type::Inner) {
553 "[TryTriangleSeed] returns {} because e1 is inner edge",
558 if (!ComputeBallCenter(v0->
idx_, v1->
idx_, v2->
idx_, radius, center)) {
560 "[TryTriangleSeed] returns {} could not compute ball "
567 for (
const auto& nbidx : nb_indices) {
573 if ((center - v->
point_).norm() < radius - 1
e-16) {
575 "[TryTriangleSeed] returns {} computed ball is not "
589 std::vector<int> indices;
590 std::vector<double> dists2;
591 kdtree_.SearchRadius(v->
point_, 2 * radius, indices, dists2);
592 if (indices.size() < 3u) {
596 for (
size_t nbidx0 = 0; nbidx0 < indices.size(); ++nbidx0) {
598 if (nb0->
type_ != BallPivotingVertex::Type::Orphan) {
605 int candidate_vidx2 = -1;
606 Eigen::Vector3d center;
607 for (
size_t nbidx1 = nbidx0 + 1; nbidx1 < indices.size();
610 if (nb1->
type_ != BallPivotingVertex::Type::Orphan) {
616 if (TryTriangleSeed(v, nb0, nb1, indices, radius, center)) {
617 candidate_vidx2 = nb1->
idx_;
622 if (candidate_vidx2 >= 0) {
627 e0->type_ != BallPivotingEdge::Type::Front) {
632 e1->type_ != BallPivotingEdge::Type::Front) {
637 e2->type_ != BallPivotingEdge::Type::Front) {
641 CreateTriangle(v, nb0, nb1, center);
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);
649 if (e1->type_ == BallPivotingEdge::Type::Front) {
650 edge_front_.push_front(e1);
652 if (
e2->type_ == BallPivotingEdge::Type::Front) {
653 edge_front_.push_front(
e2);
656 if (edge_front_.size() > 0) {
658 "[TrySeed] edge_front_.size() > 0 => return "
670 for (
size_t vidx = 0; vidx < vertices.size(); ++vidx) {
673 if (vertices[vidx]->type_ == BallPivotingVertex::Type::Orphan) {
674 if (TrySeed(vertices[vidx], radius)) {
675 ExpandTriangulation(radius);
681 std::shared_ptr<ccMesh>
Run(
const std::vector<double>&
radii) {
688 for (
double radius :
radii) {
693 "got an invalid, negative radius as parameter");
697 for (
auto it = border_edges_.begin(); it != border_edges_.end();) {
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_);
706 Eigen::Vector3d center;
707 if (ComputeBallCenter(triangle->vert0_->idx_,
708 triangle->vert1_->idx_,
709 triangle->vert2_->idx_, radius, center)) {
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");
728 "[Run] yeah, add edge to edge_front_: {:d}",
730 edge->type_ = BallPivotingEdge::Type::Front;
731 edge_front_.push_back(edge);
732 it = border_edges_.erase(it);
740 if (edge_front_.empty()) {
741 FindSeedTriangle(radius);
743 ExpandTriangulation(radius);
752 mesh_->shrinkToFit();
764 std::list<BallPivotingEdgePtr> edge_front_;
765 std::list<BallPivotingEdgePtr> border_edges_;
766 std::vector<BallPivotingVertexPtr> vertices;
767 std::shared_ptr<ccMesh> mesh_;
std::shared_ptr< BallPivotingEdge > BallPivotingEdgePtr
std::shared_ptr< BallPivotingTriangle > BallPivotingTrianglePtr
BallPivotingVertex * BallPivotingVertexPtr
BallPivotingVertexPtr source_
void AddAdjacentTriangle(BallPivotingTrianglePtr triangle)
BallPivotingTrianglePtr triangle1_
BallPivotingTrianglePtr triangle0_
BallPivotingVertexPtr target_
BallPivotingEdge(BallPivotingVertexPtr source, BallPivotingVertexPtr target)
BallPivotingVertexPtr GetOppositeVertex()
BallPivotingVertexPtr vert1_
Eigen::Vector3d ball_center_
BallPivotingTriangle(BallPivotingVertexPtr vert0, BallPivotingVertexPtr vert1, BallPivotingVertexPtr vert2, Eigen::Vector3d ball_center)
BallPivotingVertexPtr vert0_
BallPivotingVertexPtr vert2_
const Eigen::Vector3d & point_
const Eigen::Vector3d & normal_
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 ¢er)
bool ComputeBallCenter(int vidx1, int vidx2, int vidx3, double radius, Eigen::Vector3d ¢er)
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 ¢er)
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.
virtual void setEnabled(bool state)
Sets the "enabled" property.
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
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.
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.