Browse Source

minkowski sum of mesh along segment

Former-commit-id: 9f40991d230918de65351f0dc7d6d4a111a5a3c3
Alec Jacobson 9 years ago
parent
commit
cd1621c1b4

+ 208 - 0
include/igl/copyleft/boolean/minkowski_sum.cpp

@@ -0,0 +1,208 @@
+#include "minkowski_sum.h"
+#include "mesh_boolean.h"
+
+#include "../../slice_mask.h"
+#include "../../unique.h"
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename Deriveds,
+  typename Derivedd,
+  typename DerivedW,
+  typename DerivedG,
+  typename DerivedJ>
+IGL_INLINE void igl::copyleft::boolean::minkowski_sum(
+  const Eigen::PlainObjectBase<DerivedV> & V,
+  const Eigen::PlainObjectBase<DerivedF> & F,
+  const Eigen::PlainObjectBase<Deriveds> & s,
+  const Eigen::PlainObjectBase<Derivedd> & d,
+  Eigen::PlainObjectBase<DerivedW> & W,
+  Eigen::PlainObjectBase<DerivedG> & G,
+  Eigen::PlainObjectBase<DerivedJ> & J)
+{
+  using namespace Eigen;
+  using namespace std;
+  // silly base case
+  if(F.size() == 0)
+  {
+    W.resize(0,3);
+    G.resize(0,3);
+    return;
+  }
+  const int dim = V.cols();
+  assert(dim == 3 && "dim must be 3D");
+  assert(s.size() == 3 && "s must be 3D point");
+  assert(d.size() == 3 && "d must be 3D point");
+  // segment vector
+  const CGAL::Vector_3<CGAL::Epeck> v(d(0)-s(0),d(1)-s(1),d(2)-s(2));
+  // number of vertices
+  const int n = V.rows();
+  // duplicate vertices at s and d, we'll remove unreferernced later
+  DerivedW WW(2*n,dim);
+  for(int i = 0;i<n;i++)
+  {
+    for(int j = 0;j<dim;j++)
+    {
+      WW  (i,j) = V(i,j) + s(j);
+      WW(i+n,j) = V(i,j) + d(j);
+    }
+  }
+  // number of faces
+  const int m = F.rows();
+  // Mask whether positive dot product, or negative: because of exactly zero,
+  // these are not necessarily complementary
+  Matrix<bool,Dynamic,1> P(m,1),N(m,1);
+  // loop over faces
+  int mp = 0,mn = 0;
+  for(int f = 0;f<m;f++)
+  {
+    const CGAL::Plane_3<CGAL::Epeck> plane(
+      CGAL::Point_3<CGAL::Epeck>(V(F(f,0),0),V(F(f,0),1),V(F(f,0),2)),
+      CGAL::Point_3<CGAL::Epeck>(V(F(f,1),0),V(F(f,1),1),V(F(f,1),2)),
+      CGAL::Point_3<CGAL::Epeck>(V(F(f,2),0),V(F(f,2),1),V(F(f,2),2)));
+    const auto normal = plane.orthogonal_vector();
+    const auto dt = normal * v;
+    if(dt > 0)
+    {
+      P(f) = true;
+      N(f) = false;
+      mp++;
+    }else if(dt < 0)
+    {
+      P(f) = false;
+      N(f) = true;
+      mn++;
+    }else
+    {
+      P(f) = false;
+      N(f) = false;
+    }
+  }
+
+  typedef Matrix<typename DerivedG::Scalar,Dynamic,Dynamic> MatrixXI;
+  typedef Matrix<typename DerivedG::Scalar,Dynamic,1> VectorXI;
+  MatrixXI GT(mp+mn,3);
+  GT<< slice_mask(F,N,1), slice_mask((F.array()+n).eval(),P,1);
+  // J indexes F for parts at s and m+F for parts at d
+  J = DerivedJ::LinSpaced(m,0,m-1);
+  DerivedJ JT(mp+mn);
+  JT << slice_mask(J,P,1), slice_mask(J,N,1);
+  JT.block(mp,0,mn,1).array()+=m;
+
+  // Original non-co-planar faces with positively oriented reversed
+  MatrixXI BA(mp+mn,3);
+  BA << slice_mask(F,P,1).rowwise().reverse(), slice_mask(F,N,1);
+  // Quads along **all** sides
+  MatrixXI GQ((mp+mn)*3,4);
+  GQ<< 
+    BA.col(1), BA.col(0), BA.col(0).array()+n, BA.col(1).array()+n,
+    BA.col(2), BA.col(1), BA.col(1).array()+n, BA.col(2).array()+n,
+    BA.col(0), BA.col(2), BA.col(2).array()+n, BA.col(0).array()+n;
+
+  MatrixXI uGQ;
+  VectorXI S,sI,sJ;
+  //const auto & total_signed_distance = 
+  [](
+      const MatrixXI & F,
+      VectorXI & S,
+      MatrixXI & uF,
+      VectorXI & I,
+      VectorXI & J)
+  {
+    const int m = F.rows();
+    const int d = F.cols();
+    MatrixXI sF = F;
+    const auto MN = sF.rowwise().minCoeff().eval();
+    // rotate until smallest index is first
+    for(int p = 0;p<d;p++)
+    {
+      for(int f = 0;f<m;f++)
+      {
+        if(sF(f,0) != MN(f))
+        {
+          for(int r = 0;r<d-1;r++)
+          {
+            std::swap(sF(f,r),sF(f,r+1));
+          }
+        }
+      }
+    }
+    // swap orienation
+    for(int f = 0;f<m;f++)
+    {
+      if(sF(f,d-1) < sF(f,1))
+      {
+        sF.block(f,1,1,d-1) = sF.block(f,1,1,d-1).reverse().eval();
+      }
+    }
+    Matrix<bool,Dynamic,1> M = Matrix<bool,Dynamic,1>::Zero(m,1);
+    {
+      VectorXI P = VectorXI::LinSpaced(d,0,d-1);
+      for(int p = 0;p<d;p++)
+      {
+        for(int f = 0;f<m;f++)
+        {
+          bool all = true;
+          for(int r = 0;r<d;r++)
+          {
+            all = all && (sF(f,P(r)) == F(f,r));
+          }
+          M(f) = M(f) || all;
+        }
+        for(int r = 0;r<d-1;r++)
+        {
+          std::swap(P(r),P(r+1));
+        }
+      }
+    }
+    unique_rows(sF,uF,I,J);
+    S = VectorXI::Zero(uF.rows(),1);
+    assert(m == J.rows());
+    for(int f = 0;f<m;f++)
+    {
+      S(J(f)) += M(f) ? 1 : -1;
+    }
+  }(MatrixXI(GQ),S,uGQ,sI,sJ);
+  assert(S.rows() == uGQ.rows());
+  const int nq = (S.array().abs()==2).count();
+  GQ.resize(nq,4);
+  {
+    int k = 0;
+    for(int q = 0;q<uGQ.rows();q++)
+    {
+      switch(S(q))
+      {
+#warning "If (V,F) is non-manifold, does this handle all cases?"
+        case -2:
+          GQ.row(k++) = uGQ.row(q).reverse().eval();
+          break;
+        case 2:
+          GQ.row(k++) = uGQ.row(q);
+          break;
+        default:
+        // do not add
+          break;
+      }
+    }
+    assert(nq == k);
+  }
+
+  MatrixXI GG(GT.rows()+2*GQ.rows(),3);
+  GG<< 
+    GT,
+    GQ.col(0), GQ.col(1), GQ.col(2), 
+    GQ.col(0), GQ.col(2), GQ.col(3);
+  J.resize(JT.rows()+2*GQ.rows(),1);
+  J<<JT,DerivedJ::Constant(2*GQ.rows(),1,2*m+1);
+  {
+    DerivedJ SJ;
+    mesh_boolean(
+      WW,GG,
+      Matrix<typename DerivedV::Scalar,Dynamic,Dynamic>(),MatrixXI(),
+      MESH_BOOLEAN_TYPE_UNION,
+      W,G,SJ);
+    J = slice(DerivedJ(J),SJ,1);
+  }
+}

