mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-05 10:21:52 +03:00
update bundled_deps
This commit is contained in:
688
bundled_deps/libigl/igl/copyleft/comiso/frame_field.cpp
Normal file
688
bundled_deps/libigl/igl/copyleft/comiso/frame_field.cpp
Normal file
@@ -0,0 +1,688 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2015 Daniele Panozzo <daniele.panozzo@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 "frame_field.h"
|
||||
|
||||
#include <igl/triangle_triangle_adjacency.h>
|
||||
#include <igl/edge_topology.h>
|
||||
#include <igl/per_face_normals.h>
|
||||
#include <igl/copyleft/comiso/nrosy.h>
|
||||
#include <iostream>
|
||||
|
||||
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<bool> S_c;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
// Face Topology
|
||||
Eigen::MatrixXi TT, TTi;
|
||||
|
||||
// Two faces are consistent if their representative vector are taken modulo PI
|
||||
std::vector<bool> 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<bool> thetas_c;
|
||||
|
||||
// Edge Topology
|
||||
Eigen::MatrixXi EV, FE, EF;
|
||||
std::vector<bool> 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<Eigen::MatrixXd> 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<EV.rows(); ++i)
|
||||
isBorderEdge[i] = (EF(i,0) == -1) || ((EF(i,1) == -1));
|
||||
|
||||
// Generate normals per face
|
||||
igl::per_face_normals(V, F, N);
|
||||
|
||||
// Generate reference frames
|
||||
for(unsigned fid=0; fid<F.rows(); ++fid)
|
||||
{
|
||||
// First edge
|
||||
Vector3d e1 = V.row(F(fid,1)) - V.row(F(fid,0));
|
||||
e1.normalize();
|
||||
Vector3d e2 = N.row(fid);
|
||||
e2 = e2.cross(e1);
|
||||
e2.normalize();
|
||||
|
||||
MatrixXd TP(2,3);
|
||||
TP << e1.transpose(), e2.transpose();
|
||||
TPs.push_back(TP);
|
||||
}
|
||||
|
||||
// Reset the constraints
|
||||
resetConstraints();
|
||||
|
||||
// Compute k, differences between reference frames
|
||||
computek();
|
||||
|
||||
// Alloc internal variables
|
||||
thetas = VectorXd::Zero(F.rows());
|
||||
S = MatrixXd::Zero(F.rows(),3);
|
||||
|
||||
compute_edge_consistency();
|
||||
}
|
||||
|
||||
FrameInterpolator::~FrameInterpolator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
double FrameInterpolator::mod2pi(double d)
|
||||
{
|
||||
while(d<0)
|
||||
d = d + (2.0*igl::PI);
|
||||
|
||||
return fmod(d, (2.0*igl::PI));
|
||||
}
|
||||
|
||||
double FrameInterpolator::modpi2(double d)
|
||||
{
|
||||
while(d<0)
|
||||
d = d + (igl::PI/2.0);
|
||||
|
||||
return fmod(d, (igl::PI/2.0));
|
||||
}
|
||||
|
||||
double FrameInterpolator::modpi(double d)
|
||||
{
|
||||
while(d<0)
|
||||
d = d + (igl::PI);
|
||||
|
||||
return fmod(d, (igl::PI));
|
||||
}
|
||||
|
||||
|
||||
double FrameInterpolator::vector2theta(const Eigen::MatrixXd& TP, const Eigen::RowVectorXd& v)
|
||||
{
|
||||
// Project onto the tangent plane
|
||||
Eigen::Vector2d vp = TP * v.transpose();
|
||||
|
||||
// Convert to angle
|
||||
double theta = atan2(vp(1),vp(0));
|
||||
return theta;
|
||||
}
|
||||
|
||||
Eigen::RowVectorXd FrameInterpolator::theta2vector(const Eigen::MatrixXd& TP, const double theta)
|
||||
{
|
||||
Eigen::Vector2d vp(cos(theta),sin(theta));
|
||||
return vp.transpose() * TP;
|
||||
}
|
||||
|
||||
void FrameInterpolator::interpolateCross()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
//olga: was
|
||||
// NRosyField nrosy(V,F);
|
||||
// for (unsigned i=0; i<F.rows(); ++i)
|
||||
// if(thetas_c[i])
|
||||
// nrosy.setConstraintHard(i,theta2vector(TPs[i],thetas(i)));
|
||||
// nrosy.solve(4);
|
||||
// MatrixXd R = nrosy.getFieldPerFace();
|
||||
|
||||
//olga: is
|
||||
Eigen::MatrixXd R;
|
||||
Eigen::VectorXd S;
|
||||
Eigen::VectorXi b; b.resize(F.rows(),1);
|
||||
Eigen::MatrixXd bc; bc.resize(F.rows(),3);
|
||||
int num = 0;
|
||||
for (unsigned i=0; i<F.rows(); ++i)
|
||||
if(thetas_c[i])
|
||||
{
|
||||
b[num] = i;
|
||||
bc.row(num) = theta2vector(TPs[i],thetas(i));
|
||||
num++;
|
||||
}
|
||||
b.conservativeResize(num,Eigen::NoChange);
|
||||
bc.conservativeResize(num,Eigen::NoChange);
|
||||
|
||||
igl::copyleft::comiso::nrosy(V, F, b, bc, 4, R, S);
|
||||
//olga:end
|
||||
assert(R.rows() == F.rows());
|
||||
|
||||
for (unsigned i=0; i<F.rows(); ++i)
|
||||
thetas(i) = vector2theta(TPs[i],R.row(i));
|
||||
}
|
||||
|
||||
void FrameInterpolator::resetConstraints()
|
||||
{
|
||||
thetas_c.resize(F.rows());
|
||||
S_c.resize(F.rows());
|
||||
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
{
|
||||
thetas_c[i] = false;
|
||||
S_c[i] = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FrameInterpolator::compute_edge_consistency()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
// Compute per-edge consistency
|
||||
edge_consistency.resize(EF.rows());
|
||||
edge_consistency_TT = MatrixXi::Constant(TT.rows(),3,-1);
|
||||
|
||||
// For every non-border edge
|
||||
for (unsigned eid=0; eid<EF.rows(); ++eid)
|
||||
{
|
||||
if (!isBorderEdge[eid])
|
||||
{
|
||||
int fid0 = EF(eid,0);
|
||||
int fid1 = EF(eid,1);
|
||||
|
||||
double theta0 = thetas(fid0);
|
||||
double theta1 = thetas(fid1);
|
||||
|
||||
theta0 = theta0 + k(eid);
|
||||
|
||||
double r = modpi(theta0-theta1);
|
||||
|
||||
edge_consistency[eid] = r < igl::PI/4.0 || r > 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<EF.rows(); ++eid)
|
||||
{
|
||||
if (!isBorderEdge[eid])
|
||||
{
|
||||
int fid0 = EF(eid,0);
|
||||
int fid1 = EF(eid,1);
|
||||
|
||||
Vector3d N0 = N.row(fid0);
|
||||
//Vector3d N1 = N.row(fid1);
|
||||
|
||||
// find common edge on triangle 0 and 1
|
||||
int fid0_vc = -1;
|
||||
int fid1_vc = -1;
|
||||
for (unsigned i=0;i<3;++i)
|
||||
{
|
||||
if (EV(eid,0) == F(fid0,i))
|
||||
fid0_vc = i;
|
||||
if (EV(eid,1) == F(fid1,i))
|
||||
fid1_vc = i;
|
||||
}
|
||||
assert(fid0_vc != -1);
|
||||
assert(fid1_vc != -1);
|
||||
|
||||
Vector3d common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc));
|
||||
common_edge.normalize();
|
||||
|
||||
// Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis
|
||||
MatrixXd P(3,3);
|
||||
VectorXd o = V.row(F(fid0,fid0_vc));
|
||||
VectorXd tmp = -N0.cross(common_edge);
|
||||
P << common_edge, tmp, N0;
|
||||
P.transposeInPlace();
|
||||
|
||||
|
||||
MatrixXd V0(3,3);
|
||||
V0.row(0) = V.row(F(fid0,0)).transpose() -o;
|
||||
V0.row(1) = V.row(F(fid0,1)).transpose() -o;
|
||||
V0.row(2) = V.row(F(fid0,2)).transpose() -o;
|
||||
|
||||
V0 = (P*V0.transpose()).transpose();
|
||||
|
||||
assert(V0(0,2) < 10e-10);
|
||||
assert(V0(1,2) < 10e-10);
|
||||
assert(V0(2,2) < 10e-10);
|
||||
|
||||
MatrixXd V1(3,3);
|
||||
V1.row(0) = V.row(F(fid1,0)).transpose() -o;
|
||||
V1.row(1) = V.row(F(fid1,1)).transpose() -o;
|
||||
V1.row(2) = V.row(F(fid1,2)).transpose() -o;
|
||||
V1 = (P*V1.transpose()).transpose();
|
||||
|
||||
assert(V1(fid1_vc,2) < 10e-10);
|
||||
assert(V1((fid1_vc+1)%3,2) < 10e-10);
|
||||
|
||||
// compute rotation R such that R * N1 = N0
|
||||
// i.e. map both triangles to the same plane
|
||||
double alpha = -atan2(V1((fid1_vc+2)%3,2),V1((fid1_vc+2)%3,1));
|
||||
|
||||
MatrixXd R(3,3);
|
||||
R << 1, 0, 0,
|
||||
0, cos(alpha), -sin(alpha) ,
|
||||
0, sin(alpha), cos(alpha);
|
||||
V1 = (R*V1.transpose()).transpose();
|
||||
|
||||
assert(V1(0,2) < 10e-10);
|
||||
assert(V1(1,2) < 10e-10);
|
||||
assert(V1(2,2) < 10e-10);
|
||||
|
||||
// measure the angle between the reference frames
|
||||
// k_ij is the angle between the triangle on the left and the one on the right
|
||||
VectorXd ref0 = V0.row(1) - V0.row(0);
|
||||
VectorXd ref1 = V1.row(1) - V1.row(0);
|
||||
|
||||
ref0.normalize();
|
||||
ref1.normalize();
|
||||
|
||||
double ktemp = atan2(ref1(1),ref1(0)) - atan2(ref0(1),ref0(0));
|
||||
|
||||
// just to be sure, rotate ref0 using angle ktemp...
|
||||
MatrixXd R2(2,2);
|
||||
R2 << cos(ktemp), -sin(ktemp), sin(ktemp), cos(ktemp);
|
||||
|
||||
tmp = R2*ref0.head<2>();
|
||||
|
||||
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<double> triplet;
|
||||
std::vector<triplet> 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<EF.rows(); ++eid)
|
||||
{
|
||||
if (!isBorderEdge[eid])
|
||||
{
|
||||
for (int z=0;z<2;++z)
|
||||
{
|
||||
// W = [w_a, w_b
|
||||
// w_b, w_c]
|
||||
//
|
||||
|
||||
// It is not symmetric
|
||||
int i = EF(eid,z==0?0:1);
|
||||
int j = EF(eid,z==0?1:0);
|
||||
|
||||
int w_a_0 = (i*3)+0;
|
||||
int w_b_0 = (i*3)+1;
|
||||
int w_c_0 = (i*3)+2;
|
||||
|
||||
int w_a_1 = (j*3)+0;
|
||||
int w_b_1 = (j*3)+1;
|
||||
int w_c_1 = (j*3)+2;
|
||||
|
||||
// Rotation to change frame
|
||||
double r_a = cos(z==1?k(eid):-k(eid));
|
||||
double r_b = -sin(z==1?k(eid):-k(eid));
|
||||
double r_c = sin(z==1?k(eid):-k(eid));
|
||||
double r_d = cos(z==1?k(eid):-k(eid));
|
||||
|
||||
// First term
|
||||
// w_a_0 = r_a^2 w_a_1 + 2 r_a r_b w_b_1 + r_b^2 w_c_1 = 0
|
||||
triplets.push_back(triplet(w_a_0,w_a_0, -1 ));
|
||||
triplets.push_back(triplet(w_a_0,w_a_1, r_a*r_a ));
|
||||
triplets.push_back(triplet(w_a_0,w_b_1, 2 * r_a*r_b ));
|
||||
triplets.push_back(triplet(w_a_0,w_c_1, r_b*r_b ));
|
||||
|
||||
// Second term
|
||||
// w_b_0 = r_a r_c w_a + (r_b r_c + r_a r_d) w_b + r_b r_d w_c
|
||||
triplets.push_back(triplet(w_b_0,w_b_0, -1 ));
|
||||
triplets.push_back(triplet(w_b_0,w_a_1, r_a*r_c ));
|
||||
triplets.push_back(triplet(w_b_0,w_b_1, r_b*r_c + r_a*r_d ));
|
||||
triplets.push_back(triplet(w_b_0,w_c_1, r_b*r_d ));
|
||||
|
||||
// Third term
|
||||
// w_c_0 = r_c^2 w_a + 2 r_c r_d w_b + r_d^2 w_c
|
||||
triplets.push_back(triplet(w_c_0,w_c_0, -1 ));
|
||||
triplets.push_back(triplet(w_c_0,w_a_1, r_c*r_c ));
|
||||
triplets.push_back(triplet(w_c_0,w_b_1, 2 * r_c*r_d ));
|
||||
triplets.push_back(triplet(w_c_0,w_c_1, r_d*r_d ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SparseMatrix<double> 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<double> soft(3*F.rows(),3*F.rows());
|
||||
soft.setFromTriplets(triplets.begin(), triplets.end());
|
||||
|
||||
SparseMatrix<double> M;
|
||||
|
||||
M = L + soft;
|
||||
|
||||
// Solve Lx = b;
|
||||
|
||||
SparseLU<SparseMatrix<double> > 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<F.rows();++i)
|
||||
S.row(i) << x(i*3+0), x(i*3+1), x(i*3+2);
|
||||
|
||||
}
|
||||
|
||||
void FrameInterpolator::setConstraint(const int fid, const Eigen::VectorXd& v)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
double t_;
|
||||
VectorXd S_;
|
||||
|
||||
frame2canonical(TPs[fid],v,t_,S_);
|
||||
|
||||
Eigen::RowVectorXd v2;
|
||||
canonical2frame(TPs[fid], t_, S_, v2);
|
||||
|
||||
thetas(fid) = t_;
|
||||
thetas_c[fid] = true;
|
||||
|
||||
S.row(fid) = S_;
|
||||
S_c[fid] = true;
|
||||
|
||||
}
|
||||
|
||||
Eigen::MatrixXd FrameInterpolator::getFieldPerFace()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
MatrixXd R(F.rows(),6);
|
||||
for (unsigned i=0; i<F.rows(); ++i)
|
||||
{
|
||||
RowVectorXd v;
|
||||
canonical2frame(TPs[i],thetas(i),S.row(i),v);
|
||||
R.row(i) = v;
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
void FrameInterpolator::PolarDecomposition(Eigen::MatrixXd V, Eigen::MatrixXd& U, Eigen::MatrixXd& P)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
// Polar Decomposition
|
||||
JacobiSVD<MatrixXd> 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<b.size(); ++i)
|
||||
{
|
||||
VectorXd t(6); t << bc1.row(i).transpose(), bc2.row(i).transpose();
|
||||
field.setConstraint(b(i), t);
|
||||
}
|
||||
|
||||
// Solve
|
||||
field.solve();
|
||||
|
||||
// Copy back
|
||||
MatrixXd R = field.getFieldPerFace();
|
||||
FF1 = R.block(0, 0, R.rows(), 3);
|
||||
FF2 = R.block(0, 3, R.rows(), 3);
|
||||
}
|
||||
57
bundled_deps/libigl/igl/copyleft/comiso/frame_field.h
Normal file
57
bundled_deps/libigl/igl/copyleft/comiso/frame_field.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@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/.
|
||||
#ifndef IGL_COMISO_FRAMEFIELD_H
|
||||
#define IGL_COMISO_FRAMEFIELD_H
|
||||
|
||||
#include <igl/igl_inline.h>
|
||||
#include <igl/PI.h>
|
||||
#include <Eigen/Dense>
|
||||
#include <vector>
|
||||
|
||||
namespace igl
|
||||
{
|
||||
namespace copyleft
|
||||
{
|
||||
namespace comiso
|
||||
{
|
||||
// Generate a piecewise-constant frame-field field from a sparse set of constraints on faces
|
||||
// using the algorithm proposed in:
|
||||
// Frame Fields: Anisotropic and Non-Orthogonal Cross Fields
|
||||
// Daniele Panozzo, Enrico Puppo, Marco Tarini, Olga Sorkine-Hornung,
|
||||
// ACM Transactions on Graphics (SIGGRAPH, 2014)
|
||||
//
|
||||
// Inputs:
|
||||
// V #V by 3 list of mesh vertex coordinates
|
||||
// F #F by 3 list of mesh faces (must be triangles)
|
||||
// b #B by 1 list of constrained face indices
|
||||
// bc1 #B by 3 list of the constrained first representative vector of the frame field (up to permutation and sign)
|
||||
// bc2 #B by 3 list of the constrained second representative vector of the frame field (up to permutation and sign)
|
||||
//
|
||||
// Outputs:
|
||||
// FF1 #F by 3 the first representative vector of the frame field (up to permutation and sign)
|
||||
// FF2 #F by 3 the second representative vector of the frame field (up to permutation and sign)
|
||||
//
|
||||
// TODO: it now supports only soft constraints, should be extended to support both hard and soft constraints
|
||||
IGL_INLINE void 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IGL_STATIC_LIBRARY
|
||||
# include "frame_field.cpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1534
bundled_deps/libigl/igl/copyleft/comiso/miq.cpp
Normal file
1534
bundled_deps/libigl/igl/copyleft/comiso/miq.cpp
Normal file
File diff suppressed because it is too large
Load Diff
99
bundled_deps/libigl/igl/copyleft/comiso/miq.h
Normal file
99
bundled_deps/libigl/igl/copyleft/comiso/miq.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com>, Olga Diamanti <olga.diam@gmail.com>, Kevin Walliman <wkevin@student.ethz.ch>
|
||||
//
|
||||
// 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/.
|
||||
#ifndef IGL_COMISO_MIQ_H
|
||||
#define IGL_COMISO_MIQ_H
|
||||
#include "../../igl_inline.h"
|
||||
#include <Eigen/Core>
|
||||
#include <vector>
|
||||
|
||||
namespace igl
|
||||
{
|
||||
namespace copyleft
|
||||
{
|
||||
namespace comiso
|
||||
{
|
||||
// Global seamless parametrization aligned with a given per-face jacobian (PD1,PD2).
|
||||
// The algorithm is based on
|
||||
// "Mixed-Integer Quadrangulation" by D. Bommes, H. Zimmer, L. Kobbelt
|
||||
// ACM SIGGRAPH 2009, Article No. 77 (http://dl.acm.org/citation.cfm?id=1531383)
|
||||
// We thank Nico Pietroni for providing a reference implementation of MIQ
|
||||
// on which our code is based.
|
||||
|
||||
// Inputs:
|
||||
// V #V by 3 list of mesh vertex 3D positions
|
||||
// F #F by 3 list of faces indices in V
|
||||
// PD1 #V by 3 first line of the Jacobian per triangle
|
||||
// PD2 #V by 3 second line of the Jacobian per triangle
|
||||
// (optional, if empty it will be a vector in the tangent plane orthogonal to PD1)
|
||||
// scale global scaling for the gradient (controls the quads resolution)
|
||||
// stiffness weight for the stiffness iterations
|
||||
// direct_round greedily round all integer variables at once (greatly improves optimization speed but lowers quality)
|
||||
// iter stiffness iterations (0 = no stiffness)
|
||||
// local_iter number of local iterations for the integer rounding
|
||||
// do_round enables the integer rounding (disabling it could be useful for debugging)
|
||||
// round_vertices id of additional vertices that should be snapped to integer coordinates
|
||||
// hard_features #H by 2 list of pairs of vertices that belongs to edges that should be snapped to integer coordinates
|
||||
//
|
||||
// Output:
|
||||
// UV #UV by 2 list of vertices in 2D
|
||||
// FUV #FUV by 3 list of face indices in UV
|
||||
//
|
||||
// TODO: rename the parameters name in the cpp consistently
|
||||
// improve the handling of hard_features, right now it might fail in difficult cases
|
||||
|
||||
template <typename DerivedV, typename DerivedF, typename DerivedU>
|
||||
IGL_INLINE void miq(
|
||||
const Eigen::PlainObjectBase<DerivedV> &V,
|
||||
const Eigen::PlainObjectBase<DerivedF> &F,
|
||||
const Eigen::PlainObjectBase<DerivedV> &PD1,
|
||||
const Eigen::PlainObjectBase<DerivedV> &PD2,
|
||||
Eigen::PlainObjectBase<DerivedU> &UV,
|
||||
Eigen::PlainObjectBase<DerivedF> &FUV,
|
||||
double scale = 30.0,
|
||||
double stiffness = 5.0,
|
||||
bool direct_round = false,
|
||||
int iter = 5,
|
||||
int local_iter = 5,
|
||||
bool DoRound = true,bool SingularityRound=true,
|
||||
std::vector<int> round_vertices = std::vector<int>(),
|
||||
std::vector<std::vector<int> > hard_features = std::vector<std::vector<int> >());
|
||||
|
||||
// Helper function that allows to directly provided pre-combed bisectors for an already cut mesh
|
||||
// Additional input:
|
||||
// PD1_combed, PD2_combed : #F by 3 combed jacobian
|
||||
// BIS1_combed, BIS2_combed: #F by 3 pre combed bi-sectors
|
||||
// MMatch: #F by 3 list of per-corner integer PI/2 rotations
|
||||
// Singular: #V list of flag that denotes if a vertex is singular or not
|
||||
// SingularDegree: #V list of flag that denotes the degree of the singularity
|
||||
// Seams: #F by 3 list of per-corner flag that denotes seams
|
||||
|
||||
template <typename DerivedV, typename DerivedF, typename DerivedU>
|
||||
IGL_INLINE void miq(const Eigen::PlainObjectBase<DerivedV> &V,
|
||||
const Eigen::PlainObjectBase<DerivedF> &F,
|
||||
const Eigen::PlainObjectBase<DerivedV> &PD1_combed,
|
||||
const Eigen::PlainObjectBase<DerivedV> &PD2_combed,
|
||||
const Eigen::Matrix<int, Eigen::Dynamic, 3> &MMatch,
|
||||
const Eigen::Matrix<int, Eigen::Dynamic, 1> &Singular,
|
||||
const Eigen::Matrix<int, Eigen::Dynamic, 3> &Seams,
|
||||
Eigen::PlainObjectBase<DerivedU> &UV,
|
||||
Eigen::PlainObjectBase<DerivedF> &FUV,
|
||||
double GradientSize = 30.0,
|
||||
double Stiffness = 5.0,
|
||||
bool DirectRound = false,
|
||||
int iter = 5,
|
||||
int localIter = 5, bool DoRound = true,bool SingularityRound=true,
|
||||
std::vector<int> roundVertices = std::vector<int>(),
|
||||
std::vector<std::vector<int> > hardFeatures = std::vector<std::vector<int> >());
|
||||
};
|
||||
};
|
||||
};
|
||||
#ifndef IGL_STATIC_LIBRARY
|
||||
#include "miq.cpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
941
bundled_deps/libigl/igl/copyleft/comiso/nrosy.cpp
Normal file
941
bundled_deps/libigl/igl/copyleft/comiso/nrosy.cpp
Normal file
@@ -0,0 +1,941 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@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 "nrosy.h"
|
||||
|
||||
#include <igl/copyleft/comiso/nrosy.h>
|
||||
#include <igl/triangle_triangle_adjacency.h>
|
||||
#include <igl/edge_topology.h>
|
||||
#include <igl/per_face_normals.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
#include <Eigen/Sparse>
|
||||
#include <queue>
|
||||
|
||||
#include <gmm/gmm.h>
|
||||
#include <CoMISo/Solver/ConstrainedSolver.hh>
|
||||
#include <CoMISo/Solver/MISolver.hh>
|
||||
#include <CoMISo/Solver/GMM_Tools.hh>
|
||||
|
||||
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<bool> 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<bool> isBorderEdge;
|
||||
|
||||
// Per Edge information
|
||||
// Angle between two reference frames
|
||||
Eigen::VectorXd k;
|
||||
|
||||
// Jumps
|
||||
Eigen::VectorXi p;
|
||||
std::vector<bool> 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<Eigen::MatrixXd> TPs;
|
||||
|
||||
// System stuff
|
||||
Eigen::SparseMatrix<double> 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<EV.rows(); ++i)
|
||||
isBorderEdge[i] = (EF(i,0) == -1) || ((EF(i,1) == -1));
|
||||
|
||||
// Generate normals per face
|
||||
igl::per_face_normals(V, F, N);
|
||||
|
||||
// Generate reference frames
|
||||
for(unsigned fid=0; fid<F.rows(); ++fid)
|
||||
{
|
||||
// First edge
|
||||
Vector3d e1 = V.row(F(fid,1)) - V.row(F(fid,0));
|
||||
e1.normalize();
|
||||
Vector3d e2 = N.row(fid);
|
||||
e2 = e2.cross(e1);
|
||||
e2.normalize();
|
||||
|
||||
MatrixXd TP(2,3);
|
||||
TP << e1.transpose(), e2.transpose();
|
||||
TPs.push_back(TP);
|
||||
}
|
||||
|
||||
// Alloc internal variables
|
||||
angles = VectorXd::Zero(F.rows());
|
||||
p = VectorXi::Zero(EV.rows());
|
||||
pFixed.resize(EV.rows());
|
||||
k = VectorXd::Zero(EV.rows());
|
||||
singularityIndex = VectorXd::Zero(V.rows());
|
||||
|
||||
// Reset the constraints
|
||||
resetConstraints();
|
||||
|
||||
// Compute k, differences between reference frames
|
||||
computek();
|
||||
|
||||
softAlpha = 0.5;
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::setSoftAlpha(double alpha)
|
||||
{
|
||||
assert(alpha >= 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<int> id_t;
|
||||
int count = 0;
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
if (!isHard[i])
|
||||
{
|
||||
tag_t(i) = count++;
|
||||
id_t.push_back(i);
|
||||
}
|
||||
|
||||
unsigned count_t = id_t.size();
|
||||
|
||||
tag_p = VectorXi::Constant(EF.rows(),-1);
|
||||
vector<int> id_p;
|
||||
for(unsigned i=0; i<EF.rows(); ++i)
|
||||
{
|
||||
if (!pFixed[i])
|
||||
{
|
||||
// if it is not fixed then it is a variable
|
||||
tag_p(i) = count++;
|
||||
}
|
||||
|
||||
// if it is not a border edge,
|
||||
if (!isBorderEdge[i])
|
||||
{
|
||||
// and it is not between two fixed faces
|
||||
if (!(isHard[EF(i,0)] && isHard[EF(i,1)]))
|
||||
{
|
||||
// then it participates in the energy!
|
||||
id_p.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned count_p = count - count_t;
|
||||
// System sizes: A (count_t + count_p) x (count_t + count_p)
|
||||
// b (count_t + count_p)
|
||||
|
||||
b = VectorXd::Zero(count_t + count_p);
|
||||
|
||||
std::vector<Eigen::Triplet<double> > T;
|
||||
T.reserve(3 * 4 * count_p);
|
||||
|
||||
for(unsigned r=0; r<id_p.size(); ++r)
|
||||
{
|
||||
int eid = id_p[r];
|
||||
int i = EF(eid,0);
|
||||
int j = EF(eid,1);
|
||||
bool isFixed_i = isHard[i];
|
||||
bool isFixed_j = isHard[j];
|
||||
bool isFixed_p = pFixed[eid];
|
||||
int row;
|
||||
// (i)-th row: t_i [ 2 -2 4pi/N 2 ]
|
||||
if (!isFixed_i)
|
||||
{
|
||||
row = tag_t[i];
|
||||
if (isFixed_i) b(row) += -2 * hard[i]; else T.push_back(Eigen::Triplet<double>(row,tag_t[i] , 2 ));
|
||||
if (isFixed_j) b(row) += 2 * hard[j]; else T.push_back(Eigen::Triplet<double>(row,tag_t[j] ,-2 ));
|
||||
if (isFixed_p) b(row) += -((4 * igl::PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet<double>(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<double>(row,tag_t[i] , -2 ));
|
||||
if (isFixed_j) b(row) += -2 * hard[j]; else T.push_back(Eigen::Triplet<double>(row,tag_t[j] , 2 ));
|
||||
if (isFixed_p) b(row) += ((4 * igl::PI)/Nd) * p[eid] ; else T.push_back(Eigen::Triplet<double>(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<double>(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<double>(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<double>(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<double>(count_t + count_p, count_t + count_p);
|
||||
A.setFromTriplets(T.begin(), T.end());
|
||||
|
||||
// Soft constraints
|
||||
bool addSoft = false;
|
||||
|
||||
for(unsigned i=0; i<wSoft.size();++i)
|
||||
if (wSoft[i] != 0)
|
||||
addSoft = true;
|
||||
|
||||
if (addSoft)
|
||||
{
|
||||
cerr << " Adding soft here: " << endl;
|
||||
cerr << " softAplha: " << softAlpha << endl;
|
||||
VectorXd bSoft = VectorXd::Zero(count_t + count_p);
|
||||
|
||||
std::vector<Eigen::Triplet<double> > TSoft;
|
||||
TSoft.reserve(2 * count_p);
|
||||
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
{
|
||||
int varid = tag_t[i];
|
||||
if (varid != -1) // if it is a variable in the system
|
||||
{
|
||||
TSoft.push_back(Eigen::Triplet<double>(varid,varid,wSoft[i]));
|
||||
bSoft[varid] += wSoft[i] * soft[i];
|
||||
}
|
||||
}
|
||||
SparseMatrix<double> 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<TSoft.size(); ++i)
|
||||
// s << TSoft[i].row() << " " << TSoft[i].col() << " " << TSoft[i].value() << endl;
|
||||
// s.close();
|
||||
|
||||
// ofstream s2("/Users/daniele/bs.txt");
|
||||
// for(unsigned i=0; i<bSoft.rows(); ++i)
|
||||
// s2 << bSoft(i) << endl;
|
||||
// s2.close();
|
||||
|
||||
// Stupid Eigen bug
|
||||
SparseMatrix<double> Atmp (count_t + count_p, count_t + count_p);
|
||||
SparseMatrix<double> Atmp2(count_t + count_p, count_t + count_p);
|
||||
SparseMatrix<double> 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<A.outerSize(); ++k)
|
||||
// for (SparseMatrix<double>::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<b.rows(); ++i)
|
||||
// s2 << b(i) << endl;
|
||||
// s2.close();
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::solveNoRoundings()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
// Solve the linear system
|
||||
SimplicialLDLT<SparseMatrix<double> > solver;
|
||||
solver.compute(A);
|
||||
VectorXd x = solver.solve(b);
|
||||
|
||||
// Copy the result back
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
if (tag_t[i] != -1)
|
||||
angles[i] = x(tag_t[i]);
|
||||
else
|
||||
angles[i] = hard[i];
|
||||
|
||||
for(unsigned i=0; i<EF.rows(); ++i)
|
||||
if(tag_p[i] != -1)
|
||||
p[i] = roundl(x[tag_p[i]]);
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::solveRoundings()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
unsigned n = A.rows();
|
||||
|
||||
gmm::col_matrix< gmm::wsvector< double > > gmm_A;
|
||||
std::vector<double> gmm_b;
|
||||
std::vector<int> ids_to_round;
|
||||
std::vector<double> x;
|
||||
|
||||
gmm_A.resize(n,n);
|
||||
gmm_b.resize(n);
|
||||
x.resize(n);
|
||||
|
||||
// Copy A
|
||||
for (int k=0; k<A.outerSize(); ++k)
|
||||
for (SparseMatrix<double>::InnerIterator it(A,k); it; ++it)
|
||||
{
|
||||
gmm_A(it.row(),it.col()) += it.value();
|
||||
}
|
||||
|
||||
// Copy b
|
||||
for(unsigned i=0; i<n;++i)
|
||||
gmm_b[i] = b[i];
|
||||
|
||||
// Set variables to round
|
||||
ids_to_round.clear();
|
||||
for(unsigned i=0; i<tag_p.size();++i)
|
||||
if(tag_p[i] != -1)
|
||||
ids_to_round.push_back(tag_p[i]);
|
||||
|
||||
// Empty constraints
|
||||
gmm::row_matrix< gmm::wsvector< double > > 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<F.rows(); ++i)
|
||||
if (tag_t[i] != -1)
|
||||
angles[i] = x[tag_t[i]];
|
||||
else
|
||||
angles[i] = hard[i];
|
||||
|
||||
for(unsigned i=0; i<EF.rows(); ++i)
|
||||
if(tag_p[i] != -1)
|
||||
p[i] = roundl(x[tag_p[i]]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::roundAndFix()
|
||||
{
|
||||
for(unsigned i=0; i<p.rows(); ++i)
|
||||
pFixed[i] = true;
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::roundAndFixToZero()
|
||||
{
|
||||
for(unsigned i=0; i<p.rows(); ++i)
|
||||
{
|
||||
pFixed[i] = true;
|
||||
p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::solve(const int N)
|
||||
{
|
||||
// Reduce the search space by fixing matchings
|
||||
reduceSpace();
|
||||
|
||||
// Build the system
|
||||
prepareSystemMatrix(N);
|
||||
|
||||
// Solve with integer roundings
|
||||
solveRoundings();
|
||||
|
||||
// This is a very greedy solving strategy
|
||||
// // Solve with no roundings
|
||||
// solveNoRoundings();
|
||||
//
|
||||
// // Round all p and fix them
|
||||
// roundAndFix();
|
||||
//
|
||||
// // Build the system
|
||||
// prepareSystemMatrix(N);
|
||||
//
|
||||
// // Solve with no roundings (they are all fixed)
|
||||
// solveNoRoundings();
|
||||
|
||||
// Find the cones
|
||||
findCones(N);
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::setConstraintHard(const int fid, const Eigen::Vector3d& v)
|
||||
{
|
||||
isHard[fid] = true;
|
||||
hard(fid) = convert3DtoLocal(fid, v);
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::setConstraintSoft(const int fid, const double w, const Eigen::Vector3d& v)
|
||||
{
|
||||
wSoft(fid) = w;
|
||||
soft(fid) = convert3DtoLocal(fid, v);
|
||||
}
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::resetConstraints()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
isHard.resize(F.rows());
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
isHard[i] = false;
|
||||
hard = VectorXd::Zero(F.rows());
|
||||
|
||||
wSoft = VectorXd::Zero(F.rows());
|
||||
soft = VectorXd::Zero(F.rows());
|
||||
}
|
||||
|
||||
Eigen::MatrixXd igl::copyleft::comiso::NRosyField::getFieldPerFace()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
MatrixXd result(F.rows(),3);
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
result.row(i) = convertLocalto3D(i, angles(i));
|
||||
return result;
|
||||
}
|
||||
|
||||
Eigen::MatrixXd igl::copyleft::comiso::NRosyField::getFFieldPerFace()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
MatrixXd result(F.rows(),6);
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
{
|
||||
Vector3d v1 = convertLocalto3D(i, angles(i));
|
||||
Vector3d n = N.row(i);
|
||||
Vector3d v2 = n.cross(v1);
|
||||
v1.normalize();
|
||||
v2.normalize();
|
||||
|
||||
result.block(i,0,1,3) = v1.transpose();
|
||||
result.block(i,3,1,3) = v2.transpose();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void igl::copyleft::comiso::NRosyField::computek()
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
// For every non-border edge
|
||||
for (unsigned eid=0; eid<EF.rows(); ++eid)
|
||||
{
|
||||
if (!isBorderEdge[eid])
|
||||
{
|
||||
int fid0 = EF(eid,0);
|
||||
int fid1 = EF(eid,1);
|
||||
|
||||
Vector3d N0 = N.row(fid0);
|
||||
Vector3d N1 = N.row(fid1);
|
||||
|
||||
// find common edge on triangle 0 and 1
|
||||
int fid0_vc = -1;
|
||||
int fid1_vc = -1;
|
||||
for (unsigned i=0;i<3;++i)
|
||||
{
|
||||
if (EV(eid,0) == F(fid0,i))
|
||||
fid0_vc = i;
|
||||
if (EV(eid,1) == F(fid1,i))
|
||||
fid1_vc = i;
|
||||
}
|
||||
assert(fid0_vc != -1);
|
||||
assert(fid1_vc != -1);
|
||||
|
||||
Vector3d common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc));
|
||||
common_edge.normalize();
|
||||
|
||||
// Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis
|
||||
MatrixXd P(3,3);
|
||||
VectorXd o = V.row(F(fid0,fid0_vc));
|
||||
VectorXd tmp = -N0.cross(common_edge);
|
||||
P << common_edge, tmp, N0;
|
||||
P.transposeInPlace();
|
||||
|
||||
|
||||
MatrixXd V0(3,3);
|
||||
V0.row(0) = V.row(F(fid0,0)).transpose() -o;
|
||||
V0.row(1) = V.row(F(fid0,1)).transpose() -o;
|
||||
V0.row(2) = V.row(F(fid0,2)).transpose() -o;
|
||||
|
||||
V0 = (P*V0.transpose()).transpose();
|
||||
|
||||
assert(V0(0,2) < 10e-10);
|
||||
assert(V0(1,2) < 10e-10);
|
||||
assert(V0(2,2) < 10e-10);
|
||||
|
||||
MatrixXd V1(3,3);
|
||||
V1.row(0) = V.row(F(fid1,0)).transpose() -o;
|
||||
V1.row(1) = V.row(F(fid1,1)).transpose() -o;
|
||||
V1.row(2) = V.row(F(fid1,2)).transpose() -o;
|
||||
V1 = (P*V1.transpose()).transpose();
|
||||
|
||||
assert(V1(fid1_vc,2) < 10e-10);
|
||||
assert(V1((fid1_vc+1)%3,2) < 10e-10);
|
||||
|
||||
// compute rotation R such that R * N1 = N0
|
||||
// i.e. map both triangles to the same plane
|
||||
double alpha = -atan2(V1((fid1_vc+2)%3,2),V1((fid1_vc+2)%3,1));
|
||||
|
||||
MatrixXd R(3,3);
|
||||
R << 1, 0, 0,
|
||||
0, cos(alpha), -sin(alpha) ,
|
||||
0, sin(alpha), cos(alpha);
|
||||
V1 = (R*V1.transpose()).transpose();
|
||||
|
||||
assert(V1(0,2) < 10e-10);
|
||||
assert(V1(1,2) < 10e-10);
|
||||
assert(V1(2,2) < 10e-10);
|
||||
|
||||
// measure the angle between the reference frames
|
||||
// k_ij is the angle between the triangle on the left and the one on the right
|
||||
VectorXd ref0 = V0.row(1) - V0.row(0);
|
||||
VectorXd ref1 = V1.row(1) - V1.row(0);
|
||||
|
||||
ref0.normalize();
|
||||
ref1.normalize();
|
||||
|
||||
double ktemp = atan2(ref1(1),ref1(0)) - atan2(ref0(1),ref0(0));
|
||||
|
||||
// just to be sure, rotate ref0 using angle ktemp...
|
||||
MatrixXd R2(2,2);
|
||||
R2 << cos(ktemp), -sin(ktemp), sin(ktemp), cos(ktemp);
|
||||
|
||||
tmp = R2*ref0.head<2>();
|
||||
|
||||
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<EV.rows(); ++i)
|
||||
pFixed[i] = false;
|
||||
|
||||
vector<VectorXd> debug;
|
||||
|
||||
// debug
|
||||
// MatrixXd B(F.rows(),3);
|
||||
// for(unsigned i=0; i<F.rows(); ++i)
|
||||
// B.row(i) = 1./3. * (V.row(F(i,0)) + V.row(F(i,1)) + V.row(F(i,2)));
|
||||
|
||||
vector<bool> visited(EV.rows());
|
||||
for(unsigned i=0; i<EV.rows(); ++i)
|
||||
visited[i] = false;
|
||||
|
||||
vector<bool> starting(EV.rows());
|
||||
for(unsigned i=0; i<EV.rows(); ++i)
|
||||
starting[i] = false;
|
||||
|
||||
queue<int> q;
|
||||
for(unsigned i=0; i<F.rows(); ++i)
|
||||
if (isHard[i] || wSoft[i] != 0)
|
||||
{
|
||||
q.push(i);
|
||||
starting[i] = true;
|
||||
}
|
||||
|
||||
// Reduce the search space (see MI paper)
|
||||
while (!q.empty())
|
||||
{
|
||||
int c = q.front();
|
||||
q.pop();
|
||||
|
||||
visited[c] = true;
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
int eid = FE(c,i);
|
||||
int fid = TT(c,i);
|
||||
|
||||
// skip borders
|
||||
if (fid != -1)
|
||||
{
|
||||
assert((EF(eid,0) == c && EF(eid,1) == fid) || (EF(eid,1) == c && EF(eid,0) == fid));
|
||||
// for every neighbouring face
|
||||
if (!visited[fid] && !starting[fid])
|
||||
{
|
||||
pFixed[eid] = true;
|
||||
p[eid] = 0;
|
||||
visited[fid] = true;
|
||||
q.push(fid);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// fix borders
|
||||
pFixed[eid] = true;
|
||||
p[eid] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Force matchings between fixed faces
|
||||
for(unsigned i=0; i<F.rows();++i)
|
||||
{
|
||||
if (isHard[i])
|
||||
{
|
||||
for(unsigned int j=0; j<3; ++j)
|
||||
{
|
||||
int fid = TT(i,j);
|
||||
if ((fid!=-1) && (isHard[fid]))
|
||||
{
|
||||
// i and fid are adjacent and fixed
|
||||
int eid = FE(i,j);
|
||||
int fid0 = EF(eid,0);
|
||||
int fid1 = EF(eid,1);
|
||||
|
||||
pFixed[eid] = true;
|
||||
p[eid] = roundl(2.0/igl::PI*(hard(fid1) - hard(fid0) - k(eid)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// std::ofstream s("/Users/daniele/debug.txt");
|
||||
// for(unsigned i=0; i<debug.size(); i += 2)
|
||||
// s << debug[i].transpose() << " " << debug[i+1].transpose() << endl;
|
||||
// s.close();
|
||||
|
||||
}
|
||||
|
||||
double igl::copyleft::comiso::NRosyField::convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
// Project onto the tangent plane
|
||||
Vector2d vp = TPs[fid] * v;
|
||||
|
||||
// Convert to angle
|
||||
return atan2(vp(1),vp(0));
|
||||
}
|
||||
|
||||
Eigen::Vector3d igl::copyleft::comiso::NRosyField::convertLocalto3D(unsigned fid, double a)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace Eigen;
|
||||
|
||||
Vector2d vp(cos(a),sin(a));
|
||||
return vp.transpose() * TPs[fid];
|
||||
}
|
||||
|
||||
Eigen::VectorXd igl::copyleft::comiso::NRosyField::angleDefect()
|
||||
{
|
||||
Eigen::VectorXd A = Eigen::VectorXd::Constant(V.rows(),-2*igl::PI);
|
||||
|
||||
for (unsigned i=0; i < F.rows(); ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
Eigen::VectorXd a = V.row(F(i,(j+1)%3)) - V.row(F(i,j));
|
||||
Eigen::VectorXd b = V.row(F(i,(j+2)%3)) - V.row(F(i,j));
|
||||
double t = a.transpose()*b;
|
||||
t /= (a.norm() * b.norm());
|
||||
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<b.size();++i)
|
||||
solver.setConstraintHard(b(i),bc.row(i));
|
||||
|
||||
// Add soft constraints
|
||||
for (unsigned i=0; i<b_soft.size();++i)
|
||||
solver.setConstraintSoft(b_soft(i),w_soft(i),bc_soft.row(i));
|
||||
|
||||
// Set the soft constraints global weight
|
||||
solver.setSoftAlpha(soft);
|
||||
|
||||
// Interpolate
|
||||
solver.solve(N);
|
||||
|
||||
// Copy the result back
|
||||
R = solver.getFieldPerFace();
|
||||
|
||||
// Extract singularity indices
|
||||
S = solver.getSingularityIndexPerVertex();
|
||||
}
|
||||
|
||||
|
||||
IGL_INLINE void igl::copyleft::comiso::nrosy(
|
||||
const Eigen::MatrixXd& V,
|
||||
const Eigen::MatrixXi& F,
|
||||
const Eigen::VectorXi& b,
|
||||
const Eigen::MatrixXd& bc,
|
||||
const int N,
|
||||
Eigen::MatrixXd& R,
|
||||
Eigen::VectorXd& S
|
||||
)
|
||||
{
|
||||
// Init solver
|
||||
igl::copyleft::comiso::NRosyField solver(V,F);
|
||||
|
||||
// Add hard constraints
|
||||
for (unsigned i=0; i<b.size();++i)
|
||||
solver.setConstraintHard(b(i),bc.row(i));
|
||||
|
||||
// Interpolate
|
||||
solver.solve(N);
|
||||
|
||||
// Copy the result back
|
||||
R = solver.getFieldPerFace();
|
||||
|
||||
// Extract singularity indices
|
||||
S = solver.getSingularityIndexPerVertex();
|
||||
}
|
||||
73
bundled_deps/libigl/igl/copyleft/comiso/nrosy.h
Normal file
73
bundled_deps/libigl/igl/copyleft/comiso/nrosy.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// This file is part of libigl, a simple c++ geometry processing library.
|
||||
//
|
||||
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@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/.
|
||||
#ifndef IGL_COMISO_NROSY_H
|
||||
#define IGL_COMISO_NROSY_H
|
||||
|
||||
#include <iostream>
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Sparse>
|
||||
#include <vector>
|
||||
#include "../../igl_inline.h"
|
||||
#include "../../PI.h"
|
||||
|
||||
namespace igl
|
||||
{
|
||||
namespace copyleft
|
||||
{
|
||||
namespace comiso
|
||||
{
|
||||
// Generate a N-RoSy field from a sparse set of constraints
|
||||
//
|
||||
// Inputs:
|
||||
// V #V by 3 list of mesh vertex coordinates
|
||||
// F #F by 3 list of mesh faces (must be triangles)
|
||||
// b #B by 1 list of constrained face indices
|
||||
// bc #B by 3 list of representative vectors for the constrained
|
||||
// faces
|
||||
// b_soft #S by 1 b for soft constraints
|
||||
// w_soft #S by 1 weight for the soft constraints (0-1)
|
||||
// bc_soft #S by 3 bc for soft constraints
|
||||
// N the degree of the N-RoSy vector field
|
||||
// soft the strength of the soft constraints w.r.t. smoothness
|
||||
// (0 -> smoothness only, 1->constraints only)
|
||||
// Outputs:
|
||||
// R #F by 3 the representative vectors of the interpolated field
|
||||
// S #V by 1 the singularity index for each vertex (0 = regular)
|
||||
IGL_INLINE void 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
|
||||
);
|
||||
//wrapper for the case without soft constraints
|
||||
IGL_INLINE void nrosy(
|
||||
const Eigen::MatrixXd& V,
|
||||
const Eigen::MatrixXi& F,
|
||||
const Eigen::VectorXi& b,
|
||||
const Eigen::MatrixXd& bc,
|
||||
const int N,
|
||||
Eigen::MatrixXd& R,
|
||||
Eigen::VectorXd& S
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IGL_STATIC_LIBRARY
|
||||
# include "nrosy.cpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user