diff --git a/include/igl/hsv_to_rgb.cpp b/include/igl/hsv_to_rgb.cpp index 09edacdb6..8f8e815ac 100644 --- a/include/igl/hsv_to_rgb.cpp +++ b/include/igl/hsv_to_rgb.cpp @@ -1,9 +1,9 @@ // This file is part of libigl, a simple c++ geometry processing library. -// +// // Copyright (C) 2013 Alec Jacobson -// -// 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 +// +// 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 "hsv_to_rgb.h" #include @@ -12,21 +12,21 @@ template IGL_INLINE void igl::hsv_to_rgb(const T * hsv, T * rgb) { - igl::hsv_to_rgb( + igl::hsv_to_rgb( hsv[0],hsv[1],hsv[2], rgb[0],rgb[1],rgb[2]); } template -IGL_INLINE void igl::hsv_to_rgb( - const T & h, const T & s, const T & v, +IGL_INLINE void igl::hsv_to_rgb( + const T & h, const T & s, const T & v, T & r, T & g, T & b) { // From medit double f,p,q,t,hh; int i; hh = ((int)h % 360) / 60.; - i = (int)floor(hh); /* largest int <= h */ + i = (int)std::floor(hh); /* largest int <= h */ f = hh - i; /* fractional part of h */ p = v * (1.0 - s); q = v * (1.0 - (s * f)); diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index bd3d7ef6e..f8b135b95 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -67,6 +67,8 @@ option(LIBIGL_WITH_TETGEN "Use Tetgen" ON) option(LIBIGL_WITH_TRIANGLE "Use Triangle" ON) option(LIBIGL_WITH_XML "Use XML" ON) option(LIBIGL_WITH_PYTHON "Use Python" ON) +option(LIBIGL_WITH_COPYLEFT "Use Copyleft" ON) + if(LIBIGL_WITH_CGAL) # Do not remove or move this block, cgal strange build system fails without it find_package(CGAL REQUIRED) @@ -113,6 +115,11 @@ if (LIBIGL_WITH_CGAL) list(APPEND SHARED_SOURCES "modules/copyleft/py_igl_cgal.cpp") endif () +if (LIBIGL_WITH_COPYLEFT) + add_definitions(-DPY_COPYLEFT) + list(APPEND SHARED_SOURCES "modules/copyleft/py_igl_copyleft.cpp") +endif () + if (LIBIGL_WITH_PNG) add_definitions(-DPY_PNG) list(APPEND SHARED_SOURCES "modules/py_igl_png.cpp") diff --git a/python/modules/copyleft/py_igl_copyleft.cpp b/python/modules/copyleft/py_igl_copyleft.cpp new file mode 100644 index 000000000..7bb0bb6e3 --- /dev/null +++ b/python/modules/copyleft/py_igl_copyleft.cpp @@ -0,0 +1,20 @@ +//#include +//#include +//#include + + +#include "../../python_shared.h" + +#include +#include + + +void python_export_igl_copyleft(py::module &me) { + + py::module m = me.def_submodule( + "copyleft", "Wrappers for libigl functions that are copyleft"); + + #include "../../py_igl/copyleft/py_marching_cubes.cpp" + #include "../../py_igl/copyleft/py_swept_volume.cpp" + +} diff --git a/python/modules/py_igl_embree.cpp b/python/modules/py_igl_embree.cpp index 523636a2a..412021594 100644 --- a/python/modules/py_igl_embree.cpp +++ b/python/modules/py_igl_embree.cpp @@ -6,6 +6,7 @@ #include "../python_shared.h" #include +#include void python_export_igl_embree(py::module &me) { @@ -14,5 +15,6 @@ void python_export_igl_embree(py::module &me) { "embree", "Wrappers for libigl functions that use embree"); #include "../py_igl/embree/py_ambient_occlusion.cpp" + #include "../py_igl/embree/py_reorient_facets_raycast.cpp" } diff --git a/python/modules/py_vector.cpp b/python/modules/py_vector.cpp index 7ec0dc8fb..d740f601f 100644 --- a/python/modules/py_vector.cpp +++ b/python/modules/py_vector.cpp @@ -180,6 +180,7 @@ py::class_ bind_eigen_2(py::module &m, const char *name, .def("rowwiseMean", [](const Type &m) {return Type(m.rowwise().mean());} ) .def("rowwiseNorm", [](const Type &m) {return Type(m.rowwise().norm());} ) .def("rowwiseNormalized", [](const Type &m) {return Type(m.rowwise().normalized());} ) + .def("rowwiseReverse", [](const Type &m) {return Type(m.rowwise().reverse());} ) .def("rowwiseMinCoeff", [](const Type &m) {return Type(m.rowwise().minCoeff());} ) .def("rowwiseMaxCoeff", [](const Type &m) {return Type(m.rowwise().maxCoeff());} ) @@ -188,6 +189,8 @@ py::class_ bind_eigen_2(py::module &m, const char *name, .def("colwiseProd", [](const Type &m) {return Type(m.colwise().prod());} ) .def("colwiseMean", [](const Type &m) {return Type(m.colwise().mean());} ) .def("colwiseNorm", [](const Type &m) {return Type(m.colwise().norm());} ) + .def("colwiseNormalized", [](const Type &m) {return Type(m.colwise().normalized());} ) + .def("colwiseReverse", [](const Type &m) {return Type(m.colwise().reverse());} ) .def("colwiseMinCoeff", [](const Type &m) {return Type(m.colwise().minCoeff());} ) .def("colwiseMaxCoeff", [](const Type &m) {return Type(m.colwise().maxCoeff());} ) @@ -689,6 +692,25 @@ void python_export_vector(py::module &m) { .def("solve",[](const Eigen::SimplicialLLT >& s, const Eigen::MatrixXd& rhs) { return Eigen::MatrixXd(s.solve(rhs)); }) ; + py::class_ affine3d(me, "Affine3d"); + + affine3d + .def(py::init<>()) + .def("setIdentity",[](Eigen::Affine3d& a){ + return a.setIdentity(); + }) + .def("rotate",[](Eigen::Affine3d& a, double angle, Eigen::MatrixXd axis) { + assert_is_Vector3("axis", axis); + return a.rotate(Eigen::AngleAxisd(angle, Eigen::Vector3d(axis))); + }) + .def("translate",[](Eigen::Affine3d& a, Eigen::MatrixXd offset) { + assert_is_Vector3("offset", offset); + return a.translate(Eigen::Vector3d(offset)); + }) + .def("matrix", [](Eigen::Affine3d& a) -> Eigen::MatrixXd { + return Eigen::MatrixXd(a.matrix()); + }) + ; /* Bindings for Quaterniond*/ //py::class_ quaterniond(me, "Quaterniond"); // diff --git a/python/py_doc.cpp b/python/py_doc.cpp index a2b867ee8..2040d0dea 100644 --- a/python/py_doc.cpp +++ b/python/py_doc.cpp @@ -112,6 +112,7 @@ const char *__doc_igl_cat = R"igl_Qu8mg5v7(// Perform concatenation of a two mat // Outputs: // C output matrix // )igl_Qu8mg5v7"; +const char *__doc_igl_collapse_edge = R"igl_Qu8mg5v7(See collapse_edge for the documentation.)igl_Qu8mg5v7"; const char *__doc_igl_colon = R"igl_Qu8mg5v7(// Colon operator like matlab's colon operator. Enumerats values between low // and hi with step step. // Templates: @@ -219,6 +220,45 @@ const char *__doc_igl_copyleft_comiso_nrosy = R"igl_Qu8mg5v7(// Generate a N-RoS // Outputs: // R #F by 3 the representative vectors of the interpolated field // S #V by 1 the singularity index for each vertex (0 = regular))igl_Qu8mg5v7"; +const char *__doc_igl_copyleft_marching_cubes = R"igl_Qu8mg5v7(// marching_cubes( values, points, x_res, y_res, z_res, vertices, faces ) + // + // performs marching cubes reconstruction on the grid defined by values, and + // points, and generates vertices and faces + // + // Input: + // values #number_of_grid_points x 1 array -- the scalar values of an + // implicit function defined on the grid points (<0 in the inside of the + // surface, 0 on the border, >0 outside) + // points #number_of_grid_points x 3 array -- 3-D positions of the grid + // points, ordered in x,y,z order: + // points[index] = the point at (x,y,z) where : + // x = (index % (xres -1), + // y = (index / (xres-1)) %(yres-1), + // z = index / (xres -1) / (yres -1) ). + // where x,y,z index x, y, z dimensions + // i.e. index = x + y*xres + z*xres*yres + // xres resolutions of the grid in x dimension + // yres resolutions of the grid in y dimension + // zres resolutions of the grid in z dimension + // Output: + // vertices #V by 3 list of mesh vertex positions + // faces #F by 3 list of mesh triangle indices + //)igl_Qu8mg5v7"; +const char *__doc_igl_copyleft_swept_volume = R"igl_Qu8mg5v7(// Compute the surface of the swept volume of a solid object with surface + // (V,F) mesh under going rigid motion. + // + // Inputs: + // V #V by 3 list of mesh positions in reference pose + // F #F by 3 list of mesh indices into V + // transform function handle so that transform(t) returns the rigid + // transformation at time t∈[0,1] + // steps number of time steps: steps=3 --> t∈{0,0.5,1} + // grid_res number of grid cells on the longest side containing the + // motion (isolevel+1 cells will also be added on each side as padding) + // isolevel distance level to be contoured as swept volume + // Outputs: + // SV #SV by 3 list of mesh positions of the swept surface + // SF #SF by 3 list of mesh faces into SV)igl_Qu8mg5v7"; const char *__doc_igl_copyleft_tetgen_tetrahedralize = R"igl_Qu8mg5v7(// Mesh the interior of a surface mesh (V,F) using tetgen // // Inputs: @@ -355,6 +395,23 @@ const char *__doc_igl_embree_ambient_occlusion = R"igl_Qu8mg5v7(// Compute ambie // S #P list of ambient occlusion values between 1 (fully occluded) and // 0 (not occluded) //)igl_Qu8mg5v7"; +const char *__doc_igl_embree_reorient_facets_raycast = R"igl_Qu8mg5v7(// Orient each component (identified by C) of a mesh (V,F) using ambient + // occlusion such that the front side is less occluded than back side, as + // described in "A Simple Method for Correcting Facet Orientations in + // Polygon Meshes Based on Ray Casting" [Takayama et al. 2014]. + // + // Inputs: + // V #V by 3 list of vertex positions + // F #F by 3 list of triangle indices + // rays_total Total number of rays that will be shot + // rays_minimum Minimum number of rays that each patch should receive + // facet_wise Decision made for each face independently, no use of patches + // (i.e., each face is treated as a patch) + // use_parity Use parity mode + // is_verbose Verbose output to cout + // Outputs: + // I #F list of whether face has been flipped + // C #F list of patch ID (output of bfs_orient > manifold patches))igl_Qu8mg5v7"; const char *__doc_igl_find_cross_field_singularities = R"igl_Qu8mg5v7(// Inputs: // V #V by 3 eigen Matrix of mesh vertex 3D positions // F #F by 3 eigen Matrix of face (quad) indices @@ -402,6 +459,22 @@ const char *__doc_igl_gaussian_curvature = R"igl_Qu8mg5v7(// Compute discrete lo // Output: // K #V by 1 eigen Matrix of discrete gaussian curvature values //)igl_Qu8mg5v7"; +const char *__doc_igl_get_seconds = R"igl_Qu8mg5v7(// Return the current time in seconds since program start + // + // Example: + // const auto & tictoc = []() + // { + // static double t_start = igl::get_seconds(); + // double diff = igl::get_seconds()-t_start; + // t_start += diff; + // return diff; + // }; + // tictoc(); + // ... // part 1 + // cout<<"part 1: "< + //IGL_INLINE void winding_number_3( + // const double * V, + // const int n, + // const DerivedF * F, + // const int m, + // const double * O, + // double * S); + // 2d)igl_Qu8mg5v7"; const char *__doc_igl_writeMESH = R"igl_Qu8mg5v7(// save a tetrahedral volume mesh to a .mesh file // // Templates: diff --git a/python/py_doc.h b/python/py_doc.h index 9c6bf447e..1f5c829da 100644 --- a/python/py_doc.h +++ b/python/py_doc.h @@ -7,6 +7,7 @@ extern const char *__doc_igl_barycentric_coordinates; extern const char *__doc_igl_boundary_facets; extern const char *__doc_igl_boundary_loop; extern const char *__doc_igl_cat; +extern const char *__doc_igl_collapse_edge; extern const char *__doc_igl_colon; extern const char *__doc_igl_comb_cross_field; extern const char *__doc_igl_comb_frame_field; @@ -14,6 +15,8 @@ extern const char *__doc_igl_compute_frame_field_bisectors; extern const char *__doc_igl_copyleft_cgal_mesh_boolean; extern const char *__doc_igl_copyleft_comiso_miq; extern const char *__doc_igl_copyleft_comiso_nrosy; +extern const char *__doc_igl_copyleft_marching_cubes; +extern const char *__doc_igl_copyleft_swept_volume; extern const char *__doc_igl_copyleft_tetgen_tetrahedralize; extern const char *__doc_igl_cotmatrix; extern const char *__doc_igl_covariance_scatter_matrix; @@ -25,14 +28,17 @@ extern const char *__doc_igl_doublearea_quad; extern const char *__doc_igl_edge_lengths; extern const char *__doc_igl_eigs; extern const char *__doc_igl_embree_ambient_occlusion; +extern const char *__doc_igl_embree_reorient_facets_raycast; extern const char *__doc_igl_find_cross_field_singularities; extern const char *__doc_igl_fit_rotations; extern const char *__doc_igl_fit_rotations_planar; extern const char *__doc_igl_fit_rotations_SSE; extern const char *__doc_igl_floor; extern const char *__doc_igl_gaussian_curvature; +extern const char *__doc_igl_get_seconds; extern const char *__doc_igl_grad; extern const char *__doc_igl_harmonic; +extern const char *__doc_igl_hsv_to_rgb; extern const char *__doc_igl_internal_angles; extern const char *__doc_igl_invert_diag; extern const char *__doc_igl_is_irregular_vertex; @@ -58,6 +64,7 @@ extern const char *__doc_igl_point_mesh_squared_distance; extern const char *__doc_igl_polar_svd; extern const char *__doc_igl_principal_curvature; extern const char *__doc_igl_quad_planarity; +extern const char *__doc_igl_randperm; extern const char *__doc_igl_readDMAT; extern const char *__doc_igl_readMESH; extern const char *__doc_igl_readOBJ; @@ -78,5 +85,8 @@ extern const char *__doc_igl_unique; extern const char *__doc_igl_unique_rows; extern const char *__doc_igl_unproject_onto_mesh; extern const char *__doc_igl_upsample; +extern const char *__doc_igl_winding_number; +extern const char *__doc_igl_winding_number_3; +extern const char *__doc_igl_winding_number_2; extern const char *__doc_igl_writeMESH; extern const char *__doc_igl_writeOBJ; diff --git a/python/py_igl.cpp b/python/py_igl.cpp index 9f7b250f3..37bb2919d 100644 --- a/python/py_igl.cpp +++ b/python/py_igl.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -29,8 +30,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -51,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +71,7 @@ #include #include #include +#include #include #include @@ -85,6 +90,7 @@ void python_export_igl(py::module &m) #include "py_igl/py_boundary_facets.cpp" #include "py_igl/py_boundary_loop.cpp" #include "py_igl/py_cat.cpp" +#include "py_igl/py_collapse_edge.cpp" #include "py_igl/py_colon.cpp" #include "py_igl/py_comb_cross_field.cpp" #include "py_igl/py_comb_frame_field.cpp" @@ -100,8 +106,10 @@ void python_export_igl(py::module &m) #include "py_igl/py_fit_rotations.cpp" #include "py_igl/py_floor.cpp" #include "py_igl/py_gaussian_curvature.cpp" +#include "py_igl/py_get_seconds.cpp" #include "py_igl/py_grad.cpp" #include "py_igl/py_harmonic.cpp" +#include "py_igl/py_hsv_to_rgb.cpp" #include "py_igl/py_internal_angles.cpp" #include "py_igl/py_invert_diag.cpp" #include "py_igl/py_is_irregular_vertex.cpp" @@ -122,6 +130,7 @@ void python_export_igl(py::module &m) #include "py_igl/py_polar_svd.cpp" #include "py_igl/py_principal_curvature.cpp" #include "py_igl/py_quad_planarity.cpp" +#include "py_igl/py_randperm.cpp" #include "py_igl/py_readDMAT.cpp" #include "py_igl/py_readMESH.cpp" #include "py_igl/py_readOBJ.cpp" @@ -138,6 +147,7 @@ void python_export_igl(py::module &m) #include "py_igl/py_unique.cpp" #include "py_igl/py_unproject_onto_mesh.cpp" #include "py_igl/py_upsample.cpp" +#include "py_igl/py_winding_number.cpp" #include "py_igl/py_writeMESH.cpp" #include "py_igl/py_writeOBJ.cpp" diff --git a/python/py_igl/copyleft/py_marching_cubes.cpp b/python/py_igl/copyleft/py_marching_cubes.cpp new file mode 100644 index 000000000..4aac4f2d0 --- /dev/null +++ b/python/py_igl/copyleft/py_marching_cubes.cpp @@ -0,0 +1,21 @@ + + +m.def("marching_cubes", [] +( + const Eigen::MatrixXd& values, + const Eigen::MatrixXd& points, + const unsigned int x_res, + const unsigned int y_res, + const unsigned int z_res, + Eigen::MatrixXd& vertices, + Eigen::MatrixXi& faces +) +{ + assert_is_VectorX("values", values); + Eigen::VectorXd valuesv; + if (values.size() != 0) + valuesv = values; + return igl::copyleft::marching_cubes(valuesv, points, x_res, y_res, z_res, vertices, faces); +}, __doc_igl_copyleft_marching_cubes, +py::arg("values"), py::arg("points"), py::arg("x_res"), py::arg("y_res"), py::arg("z_res"), py::arg("vertices"), py::arg("faces")); + diff --git a/python/py_igl/copyleft/py_swept_volume.cpp b/python/py_igl/copyleft/py_swept_volume.cpp new file mode 100644 index 000000000..e9214d960 --- /dev/null +++ b/python/py_igl/copyleft/py_swept_volume.cpp @@ -0,0 +1,19 @@ + +m.def("swept_volume", [] +( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const std::function & transform, + const size_t steps, + const size_t grid_res, + const size_t isolevel, + Eigen::MatrixXd& SV, + Eigen::MatrixXi& SF +) +{ + return igl::copyleft::swept_volume(V, F, transform, steps, grid_res, isolevel, SV, SF); +}, __doc_igl_copyleft_swept_volume, +py::arg("V"), py::arg("F"), py::arg("transform"), py::arg("steps"), py::arg("grid_res"), py::arg("isolevel"), py::arg("SV"), py::arg("SF")); + + + diff --git a/python/py_igl/embree/py_reorient_facets_raycast.cpp b/python/py_igl/embree/py_reorient_facets_raycast.cpp new file mode 100644 index 000000000..04e3570c1 --- /dev/null +++ b/python/py_igl/embree/py_reorient_facets_raycast.cpp @@ -0,0 +1,37 @@ + + +m.def("reorient_facets_raycast", [] +( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + int rays_total, + int rays_minimum, + bool facet_wise, + bool use_parity, + bool is_verbose, + Eigen::MatrixXi& I, + Eigen::MatrixXi& C +) +{ + Eigen::VectorXi Iv; + Eigen::VectorXi Cv; + igl::embree::reorient_facets_raycast(V, F, rays_total, rays_minimum, facet_wise, use_parity, is_verbose, Iv, Cv); + I = Iv; + C = Cv; +}, __doc_igl_embree_reorient_facets_raycast, +py::arg("V"), py::arg("F"), py::arg("rays_total"), py::arg("rays_minimum"), py::arg("facet_wise"), py::arg("use_parity"), py::arg("is_verbose"), py::arg("I"), py::arg("C")); + +m.def("reorient_facets_raycast", [] +( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + Eigen::MatrixXi& FF, + Eigen::MatrixXi& I +) +{ + Eigen::VectorXi Iv; + igl::embree::reorient_facets_raycast(V, F, FF, Iv); + I = Iv; +}, __doc_igl_embree_reorient_facets_raycast, +py::arg("V"), py::arg("F"), py::arg("FF"), py::arg("I")); + diff --git a/python/py_igl/py_collapse_edge.cpp b/python/py_igl/py_collapse_edge.cpp new file mode 100644 index 000000000..a4416802a --- /dev/null +++ b/python/py_igl/py_collapse_edge.cpp @@ -0,0 +1,87 @@ +// COMPLETE BINDINGS ======================== + + + + + + +// INCOMPLETE BINDINGS ======================== + + +//m.def("collapse_edge", [] +//( +// int e, +// Eigen::RowVectorXd & p, +// Eigen::MatrixXd& V, +// Eigen::MatrixXi& F, +// Eigen::MatrixXi& E, +// Eigen::MatrixXi& EMAP, +// Eigen::MatrixXi& EF, +// Eigen::MatrixXi& EI, +// int & e1, +// int & e2, +// int & f1, +// int & f2 +//) +//{ +// return igl::collapse_edge(e, p, V, F, E, EMAP, EF, EI, e1, e2, f1, f2); +//}, __doc_igl_collapse_edge, +//py::arg("e"), py::arg("p"), py::arg("V"), py::arg("F"), py::arg("E"), py::arg("EMAP"), py::arg("EF"), py::arg("EI"), py::arg("e1"), py::arg("e2"), py::arg("f1"), py::arg("f2")); + +//m.def("collapse_edge", [] +//( +// int e, +// Eigen::RowVectorXd & p, +// Eigen::MatrixXd& V, +// Eigen::MatrixXi& F, +// Eigen::MatrixXi& E, +// Eigen::MatrixXi& EMAP, +// Eigen::MatrixXi& EF, +// Eigen::MatrixXi& EI +//) +//{ +// return igl::collapse_edge(e, p, V, F, E, EMAP, EF, EI); +//}, __doc_igl_collapse_edge, +//py::arg("e"), py::arg("p"), py::arg("V"), py::arg("F"), py::arg("E"), py::arg("EMAP"), py::arg("EF"), py::arg("EI")); + +//m.def("collapse_edge", [] +//( +// std::function & cost_and_placement, +// Eigen::MatrixXd& V, +// Eigen::MatrixXi& F, +// Eigen::MatrixXi& E, +// Eigen::MatrixXi& EMAP, +// Eigen::MatrixXi& EF, +// Eigen::MatrixXi& EI, +// std::set > & Q, +// std::vector >::iterator> & Qit, +// Eigen::MatrixXd& C +//) +//{ +// return igl::collapse_edge(cost_and_placement, V, F, E, EMAP, EF, EI, Q, Qit, C); +//}, __doc_igl_collapse_edge, +//py::arg("cost_and_placement"), py::arg("V"), py::arg("F"), py::arg("E"), py::arg("EMAP"), py::arg("EF"), py::arg("EI"), py::arg("Q"), py::arg("Qit"), py::arg("C")); + +//m.def("collapse_edge", [] +//( +// std::function & cost_and_placement, +// Eigen::MatrixXd& V, +// Eigen::MatrixXi& F, +// Eigen::MatrixXi& E, +// Eigen::MatrixXi& EMAP, +// Eigen::MatrixXi& EF, +// Eigen::MatrixXi& EI, +// std::set > & Q, +// std::vector >::iterator> & Qit, +// Eigen::MatrixXd& C, +// int & e, +// int & e1, +// int & e2, +// int & f1, +// int & f2 +//) +//{ +// return igl::collapse_edge(cost_and_placement, V, F, E, EMAP, EF, EI, Q, Qit, C, e, e1, e2, f1, f2); +//}, __doc_igl_collapse_edge, +//py::arg("cost_and_placement"), py::arg("V"), py::arg("F"), py::arg("E"), py::arg("EMAP"), py::arg("EF"), py::arg("EI"), py::arg("Q"), py::arg("Qit"), py::arg("C"), py::arg("e"), py::arg("e1"), py::arg("e2"), py::arg("f1"), py::arg("f2")); + diff --git a/python/py_igl/py_get_seconds.cpp b/python/py_igl/py_get_seconds.cpp new file mode 100644 index 000000000..bb96c7fc3 --- /dev/null +++ b/python/py_igl/py_get_seconds.cpp @@ -0,0 +1,6 @@ +m.def("get_seconds", [] +() +{ + return igl::get_seconds(); +}, __doc_igl_get_seconds); + diff --git a/python/py_igl/py_hsv_to_rgb.cpp b/python/py_igl/py_hsv_to_rgb.cpp new file mode 100644 index 000000000..e9c8e7101 --- /dev/null +++ b/python/py_igl/py_hsv_to_rgb.cpp @@ -0,0 +1,44 @@ +// COMPLETE BINDINGS ======================== + + +m.def("hsv_to_rgb", [] +( + const Eigen::MatrixXd& H, + Eigen::MatrixXd& R +) +{ + return igl::hsv_to_rgb(H, R); +}, __doc_igl_hsv_to_rgb, +py::arg("H"), py::arg("R")); + + + + + +// INCOMPLETE BINDINGS ======================== + + +//m.def("hsv_to_rgb", [] +//( +// T * hsv, +// T * rgb +//) +//{ +// return igl::hsv_to_rgb(hsv, rgb); +//}, __doc_igl_hsv_to_rgb, +//py::arg("hsv"), py::arg("rgb")); + +//m.def("hsv_to_rgb", [] +//( +// T & h, +// T & s, +// T & v, +// T & r, +// T & g, +// T & b +//) +//{ +// return igl::hsv_to_rgb(h, s, v, r, g, b); +//}, __doc_igl_hsv_to_rgb, +//py::arg("h"), py::arg("s"), py::arg("v"), py::arg("r"), py::arg("g"), py::arg("b")); + diff --git a/python/py_igl/py_randperm.cpp b/python/py_igl/py_randperm.cpp new file mode 100644 index 000000000..010044385 --- /dev/null +++ b/python/py_igl/py_randperm.cpp @@ -0,0 +1,10 @@ +m.def("randperm", [] +( + int n, + Eigen::MatrixXi& I +) +{ + return igl::randperm(n, I); +}, __doc_igl_randperm, +py::arg("n"), py::arg("I")); + diff --git a/python/py_igl/py_slice_tets.cpp b/python/py_igl/py_slice_tets.cpp index 5f58372d8..3176a6d9c 100644 --- a/python/py_igl/py_slice_tets.cpp +++ b/python/py_igl/py_slice_tets.cpp @@ -6,15 +6,15 @@ m.def("slice_tets", [] Eigen::MatrixXd& U, Eigen::MatrixXi& G, Eigen::MatrixXi& J, - Eigen::SparseMatrix & BC + Eigen::SparseMatrix& BC ) { assert_is_VectorX("plane", plane); - Eigen::VectorXd pl; + Eigen::VectorXd planev; if (plane.size() != 0) - pl = plane; + planev = plane; Eigen::VectorXi Jv; - igl::slice_tets(V, T, pl, U, G, Jv, BC); + igl::slice_tets(V, T, planev, U, G, Jv, BC); J = Jv; }, __doc_igl_slice_tets, py::arg("V"), py::arg("T"), py::arg("plane"), py::arg("U"), py::arg("G"), py::arg("J"), py::arg("BC")); diff --git a/python/py_igl/py_winding_number.cpp b/python/py_igl/py_winding_number.cpp new file mode 100644 index 000000000..49752ff15 --- /dev/null +++ b/python/py_igl/py_winding_number.cpp @@ -0,0 +1,18 @@ +// COMPLETE BINDINGS ======================== + + +m.def("winding_number", [] +( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::MatrixXd& O, + Eigen::MatrixXd& W +) +{ + Eigen::VectorXd Wv; + igl::winding_number(V, F, O, Wv); + W = Wv; +}, __doc_igl_winding_number, +py::arg("V"), py::arg("F"), py::arg("O"), py::arg("W")); + + diff --git a/python/python_shared.cpp b/python/python_shared.cpp index c3a650555..36d970e0c 100644 --- a/python/python_shared.cpp +++ b/python/python_shared.cpp @@ -34,6 +34,10 @@ extern void python_export_igl_cgal(py::module &); extern void python_export_igl_png(py::module &); #endif +#ifdef PY_COPYLEFT +extern void python_export_igl_copyleft(py::module &); +#endif + PYBIND11_PLUGIN(pyigl) { py::module m("pyigl", R"pyigldoc( Python wrappers for libigl @@ -56,6 +60,7 @@ PYBIND11_PLUGIN(pyigl) { boundary_facets boundary_loop cat + collapse_edge colon comb_cross_field comb_frame_field @@ -63,6 +68,8 @@ PYBIND11_PLUGIN(pyigl) { copyleft_cgal_mesh_boolean copyleft_comiso_miq copyleft_comiso_nrosy + copyleft_marching_cubes + copyleft_swept_volume copyleft_tetgen_tetrahedralize cotmatrix covariance_scatter_matrix @@ -72,12 +79,15 @@ PYBIND11_PLUGIN(pyigl) { edge_lengths eigs embree_ambient_occlusion + embree_reorient_facets_raycast find_cross_field_singularities fit_rotations floor gaussian_curvature + get_seconds grad harmonic + hsv_to_rgb internal_angles invert_diag is_irregular_vertex @@ -100,6 +110,7 @@ PYBIND11_PLUGIN(pyigl) { polar_svd principal_curvature quad_planarity + randperm readDMAT readMESH readOBJ @@ -117,6 +128,7 @@ PYBIND11_PLUGIN(pyigl) { unique unproject_onto_mesh upsample + winding_number writeMESH writeOBJ @@ -154,5 +166,9 @@ PYBIND11_PLUGIN(pyigl) { python_export_igl_png(m); #endif + #ifdef PY_COPYLEFT + python_export_igl_copyleft(m); + #endif + return m.ptr(); } diff --git a/python/scripts/generate_bindings.py b/python/scripts/generate_bindings.py index fe65a3708..0ffc14fea 100755 --- a/python/scripts/generate_bindings.py +++ b/python/scripts/generate_bindings.py @@ -79,7 +79,7 @@ def map_parameter_types(name, cpp_type, parsed_types, errors, enum_types): if len(parsed_types) == 0: errors.append("Empty typechain: %s" % cpp_type) - if cpp_type == "int" or cpp_type == "bool": + if cpp_type == "int" or cpp_type == "bool" or cpp_type == "unsigned int": return cpp_type, True else: return cpp_type, False diff --git a/python/scripts/python_shared.mako b/python/scripts/python_shared.mako index db26308a7..87b546097 100644 --- a/python/scripts/python_shared.mako +++ b/python/scripts/python_shared.mako @@ -34,6 +34,10 @@ extern void python_export_igl_cgal(py::module &); extern void python_export_igl_png(py::module &); #endif +#ifdef PY_COPYLEFT +extern void python_export_igl_copyleft(py::module &); +#endif + PYBIND11_PLUGIN(pyigl) { py::module m("pyigl", R"pyigldoc( Python wrappers for libigl @@ -82,5 +86,9 @@ PYBIND11_PLUGIN(pyigl) { python_export_igl_png(m); #endif + #ifdef PY_COPYLEFT + python_export_igl_copyleft(m); + #endif + return m.ptr(); } diff --git a/python/tutorial/702_WindingNumber.py b/python/tutorial/702_WindingNumber.py new file mode 100755 index 000000000..2de16e664 --- /dev/null +++ b/python/tutorial/702_WindingNumber.py @@ -0,0 +1,112 @@ +import sys, os + +# Add the igl library to the modules search path +sys.path.insert(0, os.getcwd() + "/../") +import pyigl as igl + +from shared import TUTORIAL_SHARED_PATH, check_dependencies, print_usage + +dependencies = ["viewer"] +check_dependencies(dependencies) + + +def append_mesh(C_vis, F_vis, V_vis, V, F, color): + F_vis.conservativeResize(F_vis.rows() + F.rows(), 3) + F_vis.setBottomRows(F.rows(), F + V_vis.rows()) + V_vis.conservativeResize(V_vis.rows() + V.rows(), 3) + V_vis.setBottomRows(V.rows(), V) + C_vis.conservativeResize(C_vis.rows() + F.rows(), 3) + colorM = igl.eigen.MatrixXd(F.rows(), C_vis.cols()) + colorM.rowwiseSet(color) + C_vis.setBottomRows(F.rows(), colorM) + + +def update(viewer): + global V, F, T, W, slice_z, overlay + plane = igl.eigen.MatrixXd([0, 0, 1, -((1 - slice_z) * V.col(2).minCoeff() + slice_z * V.col(2).maxCoeff())]) + V_vis = igl.eigen.MatrixXd() + F_vis = igl.eigen.MatrixXi() + J = igl.eigen.MatrixXi() + bary = igl.eigen.SparseMatrixd() + igl.slice_tets(V, T, plane, V_vis, F_vis, J, bary) + W_vis = igl.eigen.MatrixXd() + igl.slice(W, J, W_vis) + C_vis = igl.eigen.MatrixXd() + igl.parula(W_vis, False, C_vis) + + if overlay == 1: # OVERLAY_INPUT + append_mesh(C_vis, F_vis, V_vis, V, F, igl.eigen.MatrixXd([[1., 0.894, 0.227]])) + elif overlay == 2: # OVERLAY_OUTPUT + append_mesh(C_vis, F_vis, V_vis, V, F, igl.eigen.MatrixXd([[0.8, 0.8, 0.8]])) + + viewer.data.clear() + viewer.data.set_mesh(V_vis, F_vis) + viewer.data.set_colors(C_vis) + viewer.data.set_face_based(True) + + +def key_down(viewer, key, modifier): + global overlay, slice_z + + if key == ord(' '): + overlay = (overlay + 1) % 3 + elif key == ord('.'): + slice_z = min(slice_z + 0.01, 0.99) + elif key == ord(','): + slice_z = max(slice_z - 0.01, 0.01) + + update(viewer) + + return False + + +if __name__ == "__main__": + keys = {"space": "toggle showing input mesh, output mesh or slice through tet-mesh of convex hull", + ". / ,": "push back/pull forward slicing plane"} + + print_usage(keys) + + V = igl.eigen.MatrixXd() + BC = igl.eigen.MatrixXd() + W = igl.eigen.MatrixXd() + T = igl.eigen.MatrixXi() + F = igl.eigen.MatrixXi() + G = igl.eigen.MatrixXi() + + slice_z = 0.5 + overlay = 0 + + # Load mesh: (V,T) tet-mesh of convex hull, F contains facets of input + # surface mesh _after_ self-intersection resolution + igl.readMESH(TUTORIAL_SHARED_PATH + "big-sigcat.mesh", V, T, F) + + # Compute barycenters of all tets + igl.barycenter(V, T, BC) + + # Compute generalized winding number at all barycenters + print("Computing winding number over all %i tets..." % T.rows()) + igl.winding_number(V, F, BC, W) + + # Extract interior tets + Wt = sum(W > 0.5) + CT = igl.eigen.MatrixXi(Wt, 4) + k = 0 + for t in range(T.rows()): + if W[t] > 0.5: + CT.setRow(k, T.row(t)) + k += 1 + + # find bounary facets of interior tets + igl.boundary_facets(CT, G) + + # boundary_facets seem to be reversed... + G = G.rowwiseReverse() + + # normalize + W = (W - W.minCoeff()) / (W.maxCoeff() - W.minCoeff()) + + # Plot the generated mesh + viewer = igl.viewer.Viewer() + update(viewer) + viewer.callback_key_down = key_down + viewer.launch() diff --git a/python/tutorial/705_MarchingCubes.py b/python/tutorial/705_MarchingCubes.py new file mode 100755 index 000000000..c5e483d76 --- /dev/null +++ b/python/tutorial/705_MarchingCubes.py @@ -0,0 +1,94 @@ +import sys, os + +# Add the igl library to the modules search path +sys.path.insert(0, os.getcwd() + "/../") +import pyigl as igl + +from shared import TUTORIAL_SHARED_PATH, check_dependencies, print_usage + +dependencies = ["copyleft", "viewer"] +check_dependencies(dependencies) + + +def key_down(viewer, key, modifier): + if key == ord('1'): + viewer.data.clear() + viewer.data.set_mesh(V, F) + elif key == ord('2'): + viewer.data.clear() + viewer.data.set_mesh(SV, SF) + elif key == ord('3'): + viewer.data.clear() + viewer.data.set_mesh(BV, BF) + + return True + + +if __name__ == "__main__": + keys = {"1": "show original mesh", + "2": "show marching cubes contour of signed distance", + "3": "show marching cubes contour of indicator function"} + + print_usage(keys) + + V = igl.eigen.MatrixXd() + F = igl.eigen.MatrixXi() + + # Read in inputs as double precision floating point meshes + igl.read_triangle_mesh(TUTORIAL_SHARED_PATH + "armadillo.obj", V, F) + + # number of vertices on the largest side + s = 50 + Vmin = V.colwiseMinCoeff() + Vmax = V.colwiseMaxCoeff() + h = (Vmax - Vmin).maxCoeff() / s + res = (s * ((Vmax - Vmin) / (Vmax - Vmin).maxCoeff())).castint() + + def lerp(res, Vmin, Vmax, di, d): + return Vmin[d] + di / (res[d] - 1) * (Vmax[d] - Vmin[d]) + + # create grid + print("Creating grid...") + GV = igl.eigen.MatrixXd(res[0] * res[1] * res[2], 3) + for zi in range(res[2]): + z = lerp(res, Vmin, Vmax, zi, 2) + for yi in range(res[1]): + y = lerp(res, Vmin, Vmax, yi, 1) + for xi in range(res[0]): + x = lerp(res, Vmin, Vmax, xi, 0) + GV.setRow(xi + res[0] * (yi + res[1] * zi), igl.eigen.MatrixXd([[x, y, z]])) + + # compute values + print("Computing distances...") + S = igl.eigen.MatrixXd() + B = igl.eigen.MatrixXd() + I = igl.eigen.MatrixXi() + C = igl.eigen.MatrixXd() + N = igl.eigen.MatrixXd() + + igl.signed_distance(GV, V, F, igl.SIGNED_DISTANCE_TYPE_PSEUDONORMAL, S, I, C, N) + # Convert distances to binary inside-outside data --> aliasing artifacts + B = S.copy() + for e in range(B.rows()): + if B[e] > 0: + B[e] = 1 + else: + if B[e] < 0: + B[e] = -1 + else: + B[e] = 0 + + print("Marching cubes...") + SV = igl.eigen.MatrixXd() + BV = igl.eigen.MatrixXd() + SF = igl.eigen.MatrixXi() + BF = igl.eigen.MatrixXi() + + igl.copyleft.marching_cubes(S, GV, res[0], res[1], res[2], SV, SF) + igl.copyleft.marching_cubes(B, GV, res[0], res[1], res[2], BV, BF) + + # Plot the generated mesh + viewer = igl.viewer.Viewer() + viewer.data.set_mesh(SV, SF) + viewer.callback_key_down = key_down + viewer.launch() diff --git a/python/tutorial/706_FacetOrientation.py b/python/tutorial/706_FacetOrientation.py new file mode 100755 index 000000000..6543e6c62 --- /dev/null +++ b/python/tutorial/706_FacetOrientation.py @@ -0,0 +1,78 @@ +import sys, os + +# Add the igl library to the modules search path +sys.path.insert(0, os.getcwd() + "/../") +import pyigl as igl + +from shared import TUTORIAL_SHARED_PATH, check_dependencies, print_usage + +dependencies = ["embree", "viewer"] +check_dependencies(dependencies) + + +def key_down(viewer, key, modifier): + global facetwise, is_showing_reoriented, FF + if key == ord('F') or key == ord('f'): + facetwise = (facetwise + 1) % 2 + elif key == ord('S') or key == ord('s'): + scramble_colors() + elif key == ord(' '): + is_showing_reoriented = ~is_showing_reoriented + + viewer.data.clear() + viewer.data.set_mesh(V, FF[facetwise] if is_showing_reoriented else F) + viewer.data.set_colors(RGBcolors[facetwise]) + + return True + +def scramble_colors(): + global C, viewer, RGBcolors + for p in range(2): + R = igl.eigen.MatrixXi() + igl.randperm(C[p].maxCoeff() + 1, R) + C[p] = igl.slice(R, igl.eigen.MatrixXi(C[p])) + HSV = igl.eigen.MatrixXd(C[p].rows(), 3) + HSV.setCol(0, 360.0 * C[p].castdouble() / C[p].maxCoeff()) + HSVright = igl.eigen.MatrixXd(HSV.rows(), 2) + HSVright.setConstant(1.0) + HSV.setRightCols(2, HSVright) + igl.hsv_to_rgb(HSV, RGBcolors[p]) + viewer.data.set_colors(RGBcolors[facetwise]) + + + +if __name__ == "__main__": + keys = {"space": "toggle between original and reoriented faces", + "F,f": "toggle between patchwise and facetwise reorientation", + "S,s": "scramble colors"} + print_usage(keys) + + V = igl.eigen.MatrixXd() + F = igl.eigen.MatrixXi() + C = [igl.eigen.MatrixXi(), igl.eigen.MatrixXi()] + RGBcolors = [igl.eigen.MatrixXd(), igl.eigen.MatrixXd()] + FF = [igl.eigen.MatrixXi(), igl.eigen.MatrixXi()] + is_showing_reoriented = False + facetwise = 0 + + igl.read_triangle_mesh(TUTORIAL_SHARED_PATH + "truck.obj", V, F) + + # Compute patches + for p in range(2): + I = igl.eigen.MatrixXi() + igl.embree.reorient_facets_raycast(V, F, F.rows() * 100, 10, p == 1, False, False, I, C[p]) + # apply reorientation + FF[p].conservativeResize(F.rows(), F.cols()) + for i in range(I.rows()): + if I[i]: + FF[p].setRow(i, F.row(i).rowwiseReverse()) + else: + FF[p].setRow(i, F.row(i)) + + # Plot the generated mesh + viewer = igl.viewer.Viewer() + viewer.data.set_mesh(V, FF[facetwise] if is_showing_reoriented else F) + viewer.data.set_face_based(True) + scramble_colors() + viewer.callback_key_down = key_down + viewer.launch() diff --git a/python/tutorial/707_SweptVolume.py b/python/tutorial/707_SweptVolume.py new file mode 100755 index 000000000..e15252911 --- /dev/null +++ b/python/tutorial/707_SweptVolume.py @@ -0,0 +1,83 @@ +import sys, os + +# Add the igl library to the modules search path +from math import pi, cos + +sys.path.insert(0, os.getcwd() + "/../") +import pyigl as igl + +from shared import TUTORIAL_SHARED_PATH, check_dependencies, print_usage + +dependencies = ["copyleft", "viewer"] +check_dependencies(dependencies) + + +def key_down(viewer, key, modifier): + global show_swept_volume, SV, SF, V, F + if key == ord(' '): + show_swept_volume = not show_swept_volume + viewer.data.clear() + + if show_swept_volume: + viewer.data.set_mesh(SV, SF) + viewer.data.uniform_colors(igl.eigen.MatrixXd([0.2, 0.2, 0.2]), igl.eigen.MatrixXd([1.0, 1.0, 1.0]), igl.eigen.MatrixXd([1.0, 1.0, 1.0])) # TODO replace with constants from cpp + else: + viewer.data.set_mesh(V, F) + + viewer.core.is_animating = not show_swept_volume + viewer.data.set_face_based(True) + + return True + + +def pre_draw(viewer): + global show_swept_volume, V + if not show_swept_volume: + T = transform(0.25 * igl.get_seconds()) + VT = V * T.matrix().block(0, 0, 3, 3).transpose() + trans = T.matrix().block(0, 3, 3, 1).transpose() + Vtrans = igl.eigen.MatrixXd(VT.rows(), VT.cols()) + Vtrans.rowwiseSet(trans) + VT += Vtrans + viewer.data.set_vertices(VT) + viewer.data.compute_normals() + return False + + +# Define a rigid motion +def transform(t): + T = igl.eigen.Affine3d() + T.setIdentity() + T.rotate(t * 2 * pi, igl.eigen.MatrixXd([0, 1, 0])) + T.translate(igl.eigen.MatrixXd([0, 0.125 * cos(2 * pi * t), 0])) + return T + + +if __name__ == "__main__": + keys = {"space": "toggle between transforming original mesh and swept volume"} + print_usage(keys) + + V = igl.eigen.MatrixXd() + SV = igl.eigen.MatrixXd() + VT = igl.eigen.MatrixXd() + F = igl.eigen.MatrixXi() + SF = igl.eigen.MatrixXi() + show_swept_volume = False + grid_size = 50 + time_steps = 200 + isolevel = 1 + + igl.read_triangle_mesh(TUTORIAL_SHARED_PATH + "bunny.off", V, F) + + print("Computing swept volume...") + igl.copyleft.swept_volume(V, F, transform, time_steps, grid_size, isolevel, SV, SF) + print("...finished.") + + # Plot the generated mesh + viewer = igl.viewer.Viewer() + viewer.data.set_mesh(V, F) + viewer.data.set_face_based(True) + viewer.core.is_animating = not show_swept_volume + viewer.callback_pre_draw = pre_draw + viewer.callback_key_down = key_down + viewer.launch() diff --git a/python/tutorial/607_Picking.py b/python/tutorial/708_Picking.py similarity index 53% rename from python/tutorial/607_Picking.py rename to python/tutorial/708_Picking.py index 87211b88e..19b1e3f72 100755 --- a/python/tutorial/607_Picking.py +++ b/python/tutorial/708_Picking.py @@ -5,19 +5,12 @@ sys.path.insert(0, os.getcwd() + "/../") import pyigl as igl -from shared import TUTORIAL_SHARED_PATH, check_dependencies +from shared import TUTORIAL_SHARED_PATH, check_dependencies, print_usage dependencies = ["viewer"] check_dependencies(dependencies) -# Mesh with per-face color -V = igl.eigen.MatrixXd() -F = igl.eigen.MatrixXi() -C = igl.eigen.MatrixXd() - -viewer = igl.viewer.Viewer() - def mouse_down(viewer, a, b): bc = igl.eigen.MatrixXd() @@ -27,6 +20,7 @@ def mouse_down(viewer, a, b): hit = igl.unproject_onto_mesh(coord, viewer.core.view * viewer.core.model, viewer.core.proj, viewer.core.viewport, V, F, fid, bc) if hit: + # paint hit red C.setRow(fid[0, 0], igl.eigen.MatrixXd([[1, 0, 0]])) viewer.data.set_colors(C) return True @@ -34,16 +28,25 @@ def mouse_down(viewer, a, b): return False -print("Usage: [LeftMouseClick] to select a face") +if __name__ == "__main__": + keys = {"click": "Pick face on shape"} + print_usage(keys) -# Load a mesh in OFF format -igl.readOFF(TUTORIAL_SHARED_PATH + "fertility.off", V, F) + # Mesh with per-face color + V = igl.eigen.MatrixXd() + F = igl.eigen.MatrixXi() + C = igl.eigen.MatrixXd() -# Initialize white -C.setConstant(F.rows(), 3, 1.0) + # Load a mesh in OFF format + igl.readOFF(TUTORIAL_SHARED_PATH + "fertility.off", V, F) -viewer.data.set_mesh(V, F) -viewer.data.set_colors(C) -viewer.core.show_lines = False -viewer.callback_mouse_down = mouse_down -viewer.launch() + # Initialize white + C.setConstant(F.rows(), 3, 1.0) + + # Show mesh + viewer = igl.viewer.Viewer() + viewer.data.set_mesh(V, F) + viewer.data.set_colors(C) + viewer.core.show_lines = False + viewer.callback_mouse_down = mouse_down + viewer.launch() diff --git a/python/tutorial/shared.py b/python/tutorial/shared.py index 365b6c348..7afbde052 100644 --- a/python/tutorial/shared.py +++ b/python/tutorial/shared.py @@ -14,3 +14,9 @@ def check_dependencies(deps): if not all_available: sys.exit(-1) + + +def print_usage(key_dict): + print("Usage:") + for k in key_dict.keys(): + print("%s : %s" %(k, key_dict[k]))