// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2019 Hanxiao Shen // // 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 "ear_clipping.h" #include "predicates.h" #include "segment_segment_intersect.h" #include "../turning_number.h" template < typename DerivedP, typename DerivedRT, typename DerivedF, typename DerivedI> IGL_INLINE void igl::predicates::ear_clipping( const Eigen::MatrixBase& P, const Eigen::MatrixBase& RT, Eigen::PlainObjectBase& eF, Eigen::PlainObjectBase& I) { typedef typename DerivedF::Scalar Index; typedef typename DerivedP::Scalar Scalar; static_assert(std::is_same::value, "index type should be consistent"); // check whether vertex i is an ear auto is_ear = []( const Eigen::MatrixBase& P, const Eigen::MatrixBase& RT, const Eigen::Matrix& L, const Eigen::Matrix& R, const Index i ){ // check if any edge in the leftover polygon intersects triangle (a,b,i) Index a = L(i), b = R(i); if(RT(i) != 0 || RT(a) != 0 || RT(b) != 0) return false; Eigen::Matrix pa = P.row(a); Eigen::Matrix pb = P.row(b); Eigen::Matrix pi = P.row(i); auto r = igl::predicates::orient2d(pa, pi, pb); if(r == igl::predicates::Orientation::NEGATIVE || r == igl::predicates::Orientation::COLLINEAR) return false; // check edge (b, R(b)) Index k = b; Index l = R(b); Eigen::Matrix pk = P.row(k); Eigen::Matrix pl = P.row(l); auto r1 = igl::predicates::orient2d(pb, pl, pa); auto r2 = igl::predicates::orient2d(pi, pl, pb); if (l == a) return true; // single triangle if (r1 != igl::predicates::Orientation::POSITIVE && r2 != igl::predicates::Orientation::POSITIVE) { return false; } // iterate through all edges do not have common endpoints with a, b k = l; l = R(k); while (l != a) { pk = P.row(k); pl = P.row(l); if (igl::predicates::segment_segment_intersect(pa, pb, pk, pl) || igl::predicates::segment_segment_intersect(pa, pi, pk, pl) || igl::predicates::segment_segment_intersect(pi, pb, pk, pl) ){ return false; } k = l; l = R(k); } // check edge (L(a), a) pk = P.row(k); pl = P.row(l); r1 = igl::predicates::orient2d(pb, pk, pl); r2 = igl::predicates::orient2d(pi, pa, pk); if (r1 != igl::predicates::Orientation::POSITIVE && r2 != igl::predicates::Orientation::POSITIVE ){ return false; } return true; }; Eigen::Matrix L(P.rows()); Eigen::Matrix R(P.rows()); for(int i = 0;i < P.rows(); i++){ L(i) = Index((i - 1 + P.rows()) % P.rows()); R(i) = Index((i + 1) % P.rows()); } Eigen::Matrix ears; // mark ears Eigen::Matrix X; // clipped vertices ears.setZero(P.rows()); X.setZero(P.rows()); // initialize ears for(int i = 0; i < P.rows(); i++){ ears(i) = is_ear(P, RT, L, R, i); } // clip ears until none left while(ears.maxCoeff()==1){ // find the first ear Index e = 0; while(e < ears.rows() && ears(e) != 1) e++; // find valid neighbors Index a = L(e), b = R(e); if(a == b) break; // clip ear and store face eF.conservativeResize(eF.rows()+1,3); eF.bottomRows(1)< IGL_INLINE bool igl::predicates::ear_clipping( const Eigen::MatrixBase& P, Eigen::PlainObjectBase& eF) { if(turning_number(P) < 0) { // reverse input const auto rP = P.colwise().reverse().eval(); const bool ret = igl::predicates::ear_clipping(rP, eF); // flip output eF = eF.rowwise().reverse(); return ret; } const Eigen::Matrix RT = Eigen::Matrix::Zero(P.rows()); Eigen::Matrix I; igl::predicates::ear_clipping(P, RT, eF, I); return I.size() == 0; } #ifdef IGL_STATIC_LIBRARY template bool igl::predicates::ear_clipping, Eigen::Matrix>(Eigen::MatrixBase> const&, Eigen::PlainObjectBase>&); template void igl::predicates::ear_clipping, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix>(Eigen::MatrixBase> const&, Eigen::MatrixBase> const&, Eigen::PlainObjectBase>&, Eigen::PlainObjectBase>&); #endif