|
@@ -0,0 +1,164 @@
|
|
|
+// This file is part of libigl, a simple c++ geometry processing library.
|
|
|
+//
|
|
|
+// Copyright (C) 2015 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 "decimate.h"
|
|
|
+#include "collapse_edge.h"
|
|
|
+#include "edge_flaps.h"
|
|
|
+#include "remove_unreferenced.h"
|
|
|
+#include <iostream>
|
|
|
+
|
|
|
+IGL_INLINE bool igl::decimate(
|
|
|
+ const Eigen::MatrixXd & V,
|
|
|
+ const Eigen::MatrixXi & F,
|
|
|
+ const size_t max_m,
|
|
|
+ Eigen::MatrixXd & U,
|
|
|
+ Eigen::MatrixXi & G)
|
|
|
+{
|
|
|
+ const auto & max_m_faces =
|
|
|
+ [&max_m,&F](
|
|
|
+ const Eigen::MatrixXd &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::VectorXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const std::set<std::pair<double,int> > &,
|
|
|
+ const std::vector<std::set<std::pair<double,int> >::iterator > &,
|
|
|
+ const Eigen::MatrixXd &,
|
|
|
+ const int,
|
|
|
+ const int,
|
|
|
+ const int,
|
|
|
+ const int,
|
|
|
+ const int)->bool
|
|
|
+ {
|
|
|
+ using namespace std;
|
|
|
+ static int m = F.rows();
|
|
|
+ m-=2;
|
|
|
+ return m<=(int)max_m;
|
|
|
+ };
|
|
|
+ const auto & shortest_edge_and_midpoint = [](
|
|
|
+ const int e,
|
|
|
+ const Eigen::MatrixXd & V,
|
|
|
+ const Eigen::MatrixXi & /*F*/,
|
|
|
+ const Eigen::MatrixXi & E,
|
|
|
+ const Eigen::VectorXi & /*EMAP*/,
|
|
|
+ const Eigen::MatrixXi & /*EF*/,
|
|
|
+ const Eigen::MatrixXi & /*EI*/,
|
|
|
+ double & cost,
|
|
|
+ Eigen::RowVectorXd & p)
|
|
|
+ {
|
|
|
+ cost = (V.row(E(e,0))-V.row(E(e,1))).norm();
|
|
|
+ p = 0.5*(V.row(E(e,0))+V.row(E(e,1)));
|
|
|
+ };
|
|
|
+ return decimate(V,F,shortest_edge_and_midpoint,max_m_faces,U,G);
|
|
|
+}
|
|
|
+
|
|
|
+IGL_INLINE bool igl::decimate(
|
|
|
+ const Eigen::MatrixXd & OV,
|
|
|
+ const Eigen::MatrixXi & OF,
|
|
|
+ const std::function<void(
|
|
|
+ const int,
|
|
|
+ const Eigen::MatrixXd &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::VectorXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ double &,
|
|
|
+ Eigen::RowVectorXd &)> & cost_and_placement,
|
|
|
+ const std::function<bool(
|
|
|
+ const Eigen::MatrixXd &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::VectorXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const Eigen::MatrixXi &,
|
|
|
+ const std::set<std::pair<double,int> > &,
|
|
|
+ const std::vector<std::set<std::pair<double,int> >::iterator > &,
|
|
|
+ const Eigen::MatrixXd &,
|
|
|
+ const int,
|
|
|
+ const int,
|
|
|
+ const int,
|
|
|
+ const int,
|
|
|
+ const int)> & stopping_condition,
|
|
|
+ Eigen::MatrixXd & U,
|
|
|
+ Eigen::MatrixXi & G)
|
|
|
+{
|
|
|
+ using namespace Eigen;
|
|
|
+ using namespace std;
|
|
|
+ // Working copies
|
|
|
+ Eigen::MatrixXd V = OV;
|
|
|
+ Eigen::MatrixXi F = OF;
|
|
|
+ VectorXi EMAP;
|
|
|
+ MatrixXi E,EF,EI;
|
|
|
+ typedef std::set<std::pair<double,int> > PriorityQueue;
|
|
|
+ PriorityQueue Q;
|
|
|
+ std::vector<PriorityQueue::iterator > Qit;
|
|
|
+ edge_flaps(F,E,EMAP,EF,EI);
|
|
|
+ Qit.resize(E.rows());
|
|
|
+ // If an edge were collapsed, we'd collapse it to these points:
|
|
|
+ MatrixXd C(E.rows(),V.cols());
|
|
|
+ for(int e = 0;e<E.rows();e++)
|
|
|
+ {
|
|
|
+ double cost = e;
|
|
|
+ RowVectorXd p(1,3);
|
|
|
+ cost_and_placement(e,V,F,E,EMAP,EF,EI,cost,p);
|
|
|
+ C.row(e) = p;
|
|
|
+ Qit[e] = Q.insert(std::pair<double,int>(cost,e)).first;
|
|
|
+ }
|
|
|
+
|
|
|
+ int prev_e = -1;
|
|
|
+ bool clean_finish = false;
|
|
|
+ while(true)
|
|
|
+ {
|
|
|
+ if(Q.empty())
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(Q.begin()->first == std::numeric_limits<double>::infinity())
|
|
|
+ {
|
|
|
+ // min cost edge is infinite cost
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ int e,e1,e2,f1,f2;
|
|
|
+ if(collapse_edge(cost_and_placement,V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2))
|
|
|
+ {
|
|
|
+ if(stopping_condition(V,F,E,EMAP,EF,EI,Q,Qit,C,e,e1,e2,f1,f2))
|
|
|
+ {
|
|
|
+ clean_finish = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }else
|
|
|
+ {
|
|
|
+ if(prev_e == e)
|
|
|
+ {
|
|
|
+ assert(false && "Edge collapse no progress... bad stopping condition?");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // Edge was not collapsed... must have been invalid. collapse_edge should
|
|
|
+ // have updated its cost to inf... continue
|
|
|
+ }
|
|
|
+ prev_e = e;
|
|
|
+ }
|
|
|
+ // remove all IGL_COLLAPSE_EDGE_NULL faces
|
|
|
+ MatrixXi F2(F.rows(),3);
|
|
|
+ int m = 0;
|
|
|
+ for(int f = 0;f<F.rows();f++)
|
|
|
+ {
|
|
|
+ if(
|
|
|
+ F(f,0) != IGL_COLLAPSE_EDGE_NULL ||
|
|
|
+ F(f,1) != IGL_COLLAPSE_EDGE_NULL ||
|
|
|
+ F(f,2) != IGL_COLLAPSE_EDGE_NULL)
|
|
|
+ {
|
|
|
+ F2.row(m++) = F.row(f);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ F2.conservativeResize(m,F2.cols());
|
|
|
+ VectorXi _1;
|
|
|
+ remove_unreferenced(V,F2,U,G,_1);
|
|
|
+ return clean_finish;
|
|
|
+}
|