// 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 "snap_rounding.h" #include "resolve_intersections.h" #include "subdivide_segments.h" #include "../../remove_unreferenced.h" #include "../../unique.h" #include #include #include #include #include template < typename DerivedV, typename DerivedE, typename DerivedVI, typename DerivedEI, typename DerivedJ> IGL_INLINE void igl::copyleft::cgal::snap_rounding( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & E, Eigen::PlainObjectBase & VI, Eigen::PlainObjectBase & EI, Eigen::PlainObjectBase & J) { using namespace Eigen; using namespace igl; using namespace igl::copyleft::cgal; using namespace std; // Exact scalar type typedef CGAL::Epeck Kernel; typedef Kernel::FT EScalar; typedef CGAL::Segment_2 Segment_2; typedef CGAL::Point_2 Point_2; typedef CGAL::Vector_2 Vector_2; typedef Matrix MatrixXE; // Convert vertex positions to exact kernel MatrixXE VE; { VectorXi IM; resolve_intersections(V,E,VE,EI,J,IM); for_each( EI.data(), EI.data()+EI.size(), [&IM](typename DerivedEI::Scalar& i){i=IM(i);}); VectorXi _; remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_); } // find all hot pixels //// southwest and north east corners //const RowVector2i SW( // round(VE.col(0).minCoeff()), // round(VE.col(1).minCoeff())); //const RowVector2i NE( // round(VE.col(0).maxCoeff()), // round(VE.col(1).maxCoeff())); // https://github.com/CGAL/cgal/issues/548 // Round an exact scalar to the nearest integer. A priori can't just round // double. Suppose e=0.5+ε but double(e)<0.5 // // Inputs: // e exact number // Outputs: // i closest integer const auto & round = [](const EScalar & e)->int { const double d = CGAL::to_double(e); // get an integer that's near the closest int int i = std::round(d); EScalar di_sqr = CGAL::square((e-EScalar(i))); const auto & search = [&i,&di_sqr,&e](const int dir) { while(true) { const int j = i+dir; const EScalar dj_sqr = CGAL::square((e-EScalar(j))); if(dj_sqr < di_sqr) { i = j; di_sqr = dj_sqr; }else { break; } } }; // Try to increase/decrease int search(1); search(-1); return i; }; vector hot; for(int i = 0;i _1,_2; igl::unique(vector(hot),hot,_1,_2); } // find all segments intersecting hot pixels // split edge at closest point to hot pixel center vector> steiner(EI.rows()); // initialize each segment with endpoints for(int i = 0;i hits; for(int j = 0;j<4;j++) { const Segment_2 & sj = wall[j]; if(CGAL::do_intersect(si,sj)) { CGAL::Object result = CGAL::intersection(si,sj); if(const Point_2 * p = CGAL::object_cast(&result)) { hits.push_back(*p); }else if(const Segment_2 * s = CGAL::object_cast(&result)) { // add both endpoints hits.push_back(s->vertex(0)); hits.push_back(s->vertex(1)); } } } if(hits.size() == 0) { continue; } // centroid of hits Vector_2 cen(0,0); for(const Point_2 & hit : hits) { cen = Vector_2(cen.x()+hit.x(), cen.y()+hit.y()); } cen = Vector_2(cen.x()/EScalar(hits.size()),cen.y()/EScalar(hits.size())); const Point_2 rcen(round(cen.x()),round(cen.y())); // after all of that, don't add as a steiner unless it's going to round // to h if(rcen == h) { steiner[i].emplace_back(cen.x(),cen.y()); } } } { DerivedJ prevJ = J; VectorXi IM; subdivide_segments(MatrixXE(VE),MatrixXi(EI),steiner,VE,EI,J,IM); for_each(J.data(),J.data()+J.size(),[&prevJ](typename DerivedJ::Scalar & j){j=prevJ(j);}); for_each( EI.data(), EI.data()+EI.size(), [&IM](typename DerivedEI::Scalar& i){i=IM(i);}); VectorXi _; remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_); } VI.resizeLike(VE); for(int i = 0;i