Эх сурвалжийг харах

Python binding for igl_adjacency_list(const MatrixXi& F, vector<vector<int>& A, bool sorted). Use an opaque type for the output parameter A.

I also fixed a couple of bugs in generate_docstrings.py:
1) We were checking for an empty string where a result could also be Nonn.
2) If you forgot to end the argument path with  a slash, the output would be incorrect
3) We were coupled to a specific version of clang for no reason
4) Python 3 clang binding support was only added in April 2017 (https://reviews.llvm.org/D31568). Using python3 by default will not work on the clang bindings which ship with pip and most Linux distros. Let's change the hashbang to use python2 for now.
5) The documentation for the script was a bit confusing.


Former-commit-id: 230ab71e20a0693d3f54846d79c53bb29af9bed0
Francis Williams 8 жил өмнө
parent
commit
e76521800e

+ 6 - 0
python/modules/py_typedefs.cpp

@@ -11,3 +11,9 @@ py::class_<RotationList>(m, "RotationList")
     .def("__iter__", [](RotationList &v) {
     .def("__iter__", [](RotationList &v) {
        return py::make_iterator(v.begin(), v.end());
        return py::make_iterator(v.begin(), v.end());
 }, py::keep_alive<0, 1>());
 }, py::keep_alive<0, 1>());
+
+
+py::bind_vector<std::vector<int>>(m, "VectorInt");
+py::bind_vector<std::vector<std::vector<int>>>(m, "VectorVectorInt");
+
+

+ 8 - 1
python/modules/py_typedefs.h

@@ -1,5 +1,12 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/stl_bind.h>
+
 typedef std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> > RotationList;
 typedef std::vector<Eigen::Quaterniond,Eigen::aligned_allocator<Eigen::Quaterniond> > RotationList;
-PYBIND11_MAKE_OPAQUE(RotationList);
+PYBIND11_MAKE_OPAQUE(RotationList)
 
 
 //typedef std::vector<Eigen::Vector3d> TranslationList;
 //typedef std::vector<Eigen::Vector3d> TranslationList;
 //PYBIND11_MAKE_OPAQUE(TranslationList);
 //PYBIND11_MAKE_OPAQUE(TranslationList);
+
+PYBIND11_MAKE_OPAQUE(std::vector<int>)
+PYBIND11_MAKE_OPAQUE(std::vector<std::vector<int>>)
+

+ 35 - 17
python/py_doc.cpp

@@ -31,6 +31,21 @@ const char *__doc_igl_active_set = R"igl_Qu8mg5v7(// Known Bugs: rows of [Aeq;Ai
   // Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2
   // Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2
   // secs, igl/min_quad_with_fixed.h 7.1 secs
   // secs, igl/min_quad_with_fixed.h 7.1 secs
   //)igl_Qu8mg5v7";
   //)igl_Qu8mg5v7";
+const char *__doc_igl_adjacency_list = R"igl_Qu8mg5v7(// Constructs the graph adjacency list of a given mesh (V,F)
+  // Templates:
+  //   T  should be a eigen sparse matrix primitive type like int or double
+  // Inputs:
+  //   F       #F by dim list of mesh faces (must be triangles)
+  //   sorted  flag that indicates if the list should be sorted counter-clockwise
+  // Outputs: 
+  //   A  vector<vector<T> > containing at row i the adjacent vertices of vertex i
+  //
+  // Example:
+  //   // Mesh in (V,F)
+  //   vector<vector<double> > A;
+  //   adjacency_list(F,A);
+  //
+  // See also: edges, cotmatrix, diag)igl_Qu8mg5v7";
 const char *__doc_igl_arap_precomputation = R"igl_Qu8mg5v7(// Compute necessary information to start using an ARAP deformation
 const char *__doc_igl_arap_precomputation = R"igl_Qu8mg5v7(// Compute necessary information to start using an ARAP deformation
   //
   //
   // Inputs:
   // Inputs:
@@ -78,10 +93,10 @@ const char *__doc_igl_barycentric_coordinates = R"igl_Qu8mg5v7(// Compute baryce
   //   )igl_Qu8mg5v7";
   //   )igl_Qu8mg5v7";
 const char *__doc_igl_barycentric_to_global = R"igl_Qu8mg5v7(// Converts barycentric coordinates in the embree form to 3D coordinates
 const char *__doc_igl_barycentric_to_global = R"igl_Qu8mg5v7(// Converts barycentric coordinates in the embree form to 3D coordinates
   // Embree stores barycentric coordinates as triples: fid, bc1, bc2
   // Embree stores barycentric coordinates as triples: fid, bc1, bc2
