TriangleInt triangle_intersection(const vec3 tri_a[3], const vec3 tri_b[3]) { if (sat_bbox(tri_a, tri_b)) return TR_INT_NONE; sat_t norm1 = sat_normal(tri_a, tri_b); sat_t norm2 = sat_normal(tri_b, tri_a); if (norm2 == SAT_NONE) return TR_INT_NONE; if (norm1 == SAT_NONE) return TR_INT_NONE; if (norm2 == SAT_COPLANAR) return TR_INT_NONE; if (norm1 == SAT_COPLANAR) return TR_INT_NONE; if (norm1 == SAT_COPLANAR) { return triangle_intersection_coplanar(tri_a, tri_b); } size_t ia, ib; int sharing = test_sharing<3>(tri_a, tri_b, ia, ib); switch (std::abs(sharing)) { case 0: { // no shared vertices. for (size_t i = 0; i < 3; ++i) { for (size_t j = 0; j < 3; ++j) { if (sat_plane(tri_a, tri_b, i, j)) return TR_INT_NONE; if (sat_plane(tri_b, tri_a, i, j)) return TR_INT_NONE; } } return TR_INT_INT; } case 1: { // shared vertex (ia, ib) [but not shared edge, and not coplanar] size_t ia0 = ia, ia1 = (ia+1)%3, ia2 = (ia+2)%3; size_t ib0 = ib, ib1 = (ib+1)%3, ib2 = (ib+2)%3; if (sat_plane(tri_a, tri_b, ia2, ib1, ib2)) return TR_INT_VERT; if (sat_plane(tri_a, tri_b, ia2, ib2, ib1)) return TR_INT_VERT; if (sat_plane(tri_a, tri_b, ia0, ib1, ib2)) return TR_INT_VERT; if (sat_plane(tri_a, tri_b, ia0, ib2, ib1)) return TR_INT_VERT; if (sat_plane(tri_b, tri_b, ib2, ia1, ia2)) return TR_INT_VERT; if (sat_plane(tri_b, tri_b, ib2, ia2, ia1)) return TR_INT_VERT; if (sat_plane(tri_b, tri_b, ib0, ia1, ia2)) return TR_INT_VERT; if (sat_plane(tri_b, tri_b, ib0, ia2, ia1)) return TR_INT_VERT; return TR_INT_INT; } case 2: { // shared edge (ia, ib) [not coplanar] return TR_INT_EDGE; } case 3: { return TR_INT_TRI; } default: { CARVE_FAIL("should not happen."); } } }
TriangleInt triangle_intersection(const vec2 tri_a[3], const vec2 tri_b[3]) { // triangles must be anticlockwise, or colinear. CARVE_ASSERT(carve::geom2d::orient2d(tri_a[0], tri_a[1], tri_a[2]) >= 0.0); CARVE_ASSERT(carve::geom2d::orient2d(tri_b[0], tri_b[1], tri_b[2]) >= 0.0); size_t ia, ib; int sharing = test_sharing<2>(tri_a, tri_b, ia, ib); switch (std::abs(sharing)) { case 0: { // no shared vertices. if (sat_edge(tri_a, tri_b, 0) < 0) return TR_INT_NONE; if (sat_edge(tri_a, tri_b, 1) < 0) return TR_INT_NONE; if (sat_edge(tri_a, tri_b, 2) < 0) return TR_INT_NONE; if (sat_edge(tri_b, tri_a, 0) < 0) return TR_INT_NONE; if (sat_edge(tri_b, tri_a, 1) < 0) return TR_INT_NONE; if (sat_edge(tri_b, tri_a, 2) < 0) return TR_INT_NONE; return TR_INT_INT; } case 1: { // shared vertex (ia, ib) [but not shared edge] if (sat_edge(tri_a, tri_b, (ia+2)%3, ib) < 0) return TR_INT_VERT; if (sat_edge(tri_a, tri_b, ia, ib) < 0) return TR_INT_VERT; if (sat_edge(tri_b, tri_a, (ib+2)%3, ia) < 0) return TR_INT_VERT; if (sat_edge(tri_b, tri_a, ib, ia) < 0) return TR_INT_VERT; return TR_INT_INT; } case 2: { // shared edge (ia, ib) -> (ia + 1, ib + (sharing > 0) ? +1 : -1) size_t pa = (ia + 2) % 3; size_t pb = (ib + (sharing == +1 ? 2 : 1)) % 3; double sa = orient2d_exact(tri_a[ia], tri_a[(ia+1)%3], tri_a[pa]); double sb = orient2d_exact(tri_a[ia], tri_a[(ia+1)%3], tri_b[pb]); if (sa * sb < 0.0) { return TR_INT_EDGE; } return TR_INT_INT; } case 3: { return TR_INT_TRI; } default: { CARVE_FAIL("should not happen."); } } }
TriangleIntType triangle_intersection_exact(const vec2 tri_a[3], const vec2 tri_b[3]) { // triangles must be anticlockwise, or colinear. CARVE_ASSERT(orient2d_exact(tri_a[0], tri_a[1], tri_a[2]) >= 0.0); CARVE_ASSERT(orient2d_exact(tri_b[0], tri_b[1], tri_b[2]) >= 0.0); int curr = +1; for (size_t i = 0; curr != -1 && i < 3; ++i) { curr = std::min(curr, sat_edge(tri_a, tri_b, i)); } for (size_t i = 0; curr != -1 && i < 3; ++i) { curr = std::min(curr, sat_edge(tri_b, tri_a, i)); } switch (curr) { case -1: return TR_TYPE_NONE; case 0: return TR_TYPE_TOUCH; case +1: return TR_TYPE_INT; default: CARVE_FAIL("should not happen."); } }
std::vector<std::pair<size_t, size_t> > carve::triangulate::incorporateHolesIntoPolygon(const std::vector<std::vector<carve::geom2d::P2> > &poly) { #if 1 std::vector<std::pair<size_t, size_t> > result; std::vector<size_t> hole_indices; hole_indices.reserve(poly.size() - 1); for (size_t i = 1; i < poly.size(); ++i) { hole_indices.push_back(i); } incorporateHolesIntoPolygon(poly, result, 0, hole_indices); return result; #else typedef std::vector<carve::geom2d::P2> loop_t; size_t N = poly[0].size(); // // work out how much space to reserve for the patched in holes. for (size_t i = 0; i < poly.size(); i++) { N += 2 + poly[i].size(); } // this is the vector that we will build the result in. std::vector<std::pair<size_t, size_t> > current_f_loop; current_f_loop.reserve(N); // this is a heap of current_f_loop indices that defines the vertex test order. std::vector<size_t> f_loop_heap; f_loop_heap.reserve(N); // add the poly loop to current_f_loop. for (size_t i = 0; i < poly[0].size(); ++i) { current_f_loop.push_back(std::make_pair((size_t)0, i)); } if (poly.size() == 1) { return current_f_loop; } std::vector<std::pair<size_t, size_t> > h_loop_min_vertex; h_loop_min_vertex.reserve(poly.size() - 1); // find the major axis for the holes - this is the axis that we // will sort on for finding vertices on the polygon to join // holes up to. // // it might also be nice to also look for whether it is better // to sort ascending or descending. // // another trick that could be used is to modify the projection // by 90 degree rotations or flipping about an axis. just as // long as we keep the carve::geom3d::Vector pointers for the // real data in sync, everything should be ok. then we wouldn't // need to accomodate axes or sort order in the main loop. // find the bounding box of all the holes. double min_x, min_y, max_x, max_y; min_x = max_x = poly[1][0].x; min_y = max_y = poly[1][0].y; for (size_t i = 1; i < poly.size(); ++i) { const loop_t &hole = poly[i]; for (size_t j = 0; j < hole.size(); ++j) { min_x = std::min(min_x, hole[j].x); min_y = std::min(min_y, hole[j].y); max_x = std::max(max_x, hole[j].x); max_y = std::max(max_y, hole[j].y); } } // choose the axis for which the bbox is largest. int axis = (max_x - min_x) > (max_y - min_y) ? 0 : 1; // for each hole, find the minimum vertex in the chosen axis. for (size_t i = 1; i < poly.size(); ++i) { const loop_t &hole = poly[i]; size_t best, curr; best = 0; for (curr = 1; curr != hole.size(); ++curr) { if (detail::axisOrdering(hole[curr], hole[best], axis)) { best = curr; } } h_loop_min_vertex.push_back(std::make_pair(i, best)); } // sort the holes by the minimum vertex. std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), order_h_loops_2d(poly, axis)); // now, for each hole, find a vertex in the current polygon loop that it can be joined to. for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) { // the index of the vertex in the hole to connect. size_t hole_i = h_loop_min_vertex[i].first; size_t hole_i_connect = h_loop_min_vertex[i].second; carve::geom2d::P2 hole_min = poly[hole_i][hole_i_connect]; f_loop_heap.clear(); // we order polygon loop vertices that may be able to be connected // to the hole vertex by their distance to the hole vertex heap_ordering_2d _heap_ordering(poly, current_f_loop, hole_min, axis); const size_t SZ = current_f_loop.size(); for (size_t j = 0; j < SZ; ++j) { // it is guaranteed that there exists a polygon vertex with // coord < the min hole coord chosen, which can be joined to // the min hole coord without crossing the polygon // boundary. also, because we merge holes in ascending // order, it is also true that this join can never cross // another hole (and that doesn't need to be tested for). if (pvert(poly, current_f_loop[j]).v[axis] <= hole_min.v[axis]) { f_loop_heap.push_back(j); std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); } } // we are going to test each potential (according to the // previous test) polygon vertex as a candidate join. we order // by closeness to the hole vertex, so that the join we make // is as small as possible. to test, we need to check the // joining line segment does not cross any other line segment // in the current polygon loop (excluding those that have the // vertex that we are attempting to join with as an endpoint). size_t attachment_point = current_f_loop.size(); while (f_loop_heap.size()) { std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering); size_t curr = f_loop_heap.back(); f_loop_heap.pop_back(); // test the candidate join from current_f_loop[curr] to hole_min if (!testCandidateAttachment(poly, current_f_loop, curr, hole_min)) { continue; } attachment_point = curr; break; } if (attachment_point == current_f_loop.size()) { CARVE_FAIL("didn't manage to link up hole!"); } patchHoleIntoPolygon_2d(current_f_loop, attachment_point, hole_i, hole_i_connect, poly[hole_i].size()); } return current_f_loop; #endif }