#include "triangle_triangle_intersect.h" #include "triangle_triangle_intersect_shared_edge.h" #include "triangle_triangle_intersect_shared_vertex.h" #include "tri_tri_intersect.h" #include #include #include #include #include //#define IGL_TRIANGLE_TRIANGLE_INTERSECT_DEBUG #ifdef IGL_TRIANGLE_TRIANGLE_INTERSECT_DEBUG // CGAL::Epeck #include #warning "🐌🐌🐌🐌🐌🐌🐌🐌 Slow debug mode for igl::triangle_triangle_intersect" #endif template < typename DerivedV, typename DerivedF, typename DerivedE, typename DerivedEMAP, typename DerivedEF, typename Derivedp> IGL_INLINE bool igl::triangle_triangle_intersect( const Eigen::MatrixBase & V, const Eigen::MatrixBase & F, const Eigen::MatrixBase & E, const Eigen::MatrixBase & EMAP, const Eigen::MatrixBase & EF, const int f, const int c, const Eigen::MatrixBase & p, const int g) { static_assert( std::is_same::value, "V and p should have same Scalar type"); assert(V.cols() == 3); assert(p.cols() == 3); #ifdef IGL_TRIANGLE_TRIANGLE_INTERSECT_DEBUG using Kernel = CGAL::Epeck; typedef CGAL::Point_3 Point_3; typedef CGAL::Segment_3 Segment_3; typedef CGAL::Triangle_3 Triangle_3; bool cgal_found_intersection = false; Point_3 Vg[3]; Point_3 Vf[3]; for(int i = 0;i<3;i++) { Vg[i] = Point_3(V(F(g,i),0),V(F(g,i),1),V(F(g,i),2)); if(i == c) { Vf[i] = Point_3(p(0),p(1),p(2)); }else { Vf[i] = Point_3(V(F(f,i),0),V(F(f,i),1),V(F(f,i),2)); } } Triangle_3 Tg(Vg[0],Vg[1],Vg[2]); Triangle_3 Tf(Vf[0],Vf[1],Vf[2]); #endif // I'm leaving this debug printing stuff in for a bit until I trust this // better. constexpr bool stinker = false; //const bool stinker = (f==1492 && g==1554); if(stinker) { printf("👀\n"); } bool found_intersection = false; // So edge opposite F(f,c) is the outer edge. const bool share_edge_opposite_c = [&]() { const int o = EMAP(f + c*F.rows()); return (EF(o,0) == f && EF(o,1) == g) || (EF(o,1) == f && EF(o,0) == g); }(); const int o = EMAP(f + c*F.rows()); // Do they share the edge opposite c? if(share_edge_opposite_c) { if(stinker) { printf("⚠️ shares an edge\n"); } found_intersection = triangle_triangle_intersect_shared_edge(V,F,f,c,p,g,1e-8); }else { if(stinker) { printf("does not share an edge\n"); } // Do they share a vertex? int sf,sg; bool found_shared_vertex = false; for(sf = 0;sf<3;sf++) { if(sf == c){ continue;} for(sg = 0;sg<3;sg++) { if(F(f,sf) == F(g,sg)) { found_shared_vertex = true; break; } } if(found_shared_vertex) { break;} } if(found_shared_vertex) { if(stinker) { printf("⚠️ shares a vertex\n"); } found_intersection = triangle_triangle_intersect_shared_vertex(V,F,f,sf,c,p,g,sg,1e-14); }else { bool coplanar; Eigen::RowVector3d i1,i2; found_intersection = igl::tri_tri_overlap_test_3d( V.row(F(g,0)).template cast(), V.row(F(g,1)).template cast(), V.row(F(g,2)).template cast(), p.template cast(), V.row(F(f,(c+1)%3)).template cast(), V.row(F(f,(c+2)%3)).template cast()) && igl::tri_tri_intersection_test_3d( V.row(F(g,0)).template cast(), V.row(F(g,1)).template cast(), V.row(F(g,2)).template cast(), p.template cast(), V.row(F(f,(c+1)%3)).template cast(), V.row(F(f,(c+2)%3)).template cast(), coplanar, i1,i2); if(stinker) { printf("tri_tri_intersection_test_3d says %s\n",found_intersection?"☠️":"✅"); } #ifdef IGL_TRIANGLE_TRIANGLE_INTERSECT_DEBUG if(CGAL::do_intersect(Tg,Tf)) { cgal_found_intersection = true; printf(" ✅ sure it's anything\n"); } assert(found_intersection == cgal_found_intersection); #endif } } if(stinker) { printf("%s\n",found_intersection?"☠️":"✅"); } return found_intersection; } template < typename DerivedV, typename DerivedF, typename DerivedIF, typename DerivedEV, typename DerivedEE> void igl::triangle_triangle_intersect( const Eigen::MatrixBase & V1, const Eigen::MatrixBase & F1, const Eigen::MatrixBase & V2, const Eigen::MatrixBase & F2, const Eigen::MatrixBase & IF, Eigen::PlainObjectBase & EV, Eigen::PlainObjectBase & EE) { using Scalar = typename DerivedEV::Scalar; using RowVector3S = Eigen::Matrix; // We were promised that IF is a list of non-coplanar non-degenerately // intersecting triangle pairs. This implies that the set of intersection is a // line-segment whose endpoints are defined by edge-triangle intersections. // // Each edge-triangle intersection will be stored in a map (e,f) // std::unordered_map uf_to_ev; Eigen::VectorXi EMAP1; Eigen::MatrixXi uE1; { Eigen::VectorXi uEE,uEC; Eigen::MatrixXi E; igl::unique_edge_map(F1,E,uE1,EMAP1,uEE,uEC); } Eigen::VectorXi EMAP2_cpy; Eigen::MatrixXi uE2_cpy; const bool self = &F1 == &F2; if(!self) { Eigen::VectorXi uEE,uEC; Eigen::MatrixXi E; igl::unique_edge_map(F2,E,uE2_cpy,EMAP2_cpy,uEE,uEC); } const Eigen::VectorXi & EMAP2 = self?EMAP1:EMAP2_cpy; const Eigen::MatrixXi & uE2 = self?uE1:uE2_cpy; int num_ev = 0; EE.resize(IF.rows(),2); for(int i = 0; i B; Eigen::Matrix X; Eigen::Matrix uf; for(int p = 0;p<2;p++) { const auto consider_edges = [&B,&X,&uf] (const int p, const int f1, const int f2, const Eigen::MatrixBase & V1, const Eigen::MatrixBase & F1, const Eigen::VectorXi & EMAP1, const Eigen::MatrixXi & uE1, const Eigen::MatrixBase & V2, const Eigen::MatrixBase & F2) { for(int e1 = 0;e1<3;e1++) { // intersect edge (ij) opposite vertex F(f1,e1) with triangle ABC of F(f2,:) const int u1 = EMAP1(f1 +(EMAP1.size()/3)*e1); uf.row(p*3+e1) << u1,f2; const int i = uE1(u1,0); const int j = uE1(u1,1); // Just copy. const RowVector3S Vi = V1.row(i); const RowVector3S Vj = V1.row(j); const RowVector3S VA = V2.row(F2(f2,0)); const RowVector3S VB = V2.row(F2(f2,1)); const RowVector3S VC = V2.row(F2(f2,2)); // Find intersection of line (Vi,Vj) with plane of triangle (A,B,C) const RowVector3S n = (VB-VA).template head<3>().cross((VC-VA).template head<3>()); const Scalar d = n.dot(VA); const Scalar t = (d - n.dot(Vi))/(n.dot(Vj-Vi)); const RowVector3S x = Vi + t*(Vj-Vi); // Get barycenteric coordinates (possibly negative of X) RowVector3S b1; igl::barycentric_coordinates(x,VA,VB,VC,b1); B.row(p*3+e1).head<3>() = b1; RowVector3S b2; igl::barycentric_coordinates(x, V1.row(F1(f1,0)).template head<3>().eval(), V1.row(F1(f1,1)).template head<3>().eval(), V1.row(F1(f1,2)).template head<3>().eval(), b2); B.row(p*3+e1).tail<3>() = b2; X.row(p*3+e1) = x; } }; const int f1 = IF(i,p); const int f2 = IF(i,(p+1)%2); consider_edges(p,f1,f2, p==0? V1:V2, p==0? F1:F2, p==0?EMAP1:EMAP2, p==0? uE1:uE2, p==0? V2:V1, p==0? F2:F1); } // Find the two rows in B with the largest-smallest element int j1,j2; { double b_min1 = -std::numeric_limits::infinity(); double b_min2 = -std::numeric_limits::infinity(); for(int j = 0;j<6;j++) { // It's not clear that using barycentric coordinates is better than // point_simplex distance (though that requires inequalities). const double bminj = B.row(j).minCoeff(); if(bminj > b_min1) { b_min2 = b_min1; j2 = j1; b_min1 = bminj; j1 = j; }else if(bminj > b_min2) { b_min2 = bminj; j2 = j; } } } const auto append_or_find = [&]( int p, int u, int f, const RowVector3S & x, std::unordered_map & uf_to_ev)->int { const std::int64_t key = (std::int64_t)u + ((std::int64_t)f << 32) + ((std::int64_t)p << 63); if(uf_to_ev.find(key) == uf_to_ev.end()) { if(num_ev == EV.rows()) { EV.conservativeResize(2*EV.rows()+1,3); } EV.row(num_ev) = x; uf_to_ev[key] = num_ev; num_ev++; } return uf_to_ev[key]; }; EE.row(i) << append_or_find(j1>=3,uf(j1,0),uf(j1,1),X.row(j1),uf_to_ev), append_or_find(j2>=3,uf(j2,0),uf(j2,1),X.row(j2),uf_to_ev); } EV.conservativeResize(num_ev,3); } template < typename DerivedV, typename DerivedF, typename DerivedIF, typename DerivedEV, typename DerivedEE> void igl::triangle_triangle_intersect( const Eigen::MatrixBase & V, const Eigen::MatrixBase & F, const Eigen::MatrixBase & IF, Eigen::PlainObjectBase & EV, Eigen::PlainObjectBase & EE) { // overload will take care of detecting reference equality return triangle_triangle_intersect(V,F,V,F,IF,EV,EE); } #ifdef IGL_STATIC_LIBRARY // Explicit template instantiation // generated by autoexplicit.sh template void igl::triangle_triangle_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); // generated by autoexplicit.sh template void igl::triangle_triangle_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); template bool igl::triangle_triangle_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Block, 1, -1, false> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, int, Eigen::MatrixBase, 1, -1, false> > const&, int); template bool igl::triangle_triangle_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, int, int, Eigen::MatrixBase > const&, int); #endif