You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

303 lines
13 KiB

// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
//
// 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