Browse Source

Split mesh non-manifold (and non-orientable) edges and non-manifold vertices (#2047)

* split_nonmanifold following gptoolbox; tests; templates

* connected components should be done on strongly connected adjacency matrix

* clean up templates
Alec Jacobson 3 years ago
parent
commit
33ed4e010b

+ 2 - 0
include/igl/connected_components.cpp

@@ -57,5 +57,7 @@ IGL_INLINE int igl::connected_components(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template int igl::connected_components<bool, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<bool, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
+// generated by autoexplicit.sh
 template int igl::connected_components<int, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<int, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 #endif

+ 2 - 0
include/igl/find.cpp

@@ -122,6 +122,8 @@ IGL_INLINE void igl::find(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template void igl::find<bool, Eigen::Matrix<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<bool, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<bool, 0, int> const&, Eigen::DenseBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&, Eigen::DenseBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&, Eigen::DenseBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&);
+// generated by autoexplicit.sh
 template void igl::find<int, 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::SparseMatrix<int, 0, int> const&, Eigen::DenseBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::DenseBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::DenseBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 
 template void igl::find<bool, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Array<bool, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<bool, 0, int> const&, Eigen::DenseBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::DenseBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::DenseBase<Eigen::Array<bool, -1, 1, 0, -1, 1> >&);

+ 11 - 3
include/igl/ismember_rows.cpp

@@ -44,13 +44,19 @@ IGL_INLINE void igl::ismember_rows(
   typedef Eigen::Matrix<typename DerivedB::Scalar,DerivedB::RowsAtCompileTime,DerivedB::RowsAtCompileTime> MatrixB;
   MatrixA uA;
   MatrixB uB;
-  Eigen::Matrix<typename DerivedA::Index,Dynamic,1> uIA,uIuA,uIB,uIuB;
+  // Using Eigen::Index or typename DerivedA::Index for the _Scalar_ of the
+  // types below is a big mistake. It triggers all sorts of type confusion in
+  // the templates on windows. Until we systematically template to support int64
+  // etc. we should assume the inputs don't exceed 2147483647, which is
+  // reasonable.
+  typedef int Index;
+  Eigen::Matrix<Index,Dynamic,1> uIA,uIuA,uIB,uIuB;
   unique_rows(A,uA,uIA,uIuA);
   unique_rows(B,uB,uIB,uIuB);
   // Sort both
   MatrixA sA;
   MatrixB sB;
-  Eigen::Matrix<typename DerivedA::Index,Dynamic,1> sIA,sIB;
+  Eigen::Matrix<Index,Dynamic,1> sIA,sIB;
   sortrows(uA,true,sA,sIA);
   sortrows(uB,true,sB,sIB);
 
@@ -97,9 +103,11 @@ IGL_INLINE void igl::ismember_rows(
 
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
+// generated by autoexplicit.sh
+template void igl::ismember_rows<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::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
+// generated by autoexplicit.sh
 template void igl::ismember_rows<Eigen::Matrix<int, -1, 2, 0, -1, 2>, Eigen::Matrix<int, -1, 2, 0, -1, 2>, Eigen::Array<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 0, -1, 2> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 2, 0, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Array<bool, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 template void igl::ismember_rows<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 template void igl::ismember_rows<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<bool, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 template void igl::ismember_rows<Eigen::Matrix<int, -1, 4, 0, -1, 4>, Eigen::Matrix<int, 12, 4, 0, 12, 4>, Eigen::Array<bool, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, 4, 0, -1, 4> > const&, Eigen::MatrixBase<Eigen::Matrix<int, 12, 4, 0, 12, 4> > const&, Eigen::PlainObjectBase<Eigen::Array<bool, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 #endif
-

+ 4 - 0
include/igl/matlab_format.cpp

@@ -123,6 +123,10 @@ IGL_INLINE const std::string igl::matlab_format(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template std::basic_string<char, std::char_traits<char>, std::allocator<char> > const igl::matlab_format<bool>(Eigen::SparseMatrix<bool, 0, int> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);
+// generated by autoexplicit.sh
+template std::basic_string<char, std::char_traits<char>, std::allocator<char> > const igl::matlab_format<int>(Eigen::SparseMatrix<int, 0, int> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);
+// generated by autoexplicit.sh
 template Eigen::WithFormat<Eigen::Matrix<double, -1, 1, 0, 4, 1> > const igl::matlab_format<Eigen::Matrix<double, -1, 1, 0, 4, 1> >(Eigen::DenseBase<Eigen::Matrix<double, -1, 1, 0, 4, 1> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);
 // generated by autoexplicit.sh
 template Eigen::WithFormat<Eigen::Matrix<double, -1, 1, 0, 10, 1> > const igl::matlab_format<Eigen::Matrix<double, -1, 1, 0, 10, 1> >(Eigen::DenseBase<Eigen::Matrix<double, -1, 1, 0, 10, 1> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);

+ 2 - 0
include/igl/slice_into.cpp

@@ -140,6 +140,8 @@ IGL_INLINE void igl::slice_into(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template void igl::slice_into<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::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, int, Eigen::Matrix<int, -1, 1, 0, -1, 1>&);
+// generated by autoexplicit.sh
 template void igl::slice_into<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int>&);
 template void igl::slice_into<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::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
 template void igl::slice_into<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::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);

+ 2 - 0
include/igl/sortrows.cpp

@@ -118,6 +118,8 @@ IGL_INLINE void igl::sortrows(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template void igl::sortrows<Eigen::Matrix<int, 12, 12, 0, 12, 12>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::DenseBase<Eigen::Matrix<int, 12, 12, 0, 12, 12> > const&, bool, Eigen::PlainObjectBase<Eigen::Matrix<int, 12, 12, 0, 12, 12> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
+// generated by autoexplicit.sh
 template void igl::sortrows<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<long, -1, 1, 0, -1, 1> >(Eigen::DenseBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, bool, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&);
 // generated by autoexplicit.sh
 template void igl::sortrows<Eigen::Matrix<int, 12, 12, 0, 12, 12>, Eigen::Matrix<long, -1, 1, 0, -1, 1> >(Eigen::DenseBase<Eigen::Matrix<int, 12, 12, 0, 12, 12> > const&, bool, Eigen::PlainObjectBase<Eigen::Matrix<int, 12, 12, 0, 12, 12> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&);

+ 166 - 0
include/igl/split_nonmanifold.cpp

@@ -0,0 +1,166 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2022 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 "split_nonmanifold.h"
+#include "ismember_rows.h"
+#include "slice.h"
+#include "slice_into.h"
+#include "slice_mask.h"
+#include "connected_components.h"
+#include "remove_unreferenced.h"
+#include <igl/matlab_format.h>
+#include <iostream>
+
+template <
+  typename DerivedF,
+  typename DerivedSF,
+  typename DerivedSVI
+  >
+IGL_INLINE void igl::split_nonmanifold(
+  const Eigen::MatrixBase<DerivedF> & F,
+  Eigen::PlainObjectBase <DerivedSF> & SF,
+  Eigen::PlainObjectBase <DerivedSVI> & SVI)
+{
+  // Number of faces
+  const int m = F.rows();
+  // For moment aact like everything will be split
+  SF.resize(m,3);
+  {
+    int k =0;
+    for(int j = 0;j<3;j++)
+    {
+      for(int i = 0;i<m;i++)
+      {
+        SF(i,j) = k++;
+      }
+    }
+  }
+  // Edges in SF
+  Eigen::MatrixXi E(m*3,2);
+  for(int i = 0;i<m;i++)
+  {
+    E.row(i+0*m) << SF(i,1),SF(i,2);
+    E.row(i+1*m) << SF(i,2),SF(i,0);
+    E.row(i+2*m) << SF(i,0),SF(i,1);
+  }
+  // Reindex E by F
+  Eigen::MatrixXi FE(E.rows(),E.cols());
+  for(int i = 0;i<E.rows();i++)
+  {
+    for(int j = 0;j<2;j++)
+    {
+      const int fi = E(i,j) % m;
+      const int fj = E(i,j) / m;
+      FE(i,j) = F(fi,fj);
+    }
+  }
+  // Flip orientation
+  Eigen::MatrixXi FE_flip = FE.rowwise().reverse();
+  // Find which exist in both directions
+  Eigen::VectorXi I,J;
+  igl::ismember_rows(FE,FE_flip,I,J);
+  // Just keep those find
+  Eigen::MatrixXi EI;
+  igl::slice_mask(E,I.array().cast<bool>(),1,EI);
+
+  Eigen::VectorXi JI;
+  igl::slice_mask(J,I.array().cast<bool>(),1,JI);
+  Eigen::MatrixXi EJI;
+  igl::slice(E,JI,1,EJI);
+  Eigen::MatrixXi EJI_flip = EJI.rowwise().reverse();
+  // Build adjacency matrix
+  std::vector<Eigen::Triplet<bool> > Aijv; 
+  Aijv.reserve(EI.size());
+  for(int i = 0;i<EI.rows();i++)
+  {
+    for(int j = 0;j<2;j++)
+    {
+      Aijv.emplace_back( 
+        EI(i,j), 
+        EJI_flip(i,j), 
+        true);
+    }
+  }
+  // Build A to contain off-diagonals only if both directions are present
+  Eigen::SparseMatrix<bool> A1(m*3,m*3);
+  A1.setFromTriplets(Aijv.begin(),Aijv.end());
+  // For some reason I can't write `A = A1 && A1.transpose();`
+  Eigen::SparseMatrix<bool> A1T = A1.transpose();
+  Eigen::SparseMatrix<bool> A = A1 && A1T;
+
+
+  Eigen::VectorXi K;
+  {
+    Eigen::VectorXi _;
+    igl::connected_components(A,K,_);
+  }
+
+
+  // Remap by components
+  for(int j = 0;j<3;j++)
+  {
+    for(int i = 0;i<m;i++)
+    {
+      SF(i,j) = K(SF(i,j));
+    }
+  }
+  
+  // Initial mapping
+  Eigen::VectorXi SVI0(m*3);
+  {
+    int k =0;
+    for(int j = 0;j<3;j++)
+    {
+      for(int i = 0;i<m;i++)
+      {
+        SVI0(k++) = F(i,j);
+      }
+    }
+  }
+  assert(K.size() == m*3);
+  // Scatter via K
+  // SVI1(K) = SVI(K);
+  Eigen::VectorXi SVI1(m*3);
+  igl::slice_into(SVI0,K,1,SVI1);
+
+  {
+    Eigen::VectorXi _,J;
+    igl::remove_unreferenced(SF.maxCoeff()+1,SF,_,J);
+    // Remap by J
+    for(int j = 0;j<3;j++)
+    {
+      for(int i = 0;i<m;i++)
+      {
+        SF(i,j) = J(SF(i,j));
+      }
+    }
+    igl::slice(SVI1,J,1,SVI);
+  }
+
+}
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedSV,
+  typename DerivedSF,
+  typename DerivedSVI
+  >
+IGL_INLINE void igl::split_nonmanifold(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  Eigen::PlainObjectBase <DerivedSV> & SV,
+  Eigen::PlainObjectBase <DerivedSF> & SF,
+  Eigen::PlainObjectBase <DerivedSVI> & SVI)
+{
+  igl::split_nonmanifold(F,SF,SVI);
+  igl::slice(V,SVI,1,SV);
+}
+
+#ifdef IGL_STATIC_LIBRARY
+template void igl::split_nonmanifold<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::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<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> >&);
+#endif

+ 57 - 0
include/igl/split_nonmanifold.h

@@ -0,0 +1,57 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2022 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/.
+
+#ifndef IGL_SPLIT_NONMANIFOLD_H
+#define IGL_SPLIT_NONMANIFOLD_H
+#include "igl_inline.h"
+#include <Eigen/Core>
+namespace igl
+{
+  // SPLIT_NONMANIFOLD Split a non-manifold (or non-orientable) mesh into a
+  // orientable manifold mesh possibly with more connected components and
+  // geometrically duplicate vertices.
+  //
+  // Inputs:
+  //   F  #F by 3 list of mesh triangle indices into rows of some V
+  // Outputs:
+  //   SF  #F by 3 list of mesh triangle indices into rows of a new vertex list
+  //     SV = V(SVI,:)
+  //   SVI  #SV list of indices into V identifying vertex positions
+  template <
+    typename DerivedF,
+    typename DerivedSF,
+    typename DerivedSVI
+    >
+  IGL_INLINE void split_nonmanifold(
+      const Eigen::MatrixBase<DerivedF> & F,
+      Eigen::PlainObjectBase <DerivedSF> & SF,
+      Eigen::PlainObjectBase <DerivedSVI> & SVI);
+  // Inputs:
+  //   V  #V by dim explicit list of vertex positions
+  // Outputs:
+  //   SV  #SV by dim explicit list of vertex positions
+  template <
+    typename DerivedV,
+    typename DerivedF,
+    typename DerivedSV,
+    typename DerivedSF,
+    typename DerivedSVI
+    >
+  IGL_INLINE void split_nonmanifold(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    Eigen::PlainObjectBase <DerivedSV> & SV,
+    Eigen::PlainObjectBase <DerivedSF> & SF,
+    Eigen::PlainObjectBase <DerivedSVI> & SVI);
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "split_nonmanifold.cpp"
+#endif
+
+#endif

+ 8 - 0
include/igl/unique_rows.cpp

@@ -75,6 +75,14 @@ IGL_INLINE void igl::unique_rows(
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // generated by autoexplicit.sh
+template void igl::unique_rows<Eigen::Matrix<int, -1, 4, 0, -1, 4>, 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::DenseBase<Eigen::Matrix<int, -1, 4, 0, -1, 4> > const&, 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> >&);
+// generated by autoexplicit.sh
+template void igl::unique_rows<Eigen::Matrix<int, -1, 2, 0, -1, 2>, 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::DenseBase<Eigen::Matrix<int, -1, 2, 0, -1, 2> > const&, 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> >&);
+// generated by autoexplicit.sh
+template void igl::unique_rows<Eigen::Matrix<int, 12, 4, 0, 12, 4>, Eigen::Matrix<int, 12, 12, 0, 12, 12>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::DenseBase<Eigen::Matrix<int, 12, 4, 0, 12, 4> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, 12, 12, 0, 12, 12> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
+// generated by autoexplicit.sh
+template void igl::unique_rows<Eigen::Matrix<double, -1, 3, 0, -1, 3>, 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::DenseBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > 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> >&);
+// generated by autoexplicit.sh
 template void igl::unique_rows<Eigen::Matrix<int, -1, 4, 0, -1, 4>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<long, -1, 1, 0, -1, 1>, Eigen::Matrix<long, -1, 1, 0, -1, 1> >(Eigen::DenseBase<Eigen::Matrix<int, -1, 4, 0, -1, 4> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&);
 // generated by autoexplicit.sh
 template void igl::unique_rows<Eigen::Matrix<int, -1, 2, 0, -1, 2>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<long, -1, 1, 0, -1, 1>, Eigen::Matrix<long, -1, 1, 0, -1, 1> >(Eigen::DenseBase<Eigen::Matrix<int, -1, 2, 0, -1, 2> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<long, -1, 1, 0, -1, 1> >&);

+ 277 - 0
tests/include/igl/split_nonmanifold.cpp

@@ -0,0 +1,277 @@
+#include <test_common.h>
+#include <igl/split_nonmanifold.h>
+#include <igl/matlab_format.h>
+#include <iostream>
+
+TEST_CASE("split_nonmanifold: edge-fan", "[igl]")
+{
+  using namespace igl;
+  Eigen::MatrixXd V(7,3);
+  V << 0,0,0,
+       1,0,0,
+      -1,0,0,
+       0,1,0,
+       0,0,1,
+       0,0,-1,
+       1,0,1;
+  Eigen::MatrixXi F(5,3);
+  F<<0,1,3,
+     0,3,2,
+     0,4,3,
+     0,3,5,
+     0,3,6;
+  Eigen::MatrixXd SV;
+  Eigen::MatrixXi SF;
+  Eigen::VectorXi SVI;
+  igl::split_nonmanifold(V,F,SV,SF,SVI);
+  Eigen::MatrixXd SVgt(13,3);
+  SVgt<<
+    0,0,0,
+    0,0,0,
+    0,0,0,
+    0,0,0,
+    1,0,0,
+    0,1,0,
+    0,0,1,
+    0,1,0,
+    0,1,0,
+    -1,0,0,
+    0,1,0,
+    0,0,-1,
+    1,0,1;
+  Eigen::MatrixXi SFgt(5,3);
+  SFgt<<
+    0,4,5,
+    0,5,9,
+    1,6,10,
+    2,7,11,
+    3,8,12;
+  Eigen::VectorXi SVIgt(13);
+  SVIgt<<0,0,0,0,1,3,4,3,3,2,3,5,6;
+  test_common::assert_eq(SV,SVgt);
+  test_common::assert_eq(SF,SFgt);
+  test_common::assert_eq(SVI,SVIgt);
+}
+
+TEST_CASE("split_nonmanifold: vertex-boundary", "[igl]")
+{
+  using namespace igl;
+  Eigen::MatrixXd V(5,2);
+  V << 0,0,
+       1,0,
+       0,1,
+       2,0,
+       1,1;
+  Eigen::MatrixXi F(2,3);
+  F<<0,1,2,
+     1,3,4;
+  Eigen::MatrixXd SV;
+  Eigen::MatrixXi SF;
+  Eigen::VectorXi SVI;
+  igl::split_nonmanifold(V,F,SV,SF,SVI);
+  Eigen::MatrixXd SVgt(6,2);
+  SVgt<<
+    0,0,
+    1,0,
+    1,0,
+    2,0,
+    0,1,
+    1,1;
+  Eigen::MatrixXi SFgt(2,3);
+  SFgt<<
+    0,2,4,
+    1,3,5;
+  Eigen::VectorXi SVIgt(6);
+  SVIgt << 0, 1, 1, 3, 2, 4;
+  test_common::assert_eq(SV,SVgt);
+  test_common::assert_eq(SF,SFgt);
+  test_common::assert_eq(SVI,SVIgt);
+}
+
+TEST_CASE("split_nonmanifold: edge-disk-flap", "[igl]")
+{
+  using namespace igl;
+  Eigen::MatrixXd V(6,3);
+  V<<
+    0,0,0,
+    1,0,0,
+    0,1,0,
+    -1,0,0,
+    0,-1,0,
+    0,0,1;
+  Eigen::MatrixXi F(5,3);
+  F<<
+    0,1,2,
+    0,2,3,
+    0,3,4,
+    0,4,1,
+    0,5,1;
+  Eigen::MatrixXd SV;
+  Eigen::MatrixXi SF;
+  Eigen::VectorXi SVI;
+  igl::split_nonmanifold(V,F,SV,SF,SVI);
+  Eigen::MatrixXd SVgt(8,3);
+  SVgt<<
+    0,0,0,
+    0,0,0,
+    1,0,0,
+    0,1,0,
+    -1,0,0,
+    0,-1,0,
+    0,0,1,
+    1,0,0;
+  Eigen::MatrixXi SFgt(5,3);
+  SFgt<<
+    0,2,3,
+    0,3,4,
+    0,4,5,
+    0,5,2,
+    1,6,7;
+  Eigen::VectorXi SVIgt(8);
+  SVIgt<<  0,  0,  1,  2,  3,  4,  5,  1;
+  test_common::assert_eq(SV,SVgt);
+  test_common::assert_eq(SF,SFgt);
+  test_common::assert_eq(SVI,SVIgt);
+}
+
+TEST_CASE("split_nonmanifold: edge-disk-tent", "[igl]")
+{
+  using namespace igl;
+  Eigen::MatrixXd V(5,3);
+  V<<
+    0,0,0,
+    1,0,0,
+    -1,1,0,
+    0,-1,0,
+    0,0,1;
+  Eigen::MatrixXi F(5,3);
+  F<<
+    0,1,2,
+    0,2,3,
+    0,3,1,
+    0,4,1,
+    1,4,3;
+  Eigen::MatrixXd SV;
+  Eigen::MatrixXi SF;
+  Eigen::VectorXi SVI;
+  igl::split_nonmanifold(V,F,SV,SF,SVI);
+  Eigen::MatrixXd SVgt(8,3);
+  SVgt<<
+    0,0,0,
+    0,0,0,
+    1,0,0,
+    1,0,0,
+    -1,1,0,
+    0,-1,0,
+    0,0,1,
+    0,-1,0;
+  Eigen::MatrixXi SFgt(5,3);
+  SFgt<<
+  0,3,4,
+  0,4,5,
+  0,5,3,
+  1,6,2,
+  2,6,7;
+  Eigen::VectorXi SVIgt(8);
+  SVIgt<<  0,  0,  1,  1,  2,  3,  4,  3;
+  test_common::assert_eq(SV,SVgt);
+  test_common::assert_eq(SF,SFgt);
+  test_common::assert_eq(SVI,SVIgt);
+}
+
+TEST_CASE("split_nonmanifold: vertex-kiss", "[igl]")
+{
+  using namespace igl;
+  Eigen::MatrixXd V(7,3);
+  V<<
+    0,0,0,
+    1,0,0,
+    0,1,0,
+    0,0,1,
+    0,0,2,
+    1,0,2,
+    0,1,2;
+  Eigen::MatrixXi F(6,3);
+  F<<
+    0,1,3,
+    1,2,3,
+    2,0,3,
+    4,5,3,
+    5,6,3,
+    6,4,3;
+  Eigen::MatrixXd SV;
+  Eigen::MatrixXi SF;
+  Eigen::VectorXi SVI;
+  igl::split_nonmanifold(V,F,SV,SF,SVI);
+  Eigen::MatrixXd SVgt(8,3);
+  SVgt<<
+    0,0,0,
+    1,0,0,
+    0,1,0,
+    0,0,2,
+    1,0,2,
+    0,1,2,
+    0,0,1,
+    0,0,1;
+  Eigen::MatrixXi SFgt(6,3);
+  SFgt<<
+    0,1,6,
+    1,2,6,
+    2,0,6,
+    3,4,7,
+    4,5,7,
+    5,3,7;
+  Eigen::VectorXi SVIgt(8);
+  SVIgt<<  0,  1,  2,  4,  5,  6,  3,  3;
+  test_common::assert_eq(SV,SVgt);
+  test_common::assert_eq(SF,SFgt);
+  test_common::assert_eq(SVI,SVIgt);
+}
+
+TEST_CASE("split_nonmanifold: non-orientable", "[igl]")
+{
+  using namespace igl;
+  Eigen::MatrixXd V(6,3);
+  V<<
+     6, 0, 0,
+     4, 0, 0,
+    -3, 5, 0,
+    -2, 4, 0,
+    -2,-4, 1,
+    -3,-5,-1;
+  Eigen::MatrixXi F(6,3);
+  F<<
+    0,2,1,
+    2,3,1,
+    2,4,3,
+    4,5,3,
+    4,1,5,
+    1,0,5;
+  Eigen::MatrixXd SV;
+  Eigen::MatrixXi SF;
+  Eigen::VectorXi SVI;
+  igl::split_nonmanifold(V,F,SV,SF,SVI);
+  Eigen::MatrixXd SVgt(8,3);
+  SVgt<<
+    6,0,0,
+    -3,5,0,
+    -2,-4,1,
+    4,0,0,
+    -2,4,0,
+    -3,-5,-1,
+    6,0,0,
+    4,0,0;
+  Eigen::MatrixXi SFgt(6,3);
+  SFgt<<
+    0,1,7,
+    1,4,7,
+    1,2,4,
+    2,5,4,
+    2,3,5,
+    3,6,5;
+  Eigen::VectorXi SVIgt(8);
+  SVIgt<<  0,  2,  4,  1,  3,  5,  0,  1;
+  test_common::assert_eq(SV,SVgt);
+  test_common::assert_eq(SF,SFgt);
+  test_common::assert_eq(SVI,SVIgt);
+}