diff --git a/include/igl/n_polyvector_general.cpp b/include/igl/n_polyvector_general.cpp new file mode 100644 index 000000000..e3d494458 --- /dev/null +++ b/include/igl/n_polyvector_general.cpp @@ -0,0 +1,491 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Olga Diamanti +// +// 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 +#include +#include +#include +#include +#include +#include + +#include + +namespace igl { + template + class GeneralPolyVectorFieldFinder + { + private: + const Eigen::PlainObjectBase &V; + const Eigen::PlainObjectBase &F; int numF; + const int n; + + Eigen::MatrixXi EV; int numE; + Eigen::MatrixXi F2E; + Eigen::MatrixXi E2F; + Eigen::VectorXd K; + + Eigen::VectorXi isBorderEdge; + int numInteriorEdges; + Eigen::Matrix E2F_int; + Eigen::VectorXi indInteriorToFull; + Eigen::VectorXi indFullToInterior; + + Eigen::PlainObjectBase B1, B2, FN; + + IGL_INLINE void computek(); + IGL_INLINE void setFieldFromGeneralCoefficients(const std::vector, Eigen::Dynamic,1>> &coeffs, + std::vector > &pv); + IGL_INLINE void computeCoefficientLaplacian(int n, Eigen::SparseMatrix > &D); + IGL_INLINE void getGeneralCoeffConstraints(const Eigen::VectorXi &isConstrained, + const Eigen::Matrix &cfW, + int k, + const Eigen::VectorXi &rootsIndex, + Eigen::Matrix, Eigen::Dynamic,1> &Ck); + IGL_INLINE void precomputeInteriorEdges(); + + + IGL_INLINE void minQuadWithKnownMini(const Eigen::SparseMatrix > &Q, + const Eigen::SparseMatrix > &f, + const Eigen::VectorXi isConstrained, + const Eigen::Matrix, Eigen::Dynamic, 1> &xknown, + Eigen::Matrix, Eigen::Dynamic, 1> &x); + + public: + IGL_INLINE GeneralPolyVectorFieldFinder(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const int &_n); + IGL_INLINE bool solve(const Eigen::VectorXi &isConstrained, + const Eigen::Matrix &cfW, + const Eigen::VectorXi &rootsIndex, + Eigen::Matrix &output); + + }; +} + +template +IGL_INLINE igl::GeneralPolyVectorFieldFinder:: + GeneralPolyVectorFieldFinder(const Eigen::PlainObjectBase &_V, + const Eigen::PlainObjectBase &_F, + const int &_n): +V(_V), +F(_F), +numF(_F.rows()), +n(_n) +{ + + igl::edge_topology(V,F,EV,F2E,E2F); + numE = EV.rows(); + + + precomputeInteriorEdges(); + + igl::local_basis(V,F,B1,B2,FN); + + computek(); + +}; + + +template +IGL_INLINE void igl::GeneralPolyVectorFieldFinder:: +precomputeInteriorEdges() +{ + // Flag border edges + numInteriorEdges = 0; + isBorderEdge.setZero(numE,1); + indFullToInterior = -1.*Eigen::VectorXi::Ones(numE,1); + + for(unsigned i=0; i +IGL_INLINE void igl::GeneralPolyVectorFieldFinder:: +minQuadWithKnownMini(const Eigen::SparseMatrix > &Q, + const Eigen::SparseMatrix > &f, + const Eigen::VectorXi isConstrained, + const Eigen::Matrix, Eigen::Dynamic, 1> &xknown, + Eigen::Matrix, Eigen::Dynamic, 1> &x) +{ + int N = Q.rows(); + + int nc = xknown.rows(); + Eigen::VectorXi known; known.setZero(nc,1); + Eigen::VectorXi unknown; unknown.setZero(N-nc,1); + + int indk = 0, indu = 0; + for (int i = 0; i> Quu, Quk; + + igl::slice(Q,unknown, unknown, Quu); + igl::slice(Q,unknown, known, Quk); + + + std::vector > > tripletList; + + Eigen::SparseMatrix > fu(N-nc,1); + + igl::slice(f,unknown, Eigen::VectorXi::Zero(1,1), fu); + + Eigen::SparseMatrix > rhs = (Quk*xknown).sparseView()+.5*fu; + + Eigen::SparseLU< Eigen::SparseMatrix>> solver; + solver.compute(-Quu); + if(solver.info()!=Eigen::Success) + { + std::cerr<<"Decomposition failed!"<> b = solver.solve(rhs); + if(solver.info()!=Eigen::Success) + { + std::cerr<<"Solving failed!"< +IGL_INLINE bool igl::GeneralPolyVectorFieldFinder:: + solve(const Eigen::VectorXi &isConstrained, + const Eigen::Matrix &cfW, + const Eigen::VectorXi &rootsIndex, + Eigen::Matrix &output) +{ + + // polynomial is of the form: + // z^(2n) + + // -c[0]z^(2n-1) + + // c[1]z^(2n-2) + + // -c[2]z^(2n-3) + + // ... + + // (-1)^n c[n-1] + + std::vector, Eigen::Dynamic,1>> coeffs(n,Eigen::Matrix, Eigen::Dynamic,1>::Zero(numF, 1)); + + for (int i =0; i, Eigen::Dynamic,1> Ck; + getGeneralCoeffConstraints(isConstrained, + cfW, + i, + rootsIndex, + Ck); + + Eigen::SparseMatrix > DD; + computeCoefficientLaplacian(degree, DD); + Eigen::SparseMatrix > f; f.resize(numF,1); + + minQuadWithKnownMini(DD, f, isConstrained, Ck, coeffs[i]); + } + + std::vector > pv; + setFieldFromGeneralCoefficients(coeffs, pv); + + output.setZero(numF,3*n); + for (int fi=0; fi &b1 = B1.row(fi); + const Eigen::Matrix &b2 = B2.row(fi); + for (int i=0; i +IGL_INLINE void igl::GeneralPolyVectorFieldFinder::setFieldFromGeneralCoefficients(const std::vector, Eigen::Dynamic,1>> &coeffs, + std::vector> &pv) +{ + pv.assign(n, Eigen::Matrix::Zero(numF, 2)); + for (int i = 0; i , Eigen::Dynamic,1> polyCoeff; + polyCoeff.setZero(n+1,1); + polyCoeff[0] = 1.; + int sign = 1; + for (int k =0; k, Eigen::Dynamic,1> roots; + igl::polyRoots, typename DerivedV::Scalar >(polyCoeff,roots); + for (int k=0; k +IGL_INLINE void igl::GeneralPolyVectorFieldFinder::computeCoefficientLaplacian(int n, Eigen::SparseMatrix > &D) +{ + std::vector >> tripletList; + + // For every non-border edge + for (unsigned eid=0; eid >(fid0, + fid0, + std::complex(1.))); + tripletList.push_back(Eigen::Triplet >(fid1, + fid1, + std::complex(1.))); + tripletList.push_back(Eigen::Triplet >(fid0, + fid1, + -1.*std::polar(1.,-1.*n*K[eid]))); + tripletList.push_back(Eigen::Triplet >(fid1, + fid0, + -1.*std::polar(1.,1.*n*K[eid]))); + + } + } + D.resize(numF,numF); + D.setFromTriplets(tripletList.begin(), tripletList.end()); + + +} + +//this gives the coefficients without the (-1)^k that multiplies them +template +IGL_INLINE void igl::GeneralPolyVectorFieldFinder::getGeneralCoeffConstraints(const Eigen::VectorXi &isConstrained, + const Eigen::Matrix &cfW, + int k, + const Eigen::VectorXi &rootsIndex, + Eigen::Matrix, Eigen::Dynamic,1> &Ck) +{ + int numConstrained = isConstrained.sum(); + Ck.resize(numConstrained,1); + // int n = rootsIndex.cols(); + + std::vector> allCombs; + igl::nchoosek(0,k+1,n,allCombs); + + int ind = 0; + for (int fi = 0; fi &b1 = B1.row(fi); + const Eigen::Matrix &b2 = B2.row(fi); + if(isConstrained[fi]) + { + std::complex ck(0); + + for (int j = 0; j < allCombs.size(); ++j) + { + std::complex tk(1.); + //collect products + for (int i = 0; i < allCombs[j].size(); ++i) + { + int index = allCombs[j][i]; + + int ri = rootsIndex[index]; + Eigen::Matrix w; + if (ri>0) + w = cfW.block(fi,3*(ri-1),1,3); + else + w = -cfW.block(fi,3*(-ri-1),1,3); + typename DerivedV::Scalar w0 = w.dot(b1); + typename DerivedV::Scalar w1 = w.dot(b2); + std::complex u(w0,w1); + tk*= u; + } + //collect sum + ck += tk; + } + Ck(ind) = ck; + ind ++; + } + } + + +} + +template +IGL_INLINE void igl::GeneralPolyVectorFieldFinder::computek() +{ + K.setZero(numE); + // For every non-border edge + for (unsigned eid=0; eid N0 = FN.row(fid0); + Eigen::Matrix N1 = FN.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 (F2E(fid0,i) == eid) + fid0_vc = i; + if (F2E(fid1,i) == eid) + fid1_vc = i; + } + assert(fid0_vc != -1); + assert(fid1_vc != -1); + + Eigen::Matrix 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 + Eigen::Matrix P; + Eigen::Matrix o = V.row(F(fid0,fid0_vc)); + Eigen::Matrix tmp = -N0.cross(common_edge); + P << common_edge, tmp, N0; +// P.transposeInPlace(); + + + Eigen::Matrix V0; + V0.row(0) = V.row(F(fid0,0)) -o; + V0.row(1) = V.row(F(fid0,1)) -o; + V0.row(2) = V.row(F(fid0,2)) -o; + + V0 = (P*V0.transpose()).transpose(); + +// assert(V0(0,2) < 1e-10); +// assert(V0(1,2) < 1e-10); +// assert(V0(2,2) < 1e-10); + + Eigen::Matrix V1; + V1.row(0) = V.row(F(fid1,0)) -o; + V1.row(1) = V.row(F(fid1,1)) -o; + V1.row(2) = V.row(F(fid1,2)) -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)); + + Eigen::Matrix R; + R << 1, 0, 0, + 0, cos(alpha), -sin(alpha) , + 0, sin(alpha), cos(alpha); + V1 = (R*V1.transpose()).transpose(); + +// assert(V1(0,2) < 1e-10); +// assert(V1(1,2) < 1e-10); +// assert(V1(2,2) < 1e-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 + Eigen::Matrix ref0 = V0.row(1) - V0.row(0); + Eigen::Matrix 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... + Eigen::Matrix R2; + R2 << cos(ktemp), -sin(ktemp), sin(ktemp), cos(ktemp); + + Eigen::Matrix tmp1 = R2*(ref0.head(2)).transpose(); + +// assert(tmp1(0) - ref1(0) < 1e-10); +// assert(tmp1(1) - ref1(1) < 1e-10); + + K[eid] = ktemp; + } + } + +} + + +IGL_INLINE void igl::n_polyvector_general(const Eigen::MatrixXd &V, + const Eigen::MatrixXi &F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc, + const Eigen::VectorXi &I, + Eigen::MatrixXd &output) +{ + Eigen::VectorXi isConstrained = Eigen::VectorXi::Constant(F.rows(),0); + Eigen::MatrixXd cfW = Eigen::MatrixXd::Constant(F.rows(),bc.cols(),0); + + for(unsigned i=0; i pvff(V,F,n); + pvff.solve(isConstrained, cfW, I, output); +} + + +#ifdef IGL_STATIC_LIBRARY +// Explicit template specialization +#endif diff --git a/include/igl/n_polyvector_general.h b/include/igl/n_polyvector_general.h new file mode 100644 index 000000000..e8f994440 --- /dev/null +++ b/include/igl/n_polyvector_general.h @@ -0,0 +1,41 @@ +// This file is part of libigl, a simple c++ geometry processing library. +// +// Copyright (C) 2014 Olga Diamanti +// +// 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_N_POLYVECTOR_GENERAL +#define IGL_N_POLYVECTOR_GENERAL +#include "igl_inline.h" + +#include + +namespace igl { + //todo + /// Given 2 vectors centered on origin calculate the rotation matrix from first to the second + + // Inputs: + // v0, v1 the two #3 by 1 vectors + // normalized boolean, if false, then the vectors are normalized prior to the calculation + // Output: + // 3 by 3 rotation matrix that takes v0 to v1 + // + + IGL_INLINE void n_polyvector_general(const Eigen::MatrixXd& V, + const Eigen::MatrixXi& F, + const Eigen::VectorXi& b, + const Eigen::MatrixXd& bc, + const Eigen::VectorXi &I, + Eigen::MatrixXd &output); + +}; + + +#ifndef IGL_STATIC_LIBRARY +#include "n_polyvector_general.cpp" +#endif + + +#endif /* defined(IGL_N_POLYVECTOR) */ diff --git a/tutorial/511_PolyVectorFieldGeneral/CMakeLists.txt b/tutorial/511_PolyVectorFieldGeneral/CMakeLists.txt new file mode 100644 index 000000000..b44c6fc42 --- /dev/null +++ b/tutorial/511_PolyVectorFieldGeneral/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.6) +project(511_PolyVectorFieldGeneral) + +include("../CMakeLists.shared") + +set(SOURCES +${PROJECT_SOURCE_DIR}/main.cpp +) + +add_executable(${PROJECT_NAME}_bin ${SOURCES} ${SHARED_SOURCES}) +target_link_libraries(${PROJECT_NAME}_bin ${SHARED_LIBRARIES} ${LIBCOMISO_LIBRARY}) diff --git a/tutorial/511_PolyVectorFieldGeneral/main.cpp b/tutorial/511_PolyVectorFieldGeneral/main.cpp new file mode 100755 index 000000000..f83d1e2b6 --- /dev/null +++ b/tutorial/511_PolyVectorFieldGeneral/main.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Input mesh +Eigen::MatrixXd V; +Eigen::MatrixXi F; + +// Per face bases +Eigen::MatrixXd B1,B2,B3; + +// Face barycenters +Eigen::MatrixXd B; + +// Scale for visualizing the fields +double global_scale; + +// Random length factor +double rand_factor = 5; + +// Create a random set of tangent vectors +Eigen::VectorXd random_constraints(const + Eigen::VectorXd& b1, const + Eigen::VectorXd& b2, int n) +{ + Eigen::VectorXd r(n*3); + for (unsigned i=0; i'8') + return false; + + viewer.data.lines.resize(0,9); + + int num = key - '0'; + + // Interpolate + cerr << "Interpolating " << num << "-PolyVector field" << endl; + + VectorXi b(3); + b << 1511, 603, 506; + + int numConstraintsToGenerate; + // if it's not a 2-PV or a 1-PV, include a line direction (2 opposite vectors) + // in the field + if (num>=5) + numConstraintsToGenerate = num-2; + else + if (num>=3) + numConstraintsToGenerate = num-1; + else + numConstraintsToGenerate = num; + + + MatrixXd bc(b.size(),numConstraintsToGenerate*3); + for (unsigned i=0; i=5) + rootsIndex[num-2] = -2; + if (num>=3) + rootsIndex[num-1] = -1; + + // Interpolated PolyVector field + Eigen::MatrixXd pvf; + igl::n_polyvector_general(V, F, b, bc, rootsIndex, pvf); + + // Highlight in red the constrained faces + MatrixXd C = MatrixXd::Constant(F.rows(),3,1); + for (unsigned i=0; i