// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2016 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 "decimate.h" #include "collapse_edge.h" #include "edge_flaps.h" #include "decimate_trivial_callbacks.h" #include "AABB.h" #include "intersection_blocking_collapse_edge_callbacks.h" #include "is_edge_manifold.h" #include "remove_unreferenced.h" #include "find.h" #include "connect_boundary_to_infinity.h" #include "parallel_for.h" #include "max_faces_stopping_condition.h" #include "shortest_edge_and_midpoint.h" IGL_INLINE bool igl::decimate( const Eigen::MatrixXd & V, const Eigen::MatrixXi & F, const int max_m, const bool block_intersections, Eigen::MatrixXd & U, Eigen::MatrixXi & G, Eigen::VectorXi & J, Eigen::VectorXi & I) { igl::AABB * tree = nullptr; if(block_intersections) { tree = new igl::AABB(); tree->init(V,F); } // Original number of faces const int orig_m = F.rows(); // Tracking number of faces int m = F.rows(); typedef Eigen::MatrixXd DerivedV; typedef Eigen::MatrixXi DerivedF; DerivedV VO; DerivedF FO; igl::connect_boundary_to_infinity(V,F,VO,FO); Eigen::VectorXi EMAP; Eigen::MatrixXi E,EF,EI; edge_flaps(FO,E,EMAP,EF,EI); // decimate will not work correctly on non-edge-manifold meshes. By extension // this includes meshes with non-manifold vertices on the boundary since these // will create a non-manifold edge when connected to infinity. { Eigen::Array BF; Eigen::Array BE; if(!is_edge_manifold(FO,E.rows(),EMAP,BF,BE)) { return false; } } decimate_pre_collapse_callback pre_collapse; decimate_post_collapse_callback post_collapse; decimate_trivial_callbacks(pre_collapse,post_collapse); if(block_intersections) { igl::intersection_blocking_collapse_edge_callbacks( pre_collapse, post_collapse, // These will get copied as needed tree, pre_collapse, post_collapse); } bool ret = decimate( VO, FO, shortest_edge_and_midpoint, max_faces_stopping_condition(m,orig_m,max_m), pre_collapse, post_collapse, E, EMAP, EF, EI, U, G, J, I); const Eigen::Array keep = (J.array()root()); delete tree; return ret; } IGL_INLINE bool igl::decimate( const Eigen::MatrixXd & OV, const Eigen::MatrixXi & OF, const decimate_cost_and_placement_callback & cost_and_placement, const decimate_stopping_condition_callback & stopping_condition, Eigen::MatrixXd & U, Eigen::MatrixXi & G, Eigen::VectorXi & J, Eigen::VectorXi & I ) { decimate_pre_collapse_callback always_try; decimate_post_collapse_callback never_care; decimate_trivial_callbacks(always_try,never_care); return igl::decimate( OV,OF,cost_and_placement,stopping_condition,always_try,never_care,U,G,J,I); } IGL_INLINE bool igl::decimate( const Eigen::MatrixXd & OV, const Eigen::MatrixXi & OF, const decimate_cost_and_placement_callback & cost_and_placement, const decimate_stopping_condition_callback & stopping_condition, const decimate_pre_collapse_callback & pre_collapse, const decimate_post_collapse_callback & post_collapse, Eigen::MatrixXd & U, Eigen::MatrixXi & G, Eigen::VectorXi & J, Eigen::VectorXi & I ) { Eigen::VectorXi EMAP; Eigen::MatrixXi E,EF,EI; edge_flaps(OF,E,EMAP,EF,EI); return igl::decimate( OV,OF, cost_and_placement,stopping_condition,pre_collapse,post_collapse, E,EMAP,EF,EI, U,G,J,I); } IGL_INLINE bool igl::decimate( const Eigen::MatrixXd & OV, const Eigen::MatrixXi & OF, const decimate_cost_and_placement_callback & cost_and_placement, const decimate_stopping_condition_callback & stopping_condition, const decimate_pre_collapse_callback & pre_collapse, const decimate_post_collapse_callback & post_collapse, const Eigen::MatrixXi & /*OE*/, const Eigen::VectorXi & /*OEMAP*/, const Eigen::MatrixXi & /*OEF*/, const Eigen::MatrixXi & /*OEI*/, Eigen::MatrixXd & U, Eigen::MatrixXi & G, Eigen::VectorXi & J, Eigen::VectorXi & I ) { // Decimate 1 using namespace Eigen; using namespace std; // Working copies Eigen::MatrixXd V = OV; Eigen::MatrixXi F = OF; // Why recompute this rather than copy input? VectorXi EMAP; MatrixXi E,EF,EI; edge_flaps(F,E,EMAP,EF,EI); { Eigen::Array BF; Eigen::Array BE; if(!is_edge_manifold(F,E.rows(),EMAP,BF,BE)) { return false; } } igl::min_heap > Q; // Could reserve with https://stackoverflow.com/a/29236236/148668 Eigen::VectorXi EQ = Eigen::VectorXi::Zero(E.rows()); // If an edge were collapsed, we'd collapse it to these points: MatrixXd C(E.rows(),V.cols()); // Pushing into a vector then using constructor was slower. Maybe using // std::move + make_heap would squeeze out something? // Separating the cost/placement evaluation from the Q filling is a // performance hit for serial but faster if we can parallelize the // cost/placement. { Eigen::VectorXd costs(E.rows()); igl::parallel_for(E.rows(),[&](const int e) { double cost = e; RowVectorXd p(1,3); cost_and_placement(e,V,F,E,EMAP,EF,EI,cost,p); C.row(e) = p; costs(e) = cost; }, 10000 ); for(int e = 0;e