-  // fid is the id of a face, bc1 is the displacement of the point wrt the 
+  // fid is the id of a face, bc1 is the displacement of the point wrt the
   // first vertex v0 and the edge v1-v0. Similarly, bc2 is the displacement
   // first vertex v0 and the edge v1-v0. Similarly, bc2 is the displacement
   // wrt v2-v0.
   // wrt v2-v0.
-  // 
+  //
   // Input:
   // Input:
   // V:  #Vx3 Vertices of the mesh
   // V:  #Vx3 Vertices of the mesh
   // F:  #Fxe Faces of the mesh
   // F:  #Fxe Faces of the mesh
@@ -89,6 +104,7 @@ const char *__doc_igl_barycentric_to_global = R"igl_Qu8mg5v7(// Converts barycen
   //
   //
   // Output:
   // Output:
   // #X: #Xx3 3D coordinates of all points in bc)igl_Qu8mg5v7";
   // #X: #Xx3 3D coordinates of all points in bc)igl_Qu8mg5v7";
+
 const char *__doc_igl_bbw = R"igl_Qu8mg5v7(// Compute Bounded Biharmonic Weights on a given domain (V,Ele) with a given
 const char *__doc_igl_bbw = R"igl_Qu8mg5v7(// Compute Bounded Biharmonic Weights on a given domain (V,Ele) with a given
   // set of boundary conditions
   // set of boundary conditions
   //
   //
@@ -650,12 +666,12 @@ const char *__doc_igl_get_seconds = R"igl_Qu8mg5v7(// Return the current time in
   //    ... // part 2
   //    ... // part 2
   //    cout<<"part 2: "<<tictoc()<<endl;
   //    cout<<"part 2: "<<tictoc()<<endl;
   //    ... // etc)igl_Qu8mg5v7";
   //    ... // etc)igl_Qu8mg5v7";
-const char *__doc_igl_grad = R"igl_Qu8mg5v7(// Gradient of a scalar function defined on piecewise linear elements (mesh)
-  // is constant on each triangle [tetrahedron] i,j,k:
-  // grad(Xijk) = (Xj-Xi) * (Vi - Vk)^R90 / 2A + (Xk-Xi) * (Vj - Vi)^R90 / 2A
-  // where Xi is the scalar value at vertex i, Vi is the 3D position of vertex
-  // i, and A is the area of triangle (i,j,k). ^R90 represent a rotation of
-  // 90 degrees
+const char *__doc_igl_grad = R"igl_Qu8mg5v7(// Gradient of a scalar function defined on piecewise linear elements (mesh)
+  // is constant on each triangle [tetrahedron] i,j,k:
+  // grad(Xijk) = (Xj-Xi) * (Vi - Vk)^R90 / 2A + (Xk-Xi) * (Vj - Vi)^R90 / 2A
+  // where Xi is the scalar value at vertex i, Vi is the 3D position of vertex
+  // i, and A is the area of triangle (i,j,k). ^R90 represent a rotation of
+  // 90 degrees
   //)igl_Qu8mg5v7";
   //)igl_Qu8mg5v7";
 const char *__doc_igl_harmonic = R"igl_Qu8mg5v7(// Compute k-harmonic weight functions "coordinates".
 const char *__doc_igl_harmonic = R"igl_Qu8mg5v7(// Compute k-harmonic weight functions "coordinates".
   //
   //
@@ -839,12 +855,14 @@ const char *__doc_igl_min_quad_with_fixed_precompute = R"igl_Qu8mg5v7(// Known B
   // they're not then resulting probably will no longer be sparse: it will be
   // they're not then resulting probably will no longer be sparse: it will be
   // slow.
   // slow.
   //
   //
-  // MIN_QUAD_WITH_FIXED Minimize quadratic energy 
+  // MIN_QUAD_WITH_FIXED Minimize a quadratic energy of the form
   //
   //
-  // 0.5*Z'*A*Z + Z'*B + C with
+  // trace( 0.5*Z'*A*Z + Z'*B + constant )
   //
   //
