// 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 // obtain one at http://mozilla.org/MPL/2.0/. #include "arap.h" #include "colon.h" #include "cotmatrix.h" #include "massmatrix.h" #include "group_sum_matrix.h" #include "covariance_scatter_matrix.h" #include "speye.h" #include "mode.h" #include "project_isometrically_to_plane.h" #include "slice.h" #include "arap_rhs.h" #include "repdiag.h" #include "columnize.h" #include "fit_rotations.h" #include #include template < typename DerivedV, typename DerivedF, typename Derivedb> IGL_INLINE bool igl::arap_precomputation( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const int dim, const Eigen::PlainObjectBase & b, ARAPData & data) { using namespace std; using namespace Eigen; typedef typename DerivedV::Scalar Scalar; // number of vertices const int n = V.rows(); data.n = n; assert((b.size() == 0 || b.maxCoeff() < n) && "b out of bounds"); assert((b.size() == 0 || b.minCoeff() >=0) && "b out of bounds"); // remember b data.b = b; //assert(F.cols() == 3 && "For now only triangles"); // dimension //const int dim = V.cols(); assert((dim == 3 || dim ==2) && "dim should be 2 or 3"); data.dim = dim; //assert(dim == 3 && "Only 3d supported"); // Defaults data.f_ext = MatrixXd::Zero(n,data.dim); assert(data.dim <= V.cols() && "solve dim should be <= embedding"); bool flat = (V.cols() - data.dim)==1; DerivedV plane_V; DerivedF plane_F; typedef SparseMatrix SparseMatrixS; SparseMatrixS ref_map,ref_map_dim; if(flat) { project_isometrically_to_plane(V,F,plane_V,plane_F,ref_map); repdiag(ref_map,dim,ref_map_dim); } const PlainObjectBase& ref_V = (flat?plane_V:V); const PlainObjectBase& ref_F = (flat?plane_F:F); SparseMatrixS L; cotmatrix(V,F,L); ARAPEnergyType eff_energy = data.energy; if(eff_energy == ARAP_ENERGY_TYPE_DEFAULT) { switch(F.cols()) { case 3: if(data.dim == 3) { eff_energy = ARAP_ENERGY_TYPE_SPOKES_AND_RIMS; }else { eff_energy = ARAP_ENERGY_TYPE_ELEMENTS; } break; case 4: eff_energy = ARAP_ENERGY_TYPE_ELEMENTS; break; default: assert(false); } } // Get covariance scatter matrix, when applied collects the covariance // matrices used to fit rotations to during optimization covariance_scatter_matrix(ref_V,ref_F,eff_energy,data.CSM); if(flat) { data.CSM = (data.CSM * ref_map_dim.transpose()).eval(); } assert(data.CSM.cols() == V.rows()*data.dim); // Get group sum scatter matrix, when applied sums all entries of the same // group according to G SparseMatrix G_sum; if(data.G.size() == 0) { if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS) { speye(F.rows(),G_sum); }else { speye(n,G_sum); } }else { // groups are defined per vertex, convert to per face using mode if(eff_energy == ARAP_ENERGY_TYPE_ELEMENTS) { Eigen::Matrix GG; MatrixXi GF(F.rows(),F.cols()); for(int j = 0;j GFj; slice(data.G,F.col(j),GFj); GF.col(j) = GFj; } mode(GF,2,GG); data.G=GG; } //printf("group_sum_matrix()\n"); group_sum_matrix(data.G,G_sum); } SparseMatrix G_sum_dim; repdiag(G_sum,data.dim,G_sum_dim); assert(G_sum_dim.cols() == data.CSM.rows()); data.CSM = (G_sum_dim * data.CSM).eval(); arap_rhs(ref_V,ref_F,data.dim,eff_energy,data.K); if(flat) { data.K = (ref_map_dim * data.K).eval(); } assert(data.K.rows() == data.n*data.dim); SparseMatrix Q = (-L).eval(); if(data.with_dynamics) { const double h = data.h; assert(h != 0); SparseMatrix M; massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,data.M); const double dw = (1./data.ym)*(h*h); SparseMatrix DQ = dw * 1./(h*h)*data.M; Q += DQ; // Dummy external forces data.f_ext = MatrixXd::Zero(n,data.dim); data.vel = MatrixXd::Zero(n,data.dim); } return min_quad_with_fixed_precompute( Q,b,SparseMatrix(),true,data.solver_data); } template < typename Derivedbc, typename DerivedU> IGL_INLINE bool igl::arap_solve( const Eigen::PlainObjectBase & bc, ARAPData & data, Eigen::PlainObjectBase & U) { using namespace Eigen; using namespace std; assert(data.b.size() == bc.rows()); if(bc.size() > 0) { assert(bc.cols() == data.dim && "bc.cols() match data.dim"); } const int n = data.n; int iter = 0; if(U.size() == 0) { // terrible initial guess.. should at least copy input mesh #ifndef NDEBUG cerr<<"arap_solve: Using terrible initial guess for U. Try U = V."<0) { bcc = bc.col(c); } min_quad_with_fixed_solve( data.solver_data, Bc,bcc,Beq, Uc); U.col(c) = Uc; } iter++; } if(data.with_dynamics) { // Keep track of velocity for next time data.vel = (U-U0)/data.h; } return true; } #ifdef IGL_STATIC_LIBRARY template bool igl::arap_solve, Eigen::Matrix >(Eigen::PlainObjectBase > const&, igl::ARAPData&, Eigen::PlainObjectBase >&); template bool igl::arap_precomputation, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, Eigen::PlainObjectBase > const&, igl::ARAPData&); #endif