// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2015 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 "frame_field.h" #include #include #include #include #include namespace igl { namespace copyleft { namespace comiso { class FrameInterpolator { public: // Init IGL_INLINE FrameInterpolator(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F); IGL_INLINE ~FrameInterpolator(); // Reset constraints (at least one constraint must be present or solve will fail) IGL_INLINE void resetConstraints(); IGL_INLINE void setConstraint(const int fid, const Eigen::VectorXd& v); IGL_INLINE void interpolateSymmetric(); // Generate the frame field IGL_INLINE void solve(); // Convert the frame field in the canonical representation IGL_INLINE void frame2canonical(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v, double& theta, Eigen::VectorXd& S); // Convert the canonical representation in a frame field IGL_INLINE void canonical2frame(const Eigen::MatrixXd& TP, const double theta, const Eigen::VectorXd& S, Eigen::RowVectorXd& v); IGL_INLINE Eigen::MatrixXd getFieldPerFace(); IGL_INLINE void PolarDecomposition(Eigen::MatrixXd V, Eigen::MatrixXd& U, Eigen::MatrixXd& P); // Symmetric Eigen::MatrixXd S; std::vector S_c; // ------------------------------------------------- // Face Topology Eigen::MatrixXi TT, TTi; // Two faces are consistent if their representative vector are taken modulo PI std::vector edge_consistency; Eigen::MatrixXi edge_consistency_TT; private: IGL_INLINE double mod2pi(double d); IGL_INLINE double modpi2(double d); IGL_INLINE double modpi(double d); // Convert a direction on the tangent space into an angle IGL_INLINE double vector2theta(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v); // Convert an angle in a vector in the tangent space IGL_INLINE Eigen::RowVectorXd theta2vector(const Eigen::MatrixXd& TP, const double theta); // Interpolate the cross field (theta) IGL_INLINE void interpolateCross(); // Compute difference between reference frames IGL_INLINE void computek(); // Compute edge consistency IGL_INLINE void compute_edge_consistency(); // Cross field direction Eigen::VectorXd thetas; std::vector thetas_c; // Edge Topology Eigen::MatrixXi EV, FE, EF; std::vector isBorderEdge; // Angle between two reference frames // R(k) * t0 = t1 Eigen::VectorXd k; // Mesh Eigen::MatrixXd V; Eigen::MatrixXi F; // Normals per face Eigen::MatrixXd N; // Reference frame per triangle std::vector TPs; }; FrameInterpolator::FrameInterpolator(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 3*(igl::PI/4.0); // Copy it into edge_consistency_TT int i1 = -1; int i2 = -1; for (unsigned i=0; i<3; ++i) { if (TT(fid0,i) == fid1) i1 = i; if (TT(fid1,i) == fid0) i2 = i; } assert(i1 != -1); assert(i2 != -1); edge_consistency_TT(fid0,i1) = edge_consistency[eid]; edge_consistency_TT(fid1,i2) = edge_consistency[eid]; } } } void FrameInterpolator::computek() { using namespace std; using namespace Eigen; k.resize(EF.rows()); // For every non-border edge for (unsigned eid=0; eid(); assert(tmp(0) - ref1(0) < (0.000001)); assert(tmp(1) - ref1(1) < (0.000001)); k[eid] = ktemp; } } } void FrameInterpolator::frame2canonical(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v, double& theta, Eigen::VectorXd& S_v) { using namespace std; using namespace Eigen; RowVectorXd v0 = v.segment<3>(0); RowVectorXd v1 = v.segment<3>(3); // Project onto the tangent plane Vector2d vp0 = TP * v0.transpose(); Vector2d vp1 = TP * v1.transpose(); // Assemble matrix MatrixXd M(2,2); M << vp0, vp1; if (M.determinant() < 0) M.col(1) = -M.col(1); assert(M.determinant() > 0); // cerr << "M: " << M << endl; MatrixXd R,S; PolarDecomposition(M,R,S); // Finally, express the cross field as an angle theta = atan2(R(1,0),R(0,0)); MatrixXd R2(2,2); R2 << cos(theta), -sin(theta), sin(theta), cos(theta); assert((R2-R).norm() < 10e-8); // Convert into rotation invariant form S = R * S * R.inverse(); // Copy in vector form S_v = VectorXd(3); S_v << S(0,0), S(0,1), S(1,1); } void FrameInterpolator::canonical2frame(const Eigen::MatrixXd& TP, const double theta, const Eigen::VectorXd& S_v, Eigen::RowVectorXd& v) { using namespace std; using namespace Eigen; assert(S_v.size() == 3); MatrixXd S_temp(2,2); S_temp << S_v(0), S_v(1), S_v(1), S_v(2); // Convert angle in vector in the tangent plane // Vector2d vp(cos(theta),sin(theta)); // First reconstruct R MatrixXd R(2,2); R << cos(theta), -sin(theta), sin(theta), cos(theta); // Rotation invariant reconstruction MatrixXd M = S_temp * R; Vector2d vp0(M(0,0),M(1,0)); Vector2d vp1(M(0,1),M(1,1)); // Unproject the vectors RowVectorXd v0 = vp0.transpose() * TP; RowVectorXd v1 = vp1.transpose() * TP; v.resize(6); v << v0, v1; } void FrameInterpolator::solve() { interpolateCross(); interpolateSymmetric(); } void FrameInterpolator::interpolateSymmetric() { using namespace std; using namespace Eigen; // Generate uniform Laplacian matrix typedef Eigen::Triplet triplet; std::vector triplets; // Variables are stacked as x1,y1,z1,x2,y2,z2 triplets.reserve(3*4*F.rows()); MatrixXd b = MatrixXd::Zero(3*F.rows(),1); // Build L and b for (unsigned eid=0; eid L(3*F.rows(),3*F.rows()); L.setFromTriplets(triplets.begin(), triplets.end()); triplets.clear(); // Add soft constraints double w = 100000; for (unsigned fid=0; fid < F.rows(); ++fid) { if (S_c[fid]) { for (unsigned i=0;i<3;++i) { triplets.push_back(triplet(3*fid + i,3*fid + i,w)); b(3*fid + i) += w*S(fid,i); } } } SparseMatrix soft(3*F.rows(),3*F.rows()); soft.setFromTriplets(triplets.begin(), triplets.end()); SparseMatrix M; M = L + soft; // Solve Lx = b; SparseLU > solver; solver.compute(M); if(solver.info()!=Success) { std::cerr << "LU failed - frame_interpolator.cpp" << std::endl; assert(0); } MatrixXd x; x = solver.solve(b); if(solver.info()!=Success) { std::cerr << "Linear solve failed - frame_interpolator.cpp" << std::endl; assert(0); } S = MatrixXd::Zero(F.rows(),3); // Copy back the result for (unsigned i=0;i svd(V,Eigen::ComputeFullU | Eigen::ComputeFullV); U = svd.matrixU() * svd.matrixV().transpose(); P = svd.matrixV() * svd.singularValues().asDiagonal() * svd.matrixV().transpose(); } } } } IGL_INLINE void igl::copyleft::comiso::frame_field( const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, const Eigen::VectorXi& b, const Eigen::MatrixXd& bc1, const Eigen::MatrixXd& bc2, Eigen::MatrixXd& FF1, Eigen::MatrixXd& FF2 ) { using namespace std; using namespace Eigen; assert(b.size() > 0); // Init Solver FrameInterpolator field(V,F); for (unsigned i=0; i