| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- // This file is part of libigl, a simple c++ geometry processing library.
- //
- // Copyright (C) 2016 Alec Jacobson <[email protected]>
- //
- // This Source Code Form is subject to the terms of the Mozilla Public License
- // v. 2.0. If a copy of the MPL was not distributed with this file, You can
- // obtain one at http://mozilla.org/MPL/2.0/.
- #include "trim_with_solid.h"
- #include "assign.h"
- #include "intersect_other.h"
- #include "remesh_self_intersections.h"
- #include "point_solid_signed_squared_distance.h"
- #include "../../extract_manifold_patches.h"
- #include "../../connected_components.h"
- #include "../../facet_adjacency_matrix.h"
- #include "../../list_to_matrix.h"
- #include "../../find.h"
- #include "../../get_seconds.h"
- #include "../../barycenter.h"
- #include "../../remove_unreferenced.h"
- #include <CGAL/Exact_predicates_exact_constructions_kernel.h>
- #include <vector>
- template <
- typename DerivedVA,
- typename DerivedFA,
- typename DerivedVB,
- typename DerivedFB,
- typename DerivedV,
- typename DerivedF,
- typename DerivedD,
- typename DerivedJ>
- IGL_INLINE void igl::copyleft::cgal::trim_with_solid(
- const Eigen::PlainObjectBase<DerivedVA> & VA,
- const Eigen::PlainObjectBase<DerivedFA> & FA,
- const Eigen::PlainObjectBase<DerivedVB> & VB,
- const Eigen::PlainObjectBase<DerivedFB> & FB,
- Eigen::PlainObjectBase<DerivedV> & Vd,
- Eigen::PlainObjectBase<DerivedF> & F,
- Eigen::PlainObjectBase<DerivedD> & D,
- Eigen::PlainObjectBase<DerivedJ> & J)
- {
- return trim_with_solid(
- VA,FA,VB,FB,TrimWithSolidMethod::CHECK_EACH_PATCH,
- Vd,F,D,J);
- }
- template <
- typename DerivedVA,
- typename DerivedFA,
- typename DerivedVB,
- typename DerivedFB,
- typename DerivedV,
- typename DerivedF,
- typename DerivedD,
- typename DerivedJ>
- IGL_INLINE void igl::copyleft::cgal::trim_with_solid(
- const Eigen::PlainObjectBase<DerivedVA> & VA,
- const Eigen::PlainObjectBase<DerivedFA> & FA,
- const Eigen::PlainObjectBase<DerivedVB> & VB,
- const Eigen::PlainObjectBase<DerivedFB> & FB,
- const TrimWithSolidMethod method,
- Eigen::PlainObjectBase<DerivedV> & Vd,
- Eigen::PlainObjectBase<DerivedF> & F,
- Eigen::PlainObjectBase<DerivedD> & D,
- Eigen::PlainObjectBase<DerivedJ> & J)
- {
- // resolve intersections using exact representation
- typedef Eigen::Matrix<CGAL::Epeck::FT,Eigen::Dynamic,3> MatrixX3E;
- typedef Eigen::Matrix<CGAL::Epeck::FT,Eigen::Dynamic,1> VectorXE;
- // Previously this used intersect_other to resolve intersections between A and
- // B hoping that it'd merge the two into a mesh where patches are separated by
- // non-manifold edges. This happens most of the time but sometimes the
- // new triangulations on faces of A don't match those on faces of B.
- // Specifically it seems you can get T junctions:
- //
- // /|\
- // / | \
- // / B| \
- // ---| A ⋅
- // \ B| /
- // \ | /
- // \|/
- // Probably intersect_other should not be attempting to output a single mesh
- // (i.e., when detect_only=false).
- //
- // # Alternative 1)
- //
- // Just call point_solid_signed_squared_distance for each output face.
- // Obviously O(#output-faces) calls to point_solid_signed_squared_distance.
- // But we get to use intersect_other to avoid finding and remeshing
- // self-intersections in A
- //
- // # Alternative 2)
- //
- // Use SelfIntersectMesh to really get a
- // single mesh with non-manifold edges. _But_ this would resolve any existing
- // self-intersections in (VA,FA) which is not requested. An idea to "undo"
- // this resolution is to undo any intersections _involving_ faces between A,B.
- //
- // This results in O(#patches) calls to point_solid_signed_squared_distance
- // but calls remeshes on the order of O(#self-intersections-in-A)
- //
- // # Alterative 3)
- //
- // Use intersect_other but then create an adjacency matrix based on facets
- // that share an edge but have a dissimilar J value. This will likely result
- // in lots of tiny patches along the intersection belt. So let's say it has
- // O(#A-B-intersection) calls to point_solid_signed_squared_distance
- //
- // If point_solid_signed_squared_distance turns out to me costly then 1) is
- // out and we should do 2) or 3).
- //
- // Indeed. point_solid_signed_squared_distance is a major bottleneck for some
- // examples.
- //
- MatrixX3E V;
- const auto set_D_via_patches =
- [&V,&F,&D,&VB,&FB](const int num_patches, const Eigen::VectorXi & P)
- {
- // Aggregate representative query points for each patch
- std::vector<bool> flag(num_patches);
- std::vector<std::vector<CGAL::Epeck::FT> > vQ;
- Eigen::VectorXi P2Q(num_patches);
- for(int f = 0;f<P.rows();f++)
- {
- const auto p = P(f);
- // if not yet processed this patch
- if(!flag[p])
- {
- P2Q(p) = vQ.size();
- std::vector<CGAL::Epeck::FT> q = {
- (V(F(f,0),0)+ V(F(f,1),0)+ V(F(f,2),0))/3.,
- (V(F(f,0),1)+ V(F(f,1),1)+ V(F(f,2),1))/3.,
- (V(F(f,0),2)+ V(F(f,1),2)+ V(F(f,2),2))/3.};
- vQ.emplace_back(q);
- flag[p] = true;
- }
- }
- MatrixX3E Q;
- igl::list_to_matrix(vQ,Q);
- VectorXE SP;
- point_solid_signed_squared_distance(Q,VB,FB,SP);
- Eigen::Matrix<bool,Eigen::Dynamic,1> DP = SP.array()>0;
- // distribute flag to all faces
- D.resize(F.rows());
- for(int f = 0;f<F.rows();f++)
- {
- D(f) = DP(P2Q(P(f)));
- }
- };
- switch(method)
- {
- case CHECK_EACH_PATCH:
- case CHECK_EACH_FACE:
- {
- Eigen::MatrixXi _1;
- Eigen::VectorXi _2;
- // Intersect A and B meshes and stitch together new faces
- igl::copyleft::cgal::intersect_other(
- VA,FA,VB,FB,{false,false,true},_1,V,F,J,_2);
- const auto keep = igl::find( (J.array()<FA.rows()).eval() );
- F = F(keep,Eigen::all).eval();
- J = J(keep).eval();
- {
- Eigen::VectorXi _;
- igl::remove_unreferenced(decltype(V)(V),decltype(F)(F),V,F,_);
- }
- switch(method)
- {
- default: /*unreachable*/ break;
- case CHECK_EACH_PATCH:
- {
- Eigen::SparseMatrix<bool> A;
- igl::facet_adjacency_matrix(F,A);
- for(int i = 0; i < A.outerSize(); i++)
- {
- for(decltype(A)::InnerIterator it(A,i); it; ++it)
- {
- const int a = it.row();
- const int b = it.col();
- if(J(a) == J(b))
- {
- A.coeffRef(a,b) = false;
- }
- }
- }
- A.prune(false);
- Eigen::VectorXi P,K;
- const int num_patches = igl::connected_components(A,P,K);
- set_D_via_patches(num_patches,P);
- break;
- }
- case CHECK_EACH_FACE:
- {
- MatrixX3E Q;
- igl::barycenter(V,F,Q);
- VectorXE SP;
- point_solid_signed_squared_distance(Q,VB,FB,SP);
- D = (SP.array()>0).template cast<typename DerivedD::Scalar>();
- break;
- }
- }
- break;
- }
- case RESOLVE_BOTH_AND_RESTORE_THEN_CHECK_EACH_PATCH:
- {
- RemeshSelfIntersectionsParam params;
- // This is somewhat dubious but appears to work. The stitch_all flag is
- // poorly documented.
- params.stitch_all = false;
- {
- Eigen::MatrixXi IF;
- Eigen::Matrix<typename DerivedVA::Scalar,Eigen::Dynamic,3> VAB(VA.rows() + VB.rows(),3);
- VAB << VA,VB;
- Eigen::Matrix<typename DerivedFA::Scalar,Eigen::Dynamic,3> FAB(FA.rows() + FB.rows(),3);
- FAB << FA,FB.array()+VA.rows();
- /// Sigh. Can't use this because of how it calls remove_unreferenced
- // remesh_self_intersections(VAB,FAB,params,V,F,IF,J);
- {
- Eigen::VectorXi I;
- igl::copyleft::cgal::remesh_self_intersections(
- VAB, FAB, params, V, F, IF, J, I);
- // Undo self-intersection remeshing in FA
- {
- Eigen::Array<bool,Eigen::Dynamic,1> avoids_B =
- Eigen::Array<bool,Eigen::Dynamic,1>::Constant(FA.rows(),1,true);
- for(int p = 0;p<IF.rows();p++)
- {
- if(IF(p,0) >= FA.rows() || IF(p,1) >= FA.rows())
- {
- if(IF(p,0) < FA.rows()){ avoids_B[IF(p,0)] = false; }
- if(IF(p,1) < FA.rows()){ avoids_B[IF(p,1)] = false; }
- }
- }
- // Find first entry for each in J
- Eigen::VectorXi first = Eigen::VectorXi::Constant(FA.rows(),1,-1);
- for(int j = 0;j<J.rows();j++)
- {
- if(J(j) < FA.rows() && first[J(j)] == -1)
- {
- first[J(j)] = j;
- // restore original face at this first entry
- if(avoids_B[J(j)])
- {
- F.row(j) = FA.row(J(j));
- }
- }
- }
- // Maybe this cannot happen for co-planar?
- assert(first.minCoeff() >= 0 && "Every face should be found");
- std::vector<int> keep;
- for(int f = 0;f<F.rows();f++)
- {
- if(J(f)>=FA.rows() || !avoids_B[J(f)] || first[J(f)] == f)
- {
- keep.push_back(f);
- }
- }
- F = F(keep,Eigen::all).eval();
- J = J(keep).eval();
- }
- // Don't do this until the very end:
- assert(I.size() == V.rows());
- // Merge coinciding vertices into non-manifold vertices.
- std::for_each(F.data(),F.data()+F.size(),[&I](typename DerivedF::Scalar & a){a=I[a];});
- }
- }
- // Partition result into manifold patches
- Eigen::VectorXi P;
- const int num_patches = igl::extract_manifold_patches(F,P);
- // only keep faces from A
- Eigen::Array<bool,Eigen::Dynamic,1> A = J.array()< FA.rows();
- const auto AI = igl::find(A);
- F = F(AI,Eigen::all).eval();
- J = J(AI).eval();
- P = P(AI).eval();
- set_D_via_patches(num_patches,P);
- break;
- }
- }
- {
- Eigen::VectorXi _;
- igl::remove_unreferenced(MatrixX3E(V),DerivedF(F),V,F,_);
- }
- assign(V,Vd);
- }
- #ifdef IGL_STATIC_LIBRARY
- // Explicit template instantiation
- template void igl::copyleft::cgal::trim_with_solid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
- template void igl::copyleft::cgal::trim_with_solid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Array<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Array<bool, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
- template void igl::copyleft::cgal::trim_with_solid<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Array<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, igl::copyleft::cgal::TrimWithSolidMethod, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Array<bool, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
- #endif
|