// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2015 Olga Diamanti // // 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_INTEGRABLE_POLYVECTOR_FIELDS #define IGL_INTEGRABLE_POLYVECTOR_FIELDS #include "igl_inline.h" #include #include namespace igl { // Compute a curl-free frame field from user constraints, optionally starting // from a gived frame field (assumed to be interpolating the constraints). // Implementation of the paper "Integrable PolyVector Fields", SIGGRAPH 2015. // Set of parameters used during solve struct integrable_polyvector_fields_parameters; // All data necessary for solving. Gets initialized from the original field // and gets updated during each solve. template class IntegrableFieldSolverData; // Precomputes matrices and necessary initial variables from the mesh and the // original field. This function is meant to be called before solve(). // Inputs: // V #V by 3 list of mesh vertex coordinates // F #F by 3 list of mesh faces (must be triangles) // b #B by 1 list of constrained face indices // bc #B by 6 list of the representative vectors at the constrained faces // constraint_level #B by 1 list of "constraint level" flag (level=2 indicates that both directions are constrained, // level = 1 indicates a partially constrained face, i.e. only the first vector will be constrained) // original_field #F by 6 list of the representative vectors of the frame field to be used as a starting point // (up to permutation and sign, stacked horizontally for each face) // Returns: // data an IntegrableFieldSolverData object that holds all intermediate // data needed by the solve routine, with correctly initialized values. // template IGL_INLINE void integrable_polyvector_fields_precompute(const Eigen::PlainObjectBase& V, const Eigen::PlainObjectBase& F, const Eigen::VectorXi& b, const Eigen::PlainObjectBase& bc, const Eigen::VectorXi& constraint_level, const Eigen::PlainObjectBase& original_field, igl::IntegrableFieldSolverData &data); // Given the current estimate of the field, performes one round of optimization // iterations and updates the current estimate. The intermediate data is saved // and returned for the next iteration. // Inputs: // data an IntegrableFieldSolverData object that holds all intermediate // data needed by the solve routine, with their values at the current time instance. // params solver parameters (see below) // current_field #F by 6 list of the representative vectors of the current frame field to be used as a starting point // current_field_is_not_ccw boolean, determines whether the representative vectors in the current field are in // non- ccw order - if true, they will be corrected before being passed to the solver. // The field returned by this routine is always in ccw order, so this flag typically only // needs to be set to true during the first call to solve(). If unsure, set to true. // Returns: // current_field updated estimate for the integrable field // template IGL_INLINE void integrable_polyvector_fields_solve(IntegrableFieldSolverData &cffsoldata, integrable_polyvector_fields_parameters ¶ms, Eigen::PlainObjectBase& current_field, bool current_field_is_not_ccw); }; //parameters struct igl::integrable_polyvector_fields_parameters { // number of optimization iterations int numIter; //weight for barrier term (ensuring ccw ordering of the vectors per face) double wBarrier; //the s-parameter of the barrier term (see Schüller et al. 2013, Locally Injective Mappings) double sBarrier; //weight for the PolyCurl term of the energy double wCurl; //weight for the PolyQuotient term of the energy double wQuotCurl; //weight for the smoothness term of the energy double wSmooth; //weight for the closeness to the original field, for the unconstrained faces (regularization term) double wCloseUnconstrained; //weight for the closeness to the original field, for the constrained faces double wCloseConstrained; //the per-iteration reduction factor the smoothness weight double redFactor_wsmooth; //the step size for the Gauss-Newton optimization double gamma; //tikhonov regularization term (typically not needed, default value should suffice) double tikh_gamma; IGL_INLINE integrable_polyvector_fields_parameters(); }; //solver data template class igl::IntegrableFieldSolverData { public: //Original field Eigen::VectorXd xOriginal; //Constraints Eigen::VectorXi constrained; Eigen::VectorXi is_constrained_face;//0 for unconstrained, 1 for constrained once (partial) and 2 for fully constrained Eigen::VectorXi indInConstrained; Eigen::MatrixXd constrained_vec3; //Mesh data //edges and normalized edge vectors Eigen::MatrixXd EVecNorm; Eigen::MatrixXi E, E2F, F2E; int numV, numF, numE; //interior edge data int numInteriorEdges; Eigen::Matrix E2F_int; Eigen::VectorXi indInteriorToFull; Eigen::VectorXi indFullToInterior; //per-edge angles (for parallel transport) Eigen::VectorXd K; //local bases Eigen::PlainObjectBase B1, B2, FN; //Solver Data Eigen::VectorXd residuals; Eigen::SparseMatrix Jac; Eigen::VectorXi II_Jac, JJ_Jac; Eigen::VectorXd SS_Jac; int numVariables; int num_residuals; int num_residuals_smooth; int num_residuals_close; int num_residuals_polycurl; int num_residuals_quotcurl; int num_residuals_barrier; const int numInnerJacCols_edge = 8; const int numInnerJacCols_face = 4; const int numInnerJacRows_smooth = 4; const int numInnerJacRows_polycurl = 2; const int numInnerJacRows_quotcurl = 1; const int numInnerJacRows_barrier = 1; const int numInnerJacRows_close = 4; int numJacElements_smooth; int numJacElements_polycurl; int numJacElements_quotcurl; int numJacElements_barrier; int numJacElements_close; int numJacElements; IGL_INLINE void add_jac_indices_face(const int numInnerRows, const int numInnerCols, const int startRowInJacobian, const int startIndexInVectors, Eigen::VectorXi &Rows, Eigen::VectorXi &Columns); IGL_INLINE void face_Jacobian_indices(const int &startRow, const int &toplace, const int& fi, const int& half_degree, const int &numInnerRows, const int &numInnerCols, Eigen::VectorXi &rows, Eigen::VectorXi &columns); IGL_INLINE void add_Jacobian_to_svector(const int &toplace, const Eigen::MatrixXd &tJac, Eigen::VectorXd &SS_Jac); IGL_INLINE void add_jac_indices_edge(const int numInnerRows, const int numInnerCols, const int startRowInJacobian, const int startIndexInVectors, Eigen::VectorXi &Rows, Eigen::VectorXi &Columns); IGL_INLINE void edge_Jacobian_indices(const int &startRow, const int &toplace, const int& a, const int& b, const int& half_degree, const int &numInnerRows, const int &numInnerCols, Eigen::VectorXi &rows, Eigen::VectorXi &columns); std::vector indInSS_Hess_1_vec; std::vector indInSS_Hess_2_vec; Eigen::SparseMatrix Hess; std::vector > Hess_triplets; Eigen::SimplicialLDLT > solver; IGL_INLINE void precomputeMesh(const Eigen::PlainObjectBase &_V, const Eigen::PlainObjectBase &_F); IGL_INLINE void computeInteriorEdges(); IGL_INLINE void computeJacobianPattern(); IGL_INLINE void computeHessianPattern(); IGL_INLINE void computeNewHessValues(); IGL_INLINE void initializeOriginalVariable(const Eigen::PlainObjectBase& original_field); IGL_INLINE void initializeConstraints(const Eigen::VectorXi& b, const Eigen::PlainObjectBase& bc, const Eigen::VectorXi& constraint_level); IGL_INLINE void makeFieldCCW(Eigen::MatrixXd &sol3D); public: IGL_INLINE IntegrableFieldSolverData(); IGL_INLINE IntegrableFieldSolverData( const Eigen::PlainObjectBase &_V, const Eigen::PlainObjectBase &_F, const Eigen::VectorXi& b, const Eigen::VectorXi& constraint_level, const Eigen::PlainObjectBase& original_field); }; #ifndef IGL_STATIC_LIBRARY #include "integrable_polyvector_fields.cpp" #endif #endif /* defined(IGL_INTEGRABLE_POLYVECTOR_FIELDS) */