Browse Source

tree evaluation for booleans

Former-commit-id: 829c8a7d8443c0c13267503bfa4fd1a7e62c21f8
Alec Jacobson 9 years ago
parent
commit
05301f8a11

+ 146 - 0
include/igl/boolean/CSGTree.h

@@ -0,0 +1,146 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2015 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/.
+#ifndef IGL_BOOLEAN_CSG_TREE_H
+#define IGL_BOOLEAN_CSG_TREE_H
+
+#include <igl/boolean/string_to_mesh_boolean_type.h>
+#include <igl/boolean/MeshBooleanType.h>
+#include <igl/boolean/mesh_boolean.h>
+
+namespace igl
+{
+  namespace boolean
+  {
+    // Class for defining and computing a constructive solid geometry result
+    // out of a tree of boolean operations on "solid" triangle meshes.
+    template <typename DerivedF>
+    class CSGTree
+    {
+      private:
+        typedef CGAL::Epeck::FT ExactScalar;
+        typedef Eigen::Matrix<ExactScalar,Eigen::Dynamic,3> MatrixX3E;
+        typedef Eigen::PlainObjectBase<DerivedF> POBF;
+        typedef Eigen::Matrix<typename DerivedF::Index,Eigen::Dynamic,1> 
+          VectorJ;
+        // Resulting mesh
+        MatrixX3E m_V;
+        POBF m_F;
+        VectorJ m_J;
+        // Number of birth faces in A + those in B. I.e. sum of original "leaf"
+        // faces involved in result.
+        size_t m_number_of_birth_faces;
+      public:
+        CSGTree()
+        {
+        }
+        //typedef Eigen::MatrixXd MatrixX3E;
+        //typedef Eigen::MatrixXi POBF;
+        // http://stackoverflow.com/a/3279550/148668
+        CSGTree(const CSGTree & other)
+          // copy things
+        {
+        }
+        // copy-swap idiom
+        friend void swap(CSGTree& first, CSGTree& second)
+        {
+          using std::swap;
+        }
+        // Pass-by-value (aka copy)
+        CSGTree& operator=(CSGTree other)
+        {
+          swap(*this,other);
+          return *this;
+        }
+        CSGTree(CSGTree&& other):
+          // initialize via default constructor
+          CSGTree() 
+        {
+          swap(*this,other);
+        }
+        CSGTree(
+          const CSGTree & A,
+          const CSGTree & B,
+          const MeshBooleanType & type)
+        {
+          // conduct boolean operation
+          mesh_boolean(A.V(),A.F(),B.V(),B.F(),type,m_V,m_F,m_J);
+          // reindex m_J
+          std::for_each(m_J.data(),m_J.data()+m_J.size(),
+            [&](typename VectorJ::Scalar & j)
+            {
+              if(j < A.F().rows())
+              {
+                j = A.J()(j);
+              }else
+              {
+                assert(j<(A.F().rows()+B.F().rows()));
+                j = A.number_of_birth_faces()+(B.J()(j-A.F().rows()));
+              }
+            });
+          m_number_of_birth_faces = 
+            A.number_of_birth_faces() + B.number_of_birth_faces();
+        }
+        // Overload using string
+        CSGTree(
+          const CSGTree & A,
+          const CSGTree & B,
+          const std::string & s):
+          CSGTree(A,B,string_to_mesh_boolean_type(s))
+        {
+          // do nothing (all done in constructor).
+        }
+        // "Leaf" node with identity operation
+        template <typename DerivedV>
+        CSGTree(const Eigen::PlainObjectBase<DerivedV> & V, const POBF & F)//:
+        // Possible Eigen bug:
+        // https://forum.kde.org/viewtopic.php?f=74&t=128414
+          //m_V(V.template cast<ExactScalar>()),m_F(F)
+        {
+          m_V = V.template cast<ExactScalar>();
+          m_F = F;
+          // number of faces
+          m_number_of_birth_faces = m_F.rows();
+          // identity birth index
+          m_J = VectorJ::LinSpaced(
+            m_number_of_birth_faces,0,m_number_of_birth_faces-1);
+        }
+        // Returns reference to resulting mesh vertices m_V
+        const MatrixX3E & V() const
+        {
+          return m_V;
+        }
+        // Returns reference to resulting mesh faces m_F
+        template <typename DerivedV>
+        Eigen::PlainObjectBase<DerivedV> cast_V() const
+        {
+          Eigen::PlainObjectBase<DerivedV> dV;
+          dV.resize(m_V.rows(),m_V.cols());
+          for(int i = 0;i<m_V.size();i++)
+          {
+            *(dV.data()+i) = CGAL::to_double((*(m_V.data()+i)));
+          }
+          return dV;
+        }
+        const POBF & F() const
+        {
+          return m_F;
+        }
+        const VectorJ & J() const
+        {
+          return m_J;
+        }
+        const size_t & number_of_birth_faces() const
+        {
+          return m_number_of_birth_faces;
+        }
+    };
+  }
+}
+
+
+#endif