-  // constraints that Z(known) = Y, optionally also subject to the constraints
-  // Aeq*Z = Beq
+  // subject to
+  //
+  //   Z(known,:) = Y, and
+  //   Aeq*Z = Beq
   //
   //
   // Templates:
   // Templates:
   //   T  should be a eigen matrix primitive type like int or double
   //   T  should be a eigen matrix primitive type like int or double
@@ -870,12 +888,12 @@ const char *__doc_igl_min_quad_with_fixed_solve = R"igl_Qu8mg5v7(// Solves a sys
   //   DerivedZ  type of Z (e.g. derived from VectorXd or MatrixXd)
   //   DerivedZ  type of Z (e.g. derived from VectorXd or MatrixXd)
   // Inputs:
   // Inputs:
   //   data  factorization struct with all necessary precomputation to solve
   //   data  factorization struct with all necessary precomputation to solve
-  //   B  n by 1 column of linear coefficients
-  //   Y  b by 1 list of constant fixed values
-  //   Beq  m by 1 list of linear equality constraint constant values
+  //   B  n by k column of linear coefficients
+  //   Y  b by k list of constant fixed values
+  //   Beq  m by k list of linear equality constraint constant values
   // Outputs:
   // Outputs:
-  //   Z  n by cols solution
-  //   sol  #unknowns+#lagrange by cols solution to linear system
+  //   Z  n by k solution
+  //   sol  #unknowns+#lagrange by k solution to linear system
   // Returns true on success, false on error)igl_Qu8mg5v7";
   // Returns true on success, false on error)igl_Qu8mg5v7";
 const char *__doc_igl_min_quad_with_fixed = R"igl_Qu8mg5v7(See min_quad_with_fixed for the documentation.)igl_Qu8mg5v7";
 const char *__doc_igl_min_quad_with_fixed = R"igl_Qu8mg5v7(See min_quad_with_fixed for the documentation.)igl_Qu8mg5v7";
 const char *__doc_igl_n_polyvector = R"igl_Qu8mg5v7(// Inputs:
 const char *__doc_igl_n_polyvector = R"igl_Qu8mg5v7(// Inputs:

+ 1 - 0
python/py_doc.h

@@ -1,4 +1,5 @@
 extern const char *__doc_igl_active_set;
 extern const char *__doc_igl_active_set;
+extern const char *__doc_igl_adjacency_list;
 extern const char *__doc_igl_arap_precomputation;
 extern const char *__doc_igl_arap_precomputation;
 extern const char *__doc_igl_arap_solve;
 extern const char *__doc_igl_arap_solve;
 extern const char *__doc_igl_avg_edge_length;
 extern const char *__doc_igl_avg_edge_length;

+ 2 - 0
python/py_igl.cpp

@@ -8,6 +8,7 @@
 #include <igl/MeshBooleanType.h>
 #include <igl/MeshBooleanType.h>
 #include <igl/SolverStatus.h>
 #include <igl/SolverStatus.h>
 #include <igl/active_set.h>
 #include <igl/active_set.h>
+#include <igl/adjacency_list.h>
 #include <igl/arap.h>
 #include <igl/arap.h>
 #include <igl/avg_edge_length.h>
 #include <igl/avg_edge_length.h>
 #include <igl/barycenter.h>
 #include <igl/barycenter.h>
@@ -104,6 +105,7 @@ void python_export_igl(py::module &m)
 #include "py_igl/py_MeshBooleanType.cpp"
 #include "py_igl/py_MeshBooleanType.cpp"
 #include "py_igl/py_SolverStatus.cpp"
 #include "py_igl/py_SolverStatus.cpp"
 #include "py_igl/py_active_set.cpp"
 #include "py_igl/py_active_set.cpp"
+#include "py_igl/py_adjacency_list.cpp"
 #include "py_igl/py_arap.cpp"
 #include "py_igl/py_arap.cpp"
 #include "py_igl/py_avg_edge_length.cpp"
 #include "py_igl/py_avg_edge_length.cpp"
 #include "py_igl/py_barycenter.cpp"
 #include "py_igl/py_barycenter.cpp"

+ 3 - 0
python/py_igl/py_adjacency_list.cpp

@@ -0,0 +1,3 @@
+m.def("adjacency_list", [](const Eigen::MatrixXi& F, std::vector<std::vector<int>>& A, bool sorted) {
+    igl::adjacency_list(F, A, sorted);
+}, py::arg("F"), py::arg("A"), py::arg("sorted")=false);

