// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2015 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 "collapse_edge.h" #include "circulation.h" #include "edge_collapse_is_valid.h" #include IGL_INLINE bool igl::collapse_edge( const int e, const Eigen::RowVectorXd & p, Eigen::MatrixXd & V, Eigen::MatrixXi & F, Eigen::MatrixXi & E, Eigen::VectorXi & EMAP, Eigen::MatrixXi & EF, Eigen::MatrixXi & EI, int & a_e1, int & a_e2, int & a_f1, int & a_f2) { // Assign this to 0 rather than, say, -1 so that deleted elements will get // draw as degenerate elements at vertex 0 (which should always exist and // never get collapsed to anything else since it is the smallest index) using namespace Eigen; using namespace std; const int eflip = E(e,0)>E(e,1); // source and destination const int s = eflip?E(e,1):E(e,0); const int d = eflip?E(e,0):E(e,1); if(!edge_collapse_is_valid(e,F,E,EMAP,EF,EI)) { return false; } // Important to grab neighbors of d before monkeying with edges const std::vector nV2Fd = circulation(e,!eflip,EMAP,EF,EI); // The following implementation strongly relies on s & cost_and_placement, Eigen::MatrixXd & V, Eigen::MatrixXi & F, Eigen::MatrixXi & E, Eigen::VectorXi & EMAP, Eigen::MatrixXi & EF, Eigen::MatrixXi & EI, std::set > & Q, std::vector >::iterator > & Qit, Eigen::MatrixXd & C) { int e,e1,e2,f1,f2; const auto always_try = []( const Eigen::MatrixXd & ,/*V*/ const Eigen::MatrixXi & ,/*F*/ const Eigen::MatrixXi & ,/*E*/ const Eigen::VectorXi & ,/*EMAP*/ const Eigen::MatrixXi & ,/*EF*/ const Eigen::MatrixXi & ,/*EI*/ const std::set > & ,/*Q*/ const std::vector >::iterator > &,/*Qit*/ const Eigen::MatrixXd & ,/*C*/ const int /*e*/ ) -> bool { return true;}; const auto never_care = []( const Eigen::MatrixXd & , /*V*/ const Eigen::MatrixXi & , /*F*/ const Eigen::MatrixXi & , /*E*/ const Eigen::VectorXi & ,/*EMAP*/ const Eigen::MatrixXi & , /*EF*/ const Eigen::MatrixXi & , /*EI*/ const std::set > & , /*Q*/ const std::vector >::iterator > &, /*Qit*/ const Eigen::MatrixXd & , /*C*/ const int , /*e*/ const int , /*e1*/ const int , /*e2*/ const int , /*f1*/ const int , /*f2*/ const bool /*collapsed*/ )-> void { }; return collapse_edge( cost_and_placement,always_try,never_care, V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2); } IGL_INLINE bool igl::collapse_edge( const std::function & cost_and_placement, const std::function > & ,/*Q*/ const std::vector >::iterator > &,/*Qit*/ const Eigen::MatrixXd & ,/*C*/ const int /*e*/ )> & pre_collapse, const std::function > & , /*Q*/ const std::vector >::iterator > &, /*Qit*/ const Eigen::MatrixXd & , /*C*/ const int , /*e*/ const int , /*e1*/ const int , /*e2*/ const int , /*f1*/ const int , /*f2*/ const bool /*collapsed*/ )> & post_collapse, Eigen::MatrixXd & V, Eigen::MatrixXi & F, Eigen::MatrixXi & E, Eigen::VectorXi & EMAP, Eigen::MatrixXi & EF, Eigen::MatrixXi & EI, std::set > & Q, std::vector >::iterator > & Qit, Eigen::MatrixXd & C) { int e,e1,e2,f1,f2; return collapse_edge( cost_and_placement,pre_collapse,post_collapse, V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2); } IGL_INLINE bool igl::collapse_edge( const std::function & cost_and_placement, const std::function > & ,/*Q*/ const std::vector >::iterator > &,/*Qit*/ const Eigen::MatrixXd & ,/*C*/ const int /*e*/ )> & pre_collapse, const std::function > & , /*Q*/ const std::vector >::iterator > &, /*Qit*/ const Eigen::MatrixXd & , /*C*/ const int , /*e*/ const int , /*e1*/ const int , /*e2*/ const int , /*f1*/ const int , /*f2*/ const bool /*collapsed*/ )> & post_collapse, Eigen::MatrixXd & V, Eigen::MatrixXi & F, Eigen::MatrixXi & E, Eigen::VectorXi & EMAP, Eigen::MatrixXi & EF, Eigen::MatrixXi & EI, std::set > & Q, std::vector >::iterator > & Qit, Eigen::MatrixXd & C, int & e, int & e1, int & e2, int & f1, int & f2) { using namespace Eigen; if(Q.empty()) { // no edges to collapse return false; } std::pair p = *(Q.begin()); if(p.first == std::numeric_limits::infinity()) { // min cost edge is infinite cost return false; } Q.erase(Q.begin()); e = p.second; Qit[e] = Q.end(); std::vector N = circulation(e, true,EMAP,EF,EI); std::vector Nd = circulation(e,false,EMAP,EF,EI); N.insert(N.begin(),Nd.begin(),Nd.end()); bool collapsed = true; if(pre_collapse(V,F,E,EMAP,EF,EI,Q,Qit,C,e)) { collapsed = collapse_edge(e,C.row(e),V,F,E,EMAP,EF,EI,e1,e2,f1,f2); }else { // Aborted by pre collapse callback collapsed = false; } post_collapse(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2,collapsed); if(collapsed) { // Erase the two, other collapsed edges Q.erase(Qit[e1]); Qit[e1] = Q.end(); Q.erase(Qit[e2]); Qit[e2] = Q.end(); // update local neighbors // loop over original face neighbors for(auto n : N) { if(F(n,0) != IGL_COLLAPSE_EDGE_NULL || F(n,1) != IGL_COLLAPSE_EDGE_NULL || F(n,2) != IGL_COLLAPSE_EDGE_NULL) { for(int v = 0;v<3;v++) { // get edge id const int ei = EMAP(v*F.rows()+n); // erase old entry Q.erase(Qit[ei]); // compute cost and potential placement double cost; RowVectorXd place; cost_and_placement(ei,V,F,E,EMAP,EF,EI,cost,place); // Replace in queue Qit[ei] = Q.insert(std::pair(cost,ei)).first; C.row(ei) = place; } } } }else { // reinsert with infinite weight (the provided cost function must **not** // have given this un-collapsable edge inf cost already) p.first = std::numeric_limits::infinity(); Qit[e] = Q.insert(p).first; } return collapsed; }