+ 6 - 4
include/igl/boolean/mesh_boolean.cpp

@@ -105,7 +105,7 @@ IGL_INLINE void igl::boolean::mesh_boolean(
   using namespace igl::cgal;
   MeshBooleanType eff_type = type;
   // Concatenate A and B into a single mesh
-  typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
+  typedef CGAL::Epeck Kernel;
   typedef Kernel::FT ExactScalar;
   typedef typename DerivedVC::Scalar Scalar;
   typedef typename DerivedFC::Scalar Index;
@@ -178,7 +178,9 @@ IGL_INLINE void igl::boolean::mesh_boolean(
     CV.resize(EV.rows(), EV.cols());
     std::transform(EV.data(), EV.data() + EV.rows()*EV.cols(),
             CV.data(), [&](ExactScalar val) {
-            return CGAL::to_double(val);
+            Scalar c;
+            assign_scalar(val,c);
+            return c;
             });
   }
 
@@ -248,7 +250,7 @@ IGL_INLINE void igl::boolean::mesh_boolean(
   }
 #ifdef IGL_MESH_BOOLEAN_DEBUG
   {
-    MatrixXd O;
+    MatrixXi O;
     boundary_facets(FC,O);
     cout<<"# boundary: "<<O.rows()<<endl;
   }
@@ -334,7 +336,7 @@ IGL_INLINE void igl::boolean::mesh_boolean(
   //FC = G;
 #ifdef IGL_MESH_BOOLEAN_DEBUG
   {
-    MatrixXd O;
+    MatrixXi O;
     boundary_facets(FC,O);
     cout<<"# boundary: "<<O.rows()<<endl;
   }

+ 48 - 0
include/igl/boolean/string_to_mesh_boolean_type.cpp

@@ -0,0 +1,48 @@
+#include "string_to_mesh_boolean_type.h"
+#include <algorithm>
+#include <cassert>
+#include <vector>
+
+IGL_INLINE bool igl::boolean::string_to_mesh_boolean_type( 
+  const std::string & s,
+  MeshBooleanType & type)
+{
+  using namespace std;
+  string eff_s = s;
+  transform(eff_s.begin(), eff_s.end(), eff_s.begin(), ::tolower);
+  const auto & find_any = 
+    [](const vector<string> & haystack, const string & needle)->bool
+  {
+    return find(haystack.begin(), haystack.end(), needle) != haystack.end();
+  };
+  if(find_any({"union","unite","u","∪"},eff_s))
+  {
+    type = MESH_BOOLEAN_TYPE_UNION;
+  }else if(find_any({"intersect","intersection","i","∩"},eff_s))
+  {
+    type = MESH_BOOLEAN_TYPE_INTERSECT;
+  }else if(
+    find_any({"minus","subtract","difference","relative complement","m","\\"},eff_s))
+  {
+    type = MESH_BOOLEAN_TYPE_MINUS;
+  }else if(find_any({"xor","symmetric difference","x","∆"},eff_s))
+  {
+    type = MESH_BOOLEAN_TYPE_XOR;
+  }else if(find_any({"resolve"},eff_s))
+  {
+    type = MESH_BOOLEAN_TYPE_RESOLVE;
+  }else
+  {
+    return false;
+  }
+  return true;
+}
+
+IGL_INLINE igl::boolean::MeshBooleanType igl::boolean::string_to_mesh_boolean_type( 
+  const std::string & s)
+{
+  MeshBooleanType type;
+  const bool ret = string_to_mesh_boolean_type(s,type);
+  assert(ret && "Unknown MeshBooleanType name");
+  return type;
+}

+ 41 - 0
include/igl/boolean/string_to_mesh_boolean_type.h

@@ -0,0 +1,41 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+// 
+// Copyright (C) 2015 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/.
+#ifndef IGL_BOOLEAN_STRING_TO_MESH_BOOLEAN_H
+#define IGL_BOOLEAN_STRING_TO_MESH_BOOLEAN_H
+
+#include <igl/igl_inline.h>
+#include "MeshBooleanType.h"
+#include <string>
+
+namespace igl
+{
+  namespace boolean
+  {
+    // Convert string to boolean type
+    //
+    //  Inputs:
+    //    s  string identifying type, one of the following:
+    //      "union","intersect","minus","xor","resolve"
+    //  Outputs:
+    //    type  type of boolean operation
+    // Returns true only on success
+    //     
+    IGL_INLINE bool string_to_mesh_boolean_type(
+      const std::string & s,
+      MeshBooleanType & type);
+    // Returns type without error handling
+    IGL_INLINE MeshBooleanType string_to_mesh_boolean_type(
+      const std::string & s);
+  }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "string_to_mesh_boolean_type.cpp"
+#endif
+
+#endif

+ 7 - 0
include/igl/cgal/assign_scalar.cpp

@@ -20,3 +20,10 @@ IGL_INLINE void igl::cgal::assign_scalar(
 {
   d = CGAL::to_double(cgal);
 }
+
+IGL_INLINE void igl::cgal::assign_scalar(
+  const double & c,
+  double & d)
+{
+  d = c;
+}

+ 3 - 0
include/igl/cgal/assign_scalar.h

@@ -23,6 +23,9 @@ namespace igl
     IGL_INLINE void assign_scalar(
       const typename CGAL::Epeck::FT & cgal,
       double & d);
+    IGL_INLINE void assign_scalar(
+      const double & c,
+      double & d);
   }
 }
 #ifndef IGL_STATIC_LIBRARY

+ 15 - 3
include/igl/cgal/mesh_to_cgal_triangle_list.cpp

@@ -6,6 +6,7 @@
 // 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 "mesh_to_cgal_triangle_list.h"
+#include "assign_scalar.h"
 
 #include <cassert>
 
@@ -22,6 +23,17 @@ IGL_INLINE void igl::cgal::mesh_to_cgal_triangle_list(
   typedef CGAL::Triangle_3<Kernel> Triangle_3; 
   // Must be 3D
   assert(V.cols() == 3);
+  // **Copy** to convert to output type (this is especially/only needed if the
+  // input type DerivedV::Scalar is CGAL::Epeck
+  Eigen::Matrix<
+    typename Kernel::FT,
+    DerivedV::RowsAtCompileTime,
+    DerivedV::ColsAtCompileTime> 
+    KV(V.rows(),V.cols());
+  for(int i = 0;i<V.size();i++)
+  {
+    assign_scalar(*(V.data()+i),*(KV.data()+i));
+  }
   // Must be triangles
   assert(F.cols() == 3);
   T.reserve(F.rows());
@@ -30,9 +42,9 @@ IGL_INLINE void igl::cgal::mesh_to_cgal_triangle_list(
   {
     T.push_back(
       Triangle_3(
-        Point_3( V(F(f,0),0), V(F(f,0),1), V(F(f,0),2)),
-        Point_3( V(F(f,1),0), V(F(f,1),1), V(F(f,1),2)),
-        Point_3( V(F(f,2),0), V(F(f,2),1), V(F(f,2),2))));
+        Point_3( KV(F(f,0),0), KV(F(f,0),1), KV(F(f,0),2)),
+        Point_3( KV(F(f,1),0), KV(F(f,1),1), KV(F(f,1),2)),
+        Point_3( KV(F(f,2),0), KV(F(f,2),1), KV(F(f,2),2))));
   }
 }
 

+ 5 - 1
include/igl/cgal/remesh_intersections.cpp

@@ -274,7 +274,11 @@ IGL_INLINE void igl::cgal::remesh_intersections(
   // incoming reps are different then the points are unique.
   for(Index v = 0;v<V.rows();v++)
   {
-    const Point_3 p(V(v,0),V(v,1),V(v,2));
+    typename Kernel::FT p0,p1,p2;
+    assign_scalar(V(v,0),p0);
+    assign_scalar(V(v,1),p1);
+    assign_scalar(V(v,2),p2);
+    const Point_3 p(p0,p1,p2);
     if(vv2i.count(p)==0)
     {
       vv2i[p] = v;