#pragma once #include #include #include #include #include #include #include #include namespace mapbox { namespace geometry { namespace wagyu { template struct intersect_list_sorter { inline bool operator()(intersect_node const& node1, intersect_node const& node2) { if (!values_are_equal(node2.pt.y, node1.pt.y)) { return node2.pt.y < node1.pt.y; } else { return (node2.bound1->winding_count2 + node2.bound2->winding_count2) > (node1.bound1->winding_count2 + node1.bound2->winding_count2); } } }; template inline mapbox::geometry::point round_point(mapbox::geometry::point const& pt) { return mapbox::geometry::point(round_towards_max(pt.x), round_towards_max(pt.y)); } template inline void swap_rings(bound& b1, bound& b2) { ring_ptr ring = b1.ring; b1.ring = b2.ring; b2.ring = ring; } template inline void swap_sides(bound& b1, bound& b2) { edge_side side = b1.side; b1.side = b2.side; b2.side = side; } template bool get_edge_intersection(edge const& e1, edge const& e2, mapbox::geometry::point& pt) { T2 p0_x = static_cast(e1.bot.x); T2 p0_y = static_cast(e1.bot.y); T2 p1_x = static_cast(e1.top.x); T2 p1_y = static_cast(e1.top.y); T2 p2_x = static_cast(e2.bot.x); T2 p2_y = static_cast(e2.bot.y); T2 p3_x = static_cast(e2.top.x); T2 p3_y = static_cast(e2.top.y); T2 s1_x, s1_y, s2_x, s2_y; s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; s2_x = p3_x - p2_x; s2_y = p3_y - p2_y; T2 s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); T2 t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y); if (s >= 0.0 && s <= 1.0 && t >= 0.0 && t <= 1.0) { pt.x = p0_x + (t * s1_x); pt.y = p0_y + (t * s1_y); return true; } // LCOV_EXCL_START return false; // LCOV_EXCL_END } template struct intersection_compare { bool operator()(bound_ptr const& b1, bound_ptr const& b2) { return !(b1->current_x > b2->current_x && !slopes_equal(*(b1->current_edge), *(b2->current_edge))); } }; template struct on_intersection_swap { intersect_list& intersects; on_intersection_swap(intersect_list& i) : intersects(i) { } void operator()(bound_ptr const& b1, bound_ptr const& b2) { mapbox::geometry::point pt; if (!get_edge_intersection(*(b1->current_edge), *(b2->current_edge), pt)) { // LCOV_EXCL_START throw std::runtime_error("Trying to find intersection of lines that do not intersect"); // LCOV_EXCL_END } intersects.emplace_back(b1, b2, pt); } }; template void build_intersect_list(active_bound_list& active_bounds, intersect_list& intersects) { bubble_sort(active_bounds.begin(), active_bounds.end(), intersection_compare(), on_intersection_swap(intersects)); } template void intersect_bounds(bound& b1, bound& b2, mapbox::geometry::point const& pt, clip_type cliptype, fill_type subject_fill_type, fill_type clip_fill_type, ring_manager& rings, active_bound_list& active_bounds) { bool b1Contributing = (b1.ring != nullptr); bool b2Contributing = (b2.ring != nullptr); // update winding counts... // assumes that b1 will be to the Right of b2 ABOVE the intersection if (b1.poly_type == b2.poly_type) { if (is_even_odd_fill_type(b1, subject_fill_type, clip_fill_type)) { std::swap(b1.winding_count, b2.winding_count); } else { if (b1.winding_count + b2.winding_delta == 0) { b1.winding_count = -b1.winding_count; } else { b1.winding_count += b2.winding_delta; } if (b2.winding_count - b1.winding_delta == 0) { b2.winding_count = -b2.winding_count; } else { b2.winding_count -= b1.winding_delta; } } } else { if (!is_even_odd_fill_type(b2, subject_fill_type, clip_fill_type)) { b1.winding_count2 += b2.winding_delta; } else { b1.winding_count2 = (b1.winding_count2 == 0) ? 1 : 0; } if (!is_even_odd_fill_type(b1, subject_fill_type, clip_fill_type)) { b2.winding_count2 -= b1.winding_delta; } else { b2.winding_count2 = (b2.winding_count2 == 0) ? 1 : 0; } } fill_type b1FillType, b2FillType, b1FillType2, b2FillType2; if (b1.poly_type == polygon_type_subject) { b1FillType = subject_fill_type; b1FillType2 = clip_fill_type; } else { b1FillType = clip_fill_type; b1FillType2 = subject_fill_type; } if (b2.poly_type == polygon_type_subject) { b2FillType = subject_fill_type; b2FillType2 = clip_fill_type; } else { b2FillType = clip_fill_type; b2FillType2 = subject_fill_type; } std::int32_t b1Wc, b2Wc; switch (b1FillType) { case fill_type_positive: b1Wc = b1.winding_count; break; case fill_type_negative: b1Wc = -b1.winding_count; break; case fill_type_even_odd: case fill_type_non_zero: default: b1Wc = std::abs(static_cast(b1.winding_count)); } switch (b2FillType) { case fill_type_positive: b2Wc = b2.winding_count; break; case fill_type_negative: b2Wc = -b2.winding_count; break; case fill_type_even_odd: case fill_type_non_zero: default: b2Wc = std::abs(static_cast(b2.winding_count)); } if (b1Contributing && b2Contributing) { if ((b1Wc != 0 && b1Wc != 1) || (b2Wc != 0 && b2Wc != 1) || (b1.poly_type != b2.poly_type && cliptype != clip_type_x_or)) { add_local_maximum_point(b1, b2, pt, rings, active_bounds); } else { add_point(b1, active_bounds, pt, rings); add_point(b2, active_bounds, pt, rings); swap_sides(b1, b2); swap_rings(b1, b2); } } else if (b1Contributing) { if (b2Wc == 0 || b2Wc == 1) { add_point(b1, active_bounds, pt, rings); b2.last_point = pt; swap_sides(b1, b2); swap_rings(b1, b2); } } else if (b2Contributing) { if (b1Wc == 0 || b1Wc == 1) { b1.last_point = pt; add_point(b2, active_bounds, pt, rings); swap_sides(b1, b2); swap_rings(b1, b2); } } else if ((b1Wc == 0 || b1Wc == 1) && (b2Wc == 0 || b2Wc == 1)) { // neither bound is currently contributing ... std::int32_t b1Wc2, b2Wc2; switch (b1FillType2) { case fill_type_positive: b1Wc2 = b1.winding_count2; break; case fill_type_negative: b1Wc2 = -b1.winding_count2; break; case fill_type_even_odd: case fill_type_non_zero: default: b1Wc2 = std::abs(static_cast(b1.winding_count2)); } switch (b2FillType2) { case fill_type_positive: b2Wc2 = b2.winding_count2; break; case fill_type_negative: b2Wc2 = -b2.winding_count2; break; case fill_type_even_odd: case fill_type_non_zero: default: b2Wc2 = std::abs(static_cast(b2.winding_count2)); } if (b1.poly_type != b2.poly_type) { add_local_minimum_point(b1, b2, active_bounds, pt, rings); } else if (b1Wc == 1 && b2Wc == 1) { switch (cliptype) { case clip_type_intersection: if (b1Wc2 > 0 && b2Wc2 > 0) { add_local_minimum_point(b1, b2, active_bounds, pt, rings); } break; default: case clip_type_union: if (b1Wc2 <= 0 && b2Wc2 <= 0) { add_local_minimum_point(b1, b2, active_bounds, pt, rings); } break; case clip_type_difference: if (((b1.poly_type == polygon_type_clip) && (b1Wc2 > 0) && (b2Wc2 > 0)) || ((b1.poly_type == polygon_type_subject) && (b1Wc2 <= 0) && (b2Wc2 <= 0))) { add_local_minimum_point(b1, b2, active_bounds, pt, rings); } break; case clip_type_x_or: add_local_minimum_point(b1, b2, active_bounds, pt, rings); } } else { swap_sides(b1, b2); } } } template bool bounds_adjacent(intersect_node const& inode, bound_ptr next) { return (next == inode.bound2) || (next == inode.bound1); } template struct find_first_bound { bound_ptr b1; bound_ptr b2; find_first_bound(intersect_node const& inode) : b1(inode.bound1), b2(inode.bound2) { } bool operator()(bound_ptr const& b) { return b == b1 || b == b2; } }; template void process_intersect_list(intersect_list& intersects, clip_type cliptype, fill_type subject_fill_type, fill_type clip_fill_type, ring_manager& rings, active_bound_list& active_bounds) { for (auto node_itr = intersects.begin(); node_itr != intersects.end(); ++node_itr) { auto b1 = std::find_if(active_bounds.begin(), active_bounds.end(), find_first_bound(*node_itr)); auto b2 = std::next(b1); if (!bounds_adjacent(*node_itr, *b2)) { auto next_itr = std::next(node_itr); while (next_itr != intersects.end()) { auto n1 = std::find_if(active_bounds.begin(), active_bounds.end(), find_first_bound(*next_itr)); auto n2 = std::next(n1); if (bounds_adjacent(*next_itr, *n2)) { b1 = n1; b2 = n2; break; } ++next_itr; } if (next_itr == intersects.end()) { throw std::runtime_error("Could not properly correct intersection order."); } std::iter_swap(node_itr, next_itr); } mapbox::geometry::point pt = round_point(node_itr->pt); intersect_bounds(*(node_itr->bound1), *(node_itr->bound2), pt, cliptype, subject_fill_type, clip_fill_type, rings, active_bounds); std::iter_swap(b1, b2); } } template void update_current_x(active_bound_list& active_bounds, T top_y) { std::size_t pos = 0; for (auto& bnd : active_bounds) { bnd->pos = pos++; bnd->current_x = get_current_x(*bnd->current_edge, top_y); } } template void process_intersections(T top_y, active_bound_list& active_bounds, clip_type cliptype, fill_type subject_fill_type, fill_type clip_fill_type, ring_manager& rings) { if (active_bounds.empty()) { return; } update_current_x(active_bounds, top_y); intersect_list intersects; build_intersect_list(active_bounds, intersects); if (intersects.empty()) { return; } // Restore order of active bounds list std::stable_sort( active_bounds.begin(), active_bounds.end(), [](bound_ptr const& b1, bound_ptr const& b2) { return b1->pos < b2->pos; }); // Sort the intersection list std::stable_sort(intersects.begin(), intersects.end(), intersect_list_sorter()); process_intersect_list(intersects, cliptype, subject_fill_type, clip_fill_type, rings, active_bounds); } } } }