// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2014 Daniele Panozzo // // 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 "nrosy.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace igl { namespace copyleft { namespace comiso { class NRosyField { public: // Init IGL_INLINE NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F); // Generate the N-rosy field // N degree of the rosy field // roundseparately: round the integer variables one at a time, slower but higher quality IGL_INLINE void solve(const int N = 4); // Set a hard constraint on fid // fid: face id // v: direction to fix (in 3d) IGL_INLINE void setConstraintHard(const int fid, const Eigen::Vector3d& v); // Set a soft constraint on fid // fid: face id // w: weight of the soft constraint, clipped between 0 and 1 // v: direction to fix (in 3d) IGL_INLINE void setConstraintSoft(const int fid, const double w, const Eigen::Vector3d& v); // Set the ratio between smoothness and soft constraints (0 -> smoothness only, 1 -> soft constr only) IGL_INLINE void setSoftAlpha(double alpha); // Reset constraints (at least one constraint must be present or solve will fail) IGL_INLINE void resetConstraints(); // Return the current field IGL_INLINE Eigen::MatrixXd getFieldPerFace(); // Return the current field (in Ahish's ffield format) IGL_INLINE Eigen::MatrixXd getFFieldPerFace(); // Compute singularity indexes IGL_INLINE void findCones(int N); // Return the singularities IGL_INLINE Eigen::VectorXd getSingularityIndexPerVertex(); private: // Compute angle differences between reference frames IGL_INLINE void computek(); // Remove useless matchings IGL_INLINE void reduceSpace(); // Prepare the system matrix IGL_INLINE void prepareSystemMatrix(const int N); // Solve without roundings IGL_INLINE void solveNoRoundings(); // Solve with roundings using CoMIso IGL_INLINE void solveRoundings(); // Round all p to 0 and fix IGL_INLINE void roundAndFixToZero(); // Round all p and fix IGL_INLINE void roundAndFix(); // Convert a vector in 3d to an angle wrt the local reference system IGL_INLINE double convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v); // Convert an angle wrt the local reference system to a 3d vector IGL_INLINE Eigen::Vector3d convertLocalto3D(unsigned fid, double a); // Compute the per vertex angle defect IGL_INLINE Eigen::VectorXd angleDefect(); // Temporary variable for the field Eigen::VectorXd angles; // Hard constraints Eigen::VectorXd hard; std::vector isHard; // Soft constraints Eigen::VectorXd soft; Eigen::VectorXd wSoft; double softAlpha; // Face Topology Eigen::MatrixXi TT, TTi; // Edge Topology Eigen::MatrixXi EV, FE, EF; std::vector isBorderEdge; // Per Edge information // Angle between two reference frames Eigen::VectorXd k; // Jumps Eigen::VectorXi p; std::vector pFixed; // Mesh Eigen::MatrixXd V; Eigen::MatrixXi F; // Normals per face Eigen::MatrixXd N; // Singularity index Eigen::VectorXd singularityIndex; // Reference frame per triangle std::vector TPs; // System stuff Eigen::SparseMatrix A; Eigen::VectorXd b; Eigen::VectorXi tag_t; Eigen::VectorXi tag_p; }; } // NAMESPACE COMISO } // NAMESPACE COPYLEFT } // NAMESPACE IGL igl::copyleft::comiso::NRosyField::NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F) { using namespace std; using namespace Eigen; V = _V; F = _F; assert(V.rows() > 0); assert(F.rows() > 0); // Generate topological relations igl::triangle_triangle_adjacency(F,TT,TTi); igl::edge_topology(V,F, EV, FE, EF); // Flag border edges isBorderEdge.resize(EV.rows()); for(unsigned i=0; i= 0 && alpha < 1); softAlpha = alpha; } void igl::copyleft::comiso::NRosyField::prepareSystemMatrix(const int N) { using namespace std; using namespace Eigen; double Nd = N; // Minimize the MIQ energy // Energy on edge ij is // (t_i - t_j + kij + pij*(2*pi/N))^2 // Partial derivatives: // t_i: 2 ( t_i - t_j + kij + pij*(2*pi/N)) = 0 // t_j: 2 (-t_i + t_j - kij - pij*(2*pi/N)) = 0 // pij: 4pi/N ( t_i - t_j + kij + pij*(2*pi/N)) = 0 // // t_i t_j pij kij // t_i [ 2 -2 4pi/N 2 ] // t_j [ -2 2 -4pi/N -2 ] // pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ] // Count and tag the variables tag_t = VectorXi::Constant(F.rows(),-1); vector id_t; int count = 0; for(unsigned i=0; i id_p; for(unsigned i=0; i > T; T.reserve(3 * 4 * count_p); for(unsigned r=0; r(count_t + count_p, count_t + count_p); A.setFromTriplets(T.begin(), T.end()); // Soft constraints bool addSoft = false; for(unsigned i=0; i > TSoft; TSoft.reserve(2 * count_p); for(unsigned i=0; i(varid,varid,wSoft[i])); bSoft[varid] += wSoft[i] * soft[i]; } } SparseMatrix ASoft(count_t + count_p, count_t + count_p); ASoft.setFromTriplets(TSoft.begin(), TSoft.end()); // Stupid Eigen bug SparseMatrix Atmp (count_t + count_p, count_t + count_p); SparseMatrix Atmp2(count_t + count_p, count_t + count_p); SparseMatrix Atmp3(count_t + count_p, count_t + count_p); // Merge the two part of the energy Atmp = (1.0 - softAlpha)*A; Atmp2 = softAlpha * ASoft; Atmp3 = Atmp+Atmp2; A = Atmp3; b = b*(1.0 - softAlpha) + bSoft * softAlpha; } } void igl::copyleft::comiso::NRosyField::solveNoRoundings() { using namespace std; using namespace Eigen; // Solve the linear system SimplicialLDLT > solver; solver.compute(A); VectorXd x = solver.solve(b); // Copy the result back for(unsigned i=0; i > gmm_A; std::vector gmm_b; std::vector ids_to_round; std::vector x; gmm_A.resize(n,n); gmm_b.resize(n); x.resize(n); // Copy A for (int k=0; k::InnerIterator it(A,k); it; ++it) { gmm_A(it.row(),it.col()) += it.value(); } // Copy b for(unsigned i=0; i > gmm_C(0, n); COMISO::ConstrainedSolver cs; cs.solve(gmm_C, gmm_A, x, gmm_b, ids_to_round, 0.0, false, true); // Copy the result back for(unsigned i=0; i(); assert(tmp(0) - ref1(0) < 10^10); assert(tmp(1) - ref1(1) < 10^10); k[eid] = ktemp; } } } void igl::copyleft::comiso::NRosyField::reduceSpace() { using namespace std; using namespace Eigen; // All variables are free in the beginning for(unsigned i=0; i debug; vector visited(EV.rows()); for(unsigned i=0; i starting(EV.rows()); for(unsigned i=0; i q; for(unsigned i=0; i 0.) t /= norm_prod; else throw std::runtime_error("Error in 'igl::copyleft::comiso::NRosyField::angleDefect': division by zero!"); if (t > 1.) t = 1.; else if (t < -1.) t = -1.; A(F(i,j)) += acos(t); } } return A; } void igl::copyleft::comiso::NRosyField::findCones(int N) { // Compute I0, see http://www.graphics.rwth-aachen.de/media/papers/bommes_zimmer_2009_siggraph_011.pdf for details Eigen::VectorXd I0 = Eigen::VectorXd::Zero(V.rows()); // first the k for (unsigned i=0; i < EV.rows(); ++i) { if (!isBorderEdge[i]) { I0(EV(i,0)) -= k(i); I0(EV(i,1)) += k(i); } } // then the A Eigen::VectorXd A = angleDefect(); I0 = I0 + A; // normalize I0 = I0 / (2*igl::PI); // round to integer (remove numerical noise) for (unsigned i=0; i < I0.size(); ++i) I0(i) = round(I0(i)); // compute I Eigen::VectorXd I = I0; for (unsigned i=0; i < EV.rows(); ++i) { if (!isBorderEdge[i]) { I(EV(i,0)) -= double(p(i))/double(N); I(EV(i,1)) += double(p(i))/double(N); } } // Clear the vertices on the edges for (unsigned i=0; i < EV.rows(); ++i) { if (isBorderEdge[i]) { I0(EV(i,0)) = 0; I0(EV(i,1)) = 0; I(EV(i,0)) = 0; I(EV(i,1)) = 0; A(EV(i,0)) = 0; A(EV(i,1)) = 0; } } singularityIndex = I; } Eigen::VectorXd igl::copyleft::comiso::NRosyField::getSingularityIndexPerVertex() { return singularityIndex; } IGL_INLINE void igl::copyleft::comiso::nrosy( const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, const Eigen::VectorXi& b, const Eigen::MatrixXd& bc, const Eigen::VectorXi& b_soft, const Eigen::VectorXd& w_soft, const Eigen::MatrixXd& bc_soft, const int N, const double soft, Eigen::MatrixXd& R, Eigen::VectorXd& S ) { // Init solver igl::copyleft::comiso::NRosyField solver(V,F); // Add hard constraints for (unsigned i=0; i