+ 1 - 0
python/python_shared.cpp

@@ -53,6 +53,7 @@ PYBIND11_PLUGIN(pyigl) {
            MeshBooleanType
            MeshBooleanType
            SolverStatus
            SolverStatus
            active_set
            active_set
+           adjacency_list
            arap
            arap
            avg_edge_length
            avg_edge_length
            barycenter
            barycenter

+ 5 - 6
python/scripts/generate_docstrings.py

@@ -1,6 +1,6 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 #
 #
-#  Syntax: generate_docstrings.py <path_to_c++_header_files> <path_to_python_files>
+#  Syntax: generate_docstrings.py <path to libigl C++ header_files> <path to python binding C++ files>
 #
 #
 #  Extract documentation from C++ header files to use it in libiglPython bindings
 #  Extract documentation from C++ header files to use it in libiglPython bindings
 #
 #
@@ -37,8 +37,7 @@ def get_filepaths(directory):
 
 
 
 
 def get_name_from_path(path, basepath, prefix, postfix):
 def get_name_from_path(path, basepath, prefix, postfix):
-    f_clean = path[len(basepath):]
-    f_clean = f_clean.replace(basepath, "")
+    f_clean = os.path.relpath(path, basepath)
     f_clean = f_clean.replace(postfix, "")
     f_clean = f_clean.replace(postfix, "")
     f_clean = f_clean.replace(prefix, "")
     f_clean = f_clean.replace(prefix, "")
     f_clean = f_clean.replace("/", "_")
     f_clean = f_clean.replace("/", "_")
@@ -51,7 +50,7 @@ def get_name_from_path(path, basepath, prefix, postfix):
 if __name__ == '__main__':
 if __name__ == '__main__':
 
 
     if len(sys.argv) != 3:
     if len(sys.argv) != 3:
-        print('Syntax: %s <path_to_c++_header_files> <path_to_python_files>' % sys.argv[0])
+        print('Syntax: %s generate_docstrings.py <path to libigl C++ header_files> <path to python binding C++ files>' % sys.argv[0])
         exit(-1)
         exit(-1)
 
 
     # List all files in the given folder and subfolders
     # List all files in the given folder and subfolders
@@ -109,7 +108,7 @@ if __name__ == '__main__':
             for f in d["functions"]:
             for f in d["functions"]:
                 h_string = "extern const char *__doc_" + namespaces + "_" + f.name + ";\n"
                 h_string = "extern const char *__doc_" + namespaces + "_" + f.name + ";\n"
                 docu_string = "See " + f.name + " for the documentation."
                 docu_string = "See " + f.name + " for the documentation."
-                if f.documentation != "":
+                if f.documentation:
                     docu_string = f.documentation
                     docu_string = f.documentation
                 cpp_string = "const char *__doc_" + namespaces + "_" + f.name + " = R\"igl_Qu8mg5v7(" + docu_string + ")igl_Qu8mg5v7\";\n"
                 cpp_string = "const char *__doc_" + namespaces + "_" + f.name + " = R\"igl_Qu8mg5v7(" + docu_string + ")igl_Qu8mg5v7\";\n"
 
 

+ 1 - 1
python/scripts/parser.py

@@ -118,7 +118,7 @@ def parse(path):
     # Clang can't parse files with missing definitions, add static library definition or not?
     # Clang can't parse files with missing definitions, add static library definition or not?
     args = ['-x', 'c++', '-std=c++11', '-fparse-all-comments', '-DIGL_STATIC_LIBRARY']
     args = ['-x', 'c++', '-std=c++11', '-fparse-all-comments', '-DIGL_STATIC_LIBRARY']
     args.append('-I/usr/include/eigen3/') # TODO Properly add all needed includes
     args.append('-I/usr/include/eigen3/') # TODO Properly add all needed includes
-    syspath = ccsyspath.system_include_paths('clang++-3.7') # Add the system libraries
+    syspath = ccsyspath.system_include_paths('clang++') # Add the system libraries
     incargs = [(b'-I' + inc).decode("utf-8") for inc in syspath]
     incargs = [(b'-I' + inc).decode("utf-8") for inc in syspath]
     args.extend(incargs)
     args.extend(incargs)