+ 50 - 0
include/igl/copyleft/boolean/minkowski_sum.h

@@ -0,0 +1,50 @@
+#ifndef IGL_COPYLEFT_CGAL_MINKOWSKI_SUM_H
+#define IGL_COPYLEFT_CGAL_MINKOWSKI_SUM_H
+
+#include "../../igl_inline.h"
+#include <Eigen/Core>
+
+namespace igl
+{
+  namespace copyleft
+  {
+    namespace boolean
+    {
+      // Compute the Minkowski sum of a closed triangle mesh (V,F) and a
+      // segment [s,d] in 3D.
+      //
+      // Inputs:
+      //   V  #V by 3 list of mesh vertices in 3D
+      //   F  #F by 3 list of triangle indices into V
+      //   s  segment source endpoint in 3D
+      //   d  segment source endpoint in 3D
+      // Outputs:
+      //   W  #W by 3 list of mesh vertices in 3D
+      //   G  #G by 3 list of triangle indices into W
+      //   J  #G list of indices into [F;#V+F;[s d]] of birth parents
+      //
+      template <
+        typename DerivedV,
+        typename DerivedF,
+        typename Deriveds,
+        typename Derivedd,
+        typename DerivedW,
+        typename DerivedG,
+        typename DerivedJ>
+      IGL_INLINE void minkowski_sum(
+        const Eigen::PlainObjectBase<DerivedV> & V,
+        const Eigen::PlainObjectBase<DerivedF> & F,
+        const Eigen::PlainObjectBase<Deriveds> & s,
+        const Eigen::PlainObjectBase<Derivedd> & d,
+        Eigen::PlainObjectBase<DerivedW> & W,
+        Eigen::PlainObjectBase<DerivedG> & G,
+        Eigen::PlainObjectBase<DerivedJ> & J);
+    }
+  }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "minkowski_sum.cpp"
+#endif
+
+#endif