// 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 #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(row,tag_t[i] , 2 )); if (isFixed_j) b(row) += 2 * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] ,-2 )); if (isFixed_p) b(row) += -((4 * igl::PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid],((4 * igl::PI)/Nd))); b(row) += -2 * k[eid]; assert(hard[i] == hard[i]); assert(hard[j] == hard[j]); assert(p[eid] == p[eid]); assert(k[eid] == k[eid]); assert(b(row) == b(row)); } // (j)+1 -th row: t_j [ -2 2 -4pi/N -2 ] if (!isFixed_j) { row = tag_t[j]; if (isFixed_i) b(row) += 2 * hard[i]; else T.push_back(Eigen::Triplet(row,tag_t[i] , -2 )); if (isFixed_j) b(row) += -2 * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] , 2 )); if (isFixed_p) b(row) += ((4 * igl::PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid],-((4 * igl::PI)/Nd))); b(row) += 2 * k[eid]; assert(k[eid] == k[eid]); assert(b(row) == b(row)); } // (r*3)+2 -th row: pij [ 4pi/N -4pi/N 2*(2pi/N)^2 4pi/N ] if (!isFixed_p) { row = tag_p[eid]; if (isFixed_i) b(row) += -(4 * igl::PI)/Nd * hard[i]; else T.push_back(Eigen::Triplet(row,tag_t[i] , (4 * igl::PI)/Nd )); if (isFixed_j) b(row) += (4 * igl::PI)/Nd * hard[j]; else T.push_back(Eigen::Triplet(row,tag_t[j] , -(4 * igl::PI)/Nd )); if (isFixed_p) b(row) += -(2 * pow(((2*igl::PI)/Nd),2)) * p[eid] ; else T.push_back(Eigen::Triplet(row,tag_p[eid], (2 * pow(((2*igl::PI)/Nd),2)))); b(row) += - (4 * igl::PI)/Nd * k[eid]; assert(k[eid] == k[eid]); assert(b(row) == b(row)); } } A = SparseMatrix(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()); // ofstream s("/Users/daniele/As.txt"); // for(unsigned i=0; i 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; } // ofstream s("/Users/daniele/A.txt"); // for (int k=0; k::InnerIterator it(A,k); it; ++it) // { // s << it.row() << " " << it.col() << " " << it.value() << endl; // } // s.close(); // // ofstream s2("/Users/daniele/b.txt"); // for(unsigned i=0; i > 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; //print_miso_settings(cs.misolver()); 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; // debug // MatrixXd B(F.rows(),3); // for(unsigned i=0; i visited(EV.rows()); for(unsigned i=0; i starting(EV.rows()); for(unsigned i=0; i q; for(unsigned i=0; i