瀏覽代碼

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 年之前
父節點
當前提交
e76521800e

+ 6 - 0
python/modules/py_typedefs.cpp

@@ -11,3 +11,9 @@ py::class_<RotationList>(m, "RotationList")
     .def("__iter__", [](RotationList &v) {
        return py::make_iterator(v.begin(), v.end());
 }, 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;
-PYBIND11_MAKE_OPAQUE(RotationList);
+PYBIND11_MAKE_OPAQUE(RotationList)
 
 //typedef std::vector<Eigen::Vector3d> 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
   // secs, igl/min_quad_with_fixed.h 7.1 secs
   //)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
   //
   // Inputs:
@@ -78,10 +93,10 @@ const char *__doc_igl_barycentric_coordinates = R"igl_Qu8mg5v7(// Compute baryce
   //   )igl_Qu8mg5v7";
 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
-  // 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
   // wrt v2-v0.
-  // 
+  //
   // Input:
   // V:  #Vx3 Vertices 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:
   // #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
   // set of boundary conditions
   //
@@ -650,12 +666,12 @@ const char *__doc_igl_get_seconds = R"igl_Qu8mg5v7(// Return the current time in
   //    ... // part 2
   //    cout<<"part 2: "<<tictoc()<<endl;
   //    ... // 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";
 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
   // 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:
   //   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)
   // Inputs:
   //   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:
-  //   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";
 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:

+ 1 - 0
python/py_doc.h

@@ -1,4 +1,5 @@
 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_solve;
 extern const char *__doc_igl_avg_edge_length;

+ 2 - 0
python/py_igl.cpp

@@ -8,6 +8,7 @@
 #include <igl/MeshBooleanType.h>
 #include <igl/SolverStatus.h>
 #include <igl/active_set.h>
+#include <igl/adjacency_list.h>
 #include <igl/arap.h>
 #include <igl/avg_edge_length.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_SolverStatus.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_avg_edge_length.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
            SolverStatus
            active_set
+           adjacency_list
            arap
            avg_edge_length
            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
 #
@@ -37,8 +37,7 @@ def get_filepaths(directory):
 
 
 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(prefix, "")
     f_clean = f_clean.replace("/", "_")
@@ -51,7 +50,7 @@ def get_name_from_path(path, basepath, prefix, postfix):
 if __name__ == '__main__':
 
     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)
 
     # List all files in the given folder and subfolders
@@ -109,7 +108,7 @@ if __name__ == '__main__':
             for f in d["functions"]:
                 h_string = "extern const char *__doc_" + namespaces + "_" + f.name + ";\n"
                 docu_string = "See " + f.name + " for the documentation."
-                if f.documentation != "":
+                if f.documentation:
                     docu_string = f.documentation
                 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?
     args = ['-x', 'c++', '-std=c++11', '-fparse-all-comments', '-DIGL_STATIC_LIBRARY']
     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]
     args.extend(incargs)