
Fixing dep build script on Windows and removing some warnings. Use bundled igl by default. Not building with the dependency scripts if not explicitly stated. This way, it will stay in Fix the libigl patch to include C source files in header only mode.
312 lines
8.5 KiB
C++
312 lines
8.5 KiB
C++
// This file is part of libigl, a simple c++ geometry processing library.
|
|
//
|
|
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
|
|
//
|
|
// 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 <cassert>
|
|
#include <iostream>
|
|
|
|
template <
|
|
typename DerivedV,
|
|
typename DerivedF,
|
|
typename Derivedb>
|
|
IGL_INLINE bool igl::arap_precomputation(
|
|
const Eigen::PlainObjectBase<DerivedV> & V,
|
|
const Eigen::PlainObjectBase<DerivedF> & F,
|
|
const int dim,
|
|
const Eigen::PlainObjectBase<Derivedb> & 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<Scalar> 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<DerivedV>& ref_V = (flat?plane_V:V);
|
|
const PlainObjectBase<DerivedF>& 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<double> 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<int,Eigen::Dynamic,1> GG;
|
|
MatrixXi GF(F.rows(),F.cols());
|
|
for(int j = 0;j<F.cols();j++)
|
|
{
|
|
Matrix<int,Eigen::Dynamic,1> GFj;
|
|
slice(data.G,F.col(j),GFj);
|
|
GF.col(j) = GFj;
|
|
}
|
|
mode<int>(GF,2,GG);
|
|
data.G=GG;
|
|
}
|
|
//printf("group_sum_matrix()\n");
|
|
group_sum_matrix(data.G,G_sum);
|
|
}
|
|
SparseMatrix<double> 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<double> Q = (-L).eval();
|
|
|
|
if(data.with_dynamics)
|
|
{
|
|
const double h = data.h;
|
|
assert(h != 0);
|
|
SparseMatrix<double> M;
|
|
massmatrix(V,F,MASSMATRIX_TYPE_DEFAULT,data.M);
|
|
const double dw = (1./data.ym)*(h*h);
|
|
SparseMatrix<double> 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<double>(),true,data.solver_data);
|
|
}
|
|
|
|
template <
|
|
typename Derivedbc,
|
|
typename DerivedU>
|
|
IGL_INLINE bool igl::arap_solve(
|
|
const Eigen::PlainObjectBase<Derivedbc> & bc,
|
|
ARAPData & data,
|
|
Eigen::PlainObjectBase<DerivedU> & 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."<<endl;
|
|
#endif
|
|
U = MatrixXd::Zero(data.n,data.dim);
|
|
}else
|
|
{
|
|
assert(U.cols() == data.dim && "U.cols() match data.dim");
|
|
}
|
|
// changes each arap iteration
|
|
MatrixXd U_prev = U;
|
|
// doesn't change for fixed with_dynamics timestep
|
|
MatrixXd U0;
|
|
if(data.with_dynamics)
|
|
{
|
|
U0 = U_prev;
|
|
}
|
|
while(iter < data.max_iter)
|
|
{
|
|
U_prev = U;
|
|
// enforce boundary conditions exactly
|
|
for(int bi = 0;bi<bc.rows();bi++)
|
|
{
|
|
U.row(data.b(bi)) = bc.row(bi);
|
|
}
|
|
|
|
const auto & Udim = U.replicate(data.dim,1);
|
|
assert(U.cols() == data.dim);
|
|
// As if U.col(2) was 0
|
|
MatrixXd S = data.CSM * Udim;
|
|
// THIS NORMALIZATION IS IMPORTANT TO GET SINGLE PRECISION SVD CODE TO WORK
|
|
// CORRECTLY.
|
|
S /= S.array().abs().maxCoeff();
|
|
|
|
const int Rdim = data.dim;
|
|
MatrixXd R(Rdim,data.CSM.rows());
|
|
if(R.rows() == 2)
|
|
{
|
|
fit_rotations_planar(S,R);
|
|
}else
|
|
{
|
|
fit_rotations(S,true,R);
|
|
//#ifdef __SSE__ // fit_rotations_SSE will convert to float if necessary
|
|
// fit_rotations_SSE(S,R);
|
|
//#else
|
|
// fit_rotations(S,true,R);
|
|
//#endif
|
|
}
|
|
//for(int k = 0;k<(data.CSM.rows()/dim);k++)
|
|
//{
|
|
// R.block(0,dim*k,dim,dim) = MatrixXd::Identity(dim,dim);
|
|
//}
|
|
|
|
|
|
// Number of rotations: #vertices or #elements
|
|
int num_rots = data.K.cols()/Rdim/Rdim;
|
|
// distribute group rotations to vertices in each group
|
|
MatrixXd eff_R;
|
|
if(data.G.size() == 0)
|
|
{
|
|
// copy...
|
|
eff_R = R;
|
|
}else
|
|
{
|
|
eff_R.resize(Rdim,num_rots*Rdim);
|
|
for(int r = 0;r<num_rots;r++)
|
|
{
|
|
eff_R.block(0,Rdim*r,Rdim,Rdim) =
|
|
R.block(0,Rdim*data.G(r),Rdim,Rdim);
|
|
}
|
|
}
|
|
|
|
MatrixXd Dl;
|
|
if(data.with_dynamics)
|
|
{
|
|
assert(data.M.rows() == n &&
|
|
"No mass matrix. Call arap_precomputation if changing with_dynamics");
|
|
const double h = data.h;
|
|
assert(h != 0);
|
|
//Dl = 1./(h*h*h)*M*(-2.*V0 + Vm1) - fext;
|
|
// data.vel = (V0-Vm1)/h
|
|
// h*data.vel = (V0-Vm1)
|
|
// -h*data.vel = -V0+Vm1)
|
|
// -V0-h*data.vel = -2V0+Vm1
|
|
const double dw = (1./data.ym)*(h*h);
|
|
Dl = dw * (1./(h*h)*data.M*(-U0 - h*data.vel) - data.f_ext);
|
|
}
|
|
|
|
VectorXd Rcol;
|
|
columnize(eff_R,num_rots,2,Rcol);
|
|
VectorXd Bcol = -data.K * Rcol;
|
|
assert(Bcol.size() == data.n*data.dim);
|
|
for(int c = 0;c<data.dim;c++)
|
|
{
|
|
VectorXd Uc,Bc,bcc,Beq;
|
|
Bc = Bcol.block(c*n,0,n,1);
|
|
if(data.with_dynamics)
|
|
{
|
|
Bc += Dl.col(c);
|
|
}
|
|
if(bc.size()>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<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, igl::ARAPData&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
|
|
template bool igl::arap_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, int, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, igl::ARAPData&);
|
|
#endif
|