#pragma once #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #include #include #include #include // // void* callstack[128]; // int i, frames = backtrace(callstack, 128); // char** strs = backtrace_symbols(callstack, frames); // for (i = 0; i < frames; ++i) { // printf("%s\n", strs[i]); // } // free(strs); #endif namespace mapbox { namespace geometry { namespace wagyu { template double area_from_point(point_ptr op, std::size_t& size, mapbox::geometry::box& bbox) { point_ptr startOp = op; size = 0; double a = 0.0; T min_x = op->x; T max_x = op->x; T min_y = op->y; T max_y = op->y; do { ++size; if (op->x > max_x) { max_x = op->x; } else if (op->x < min_x) { min_x = op->x; } if (op->y > max_y) { max_y = op->y; } else if (op->y < min_y) { min_y = op->y; } a += static_cast(op->prev->x + op->x) * static_cast(op->prev->y - op->y); op = op->next; } while (op != startOp); bbox.min.x = min_x; bbox.max.x = max_x; bbox.min.y = min_y; bbox.max.y = max_y; return a * 0.5; } // NOTE: ring and ring_ptr are forward declared in wagyu/point.hpp template using ring_vector = std::vector>; template struct ring { std::size_t ring_index; // To support unset 0 is undefined and indexes offset by 1 std::size_t size_; // number of points in the ring double area_; // area of the ring mapbox::geometry::box bbox; // bounding box of the ring ring_ptr parent; ring_vector children; point_ptr points; point_ptr bottom_point; bool is_hole_; bool corrected; ring(ring const&) = delete; ring& operator=(ring const&) = delete; ring() : ring_index(0), size_(0), area_(std::numeric_limits::quiet_NaN()), bbox({ 0, 0 }, { 0, 0 }), parent(nullptr), children(), points(nullptr), bottom_point(nullptr), is_hole_(false), corrected(false) { } void reset_stats() { area_ = std::numeric_limits::quiet_NaN(); is_hole_ = false; bbox.min.x = 0; bbox.min.y = 0; bbox.max.x = 0; bbox.max.y = 0; size_ = 0; } void recalculate_stats() { if (points != nullptr) { area_ = area_from_point(points, size_, bbox); is_hole_ = !(area_ > 0.0); } } void set_stats(double a, std::size_t s, mapbox::geometry::box const& b) { bbox = b; area_ = a; size_ = s; is_hole_ = !(area_ > 0.0); } double area() { if (std::isnan(area_)) { recalculate_stats(); } return area_; } bool is_hole() { if (std::isnan(area_)) { recalculate_stats(); } return is_hole_; } std::size_t size() { if (std::isnan(area_)) { recalculate_stats(); } return size_; } }; template using hot_pixel_vector = std::vector>; template using hot_pixel_itr = typename hot_pixel_vector::iterator; template using hot_pixel_rev_itr = typename hot_pixel_vector::reverse_iterator; template struct ring_manager { ring_vector children; point_vector all_points; hot_pixel_vector hot_pixels; hot_pixel_itr current_hp_itr; std::deque> points; std::deque> rings; std::vector> storage; std::size_t index; ring_manager(ring_manager const&) = delete; ring_manager& operator=(ring_manager const&) = delete; ring_manager() : children(), all_points(), hot_pixels(), current_hp_itr(hot_pixels.end()), points(), rings(), storage(), index(0) { } }; template void preallocate_point_memory(ring_manager& rings, std::size_t size) { rings.storage.reserve(size); rings.all_points.reserve(size); } template ring_ptr create_new_ring(ring_manager& manager) { manager.rings.emplace_back(); ring_ptr result = &manager.rings.back(); result->ring_index = manager.index++; return result; } template point_ptr create_new_point(ring_ptr r, mapbox::geometry::point const& pt, ring_manager& rings) { point_ptr point; if (rings.storage.size() < rings.storage.capacity()) { rings.storage.emplace_back(r, pt); point = &rings.storage.back(); } else { rings.points.emplace_back(r, pt); point = &rings.points.back(); } rings.all_points.push_back(point); return point; } template point_ptr create_new_point(ring_ptr r, mapbox::geometry::point const& pt, point_ptr before_this_point, ring_manager& rings) { point_ptr point; if (rings.storage.size() < rings.storage.capacity()) { rings.storage.emplace_back(r, pt, before_this_point); point = &rings.storage.back(); } else { rings.points.emplace_back(r, pt, before_this_point); point = &rings.points.back(); } rings.all_points.push_back(point); return point; } template void set_to_children(ring_ptr r, ring_vector& children) { for (auto& c : children) { if (c == nullptr) { c = r; return; } } children.push_back(r); } template void remove_from_children(ring_ptr r, ring_vector& children) { for (auto& c : children) { if (c == r) { c = nullptr; return; } } } template void assign_as_child(ring_ptr new_ring, ring_ptr parent, ring_manager& manager) { // Assigning as a child assumes that this is // a brand new ring. Therefore it does // not have any existing relationships if ((parent == nullptr && new_ring->is_hole()) || (parent != nullptr && new_ring->is_hole() == parent->is_hole())) { throw std::runtime_error( "Trying to assign a child that is the same orientation as the parent"); } auto& children = parent == nullptr ? manager.children : parent->children; set_to_children(new_ring, children); new_ring->parent = parent; } template void reassign_as_child(ring_ptr ring, ring_ptr parent, ring_manager& manager) { // Reassigning a ring assumes it already // has an existing parent if ((parent == nullptr && ring->is_hole()) || (parent != nullptr && ring->is_hole() == parent->is_hole())) { throw std::runtime_error( "Trying to re-assign a child that is the same orientation as the parent"); } // Remove the old child relationship auto& old_children = ring->parent == nullptr ? manager.children : ring->parent->children; remove_from_children(ring, old_children); // Add new child relationship auto& children = parent == nullptr ? manager.children : parent->children; set_to_children(ring, children); ring->parent = parent; } template void assign_as_sibling(ring_ptr new_ring, ring_ptr sibling, ring_manager& manager) { // Assigning as a sibling assumes that this is // a brand new ring. Therefore it does // not have any existing relationships if (new_ring->is_hole() != sibling->is_hole()) { throw std::runtime_error( "Trying to assign to be a sibling that is not the same orientation as the sibling"); } auto& children = sibling->parent == nullptr ? manager.children : sibling->parent->children; set_to_children(new_ring, children); new_ring->parent = sibling->parent; } template void reassign_as_sibling(ring_ptr ring, ring_ptr sibling, ring_manager& manager) { if (ring->parent == sibling->parent) { return; } // Assigning as a sibling assumes that this is // a brand new ring. Therefore it does // not have any existing relationships if (ring->is_hole() != sibling->is_hole()) { throw std::runtime_error( "Trying to assign to be a sibling that is not the same orientation as the sibling"); } // Remove the old child relationship auto& old_children = ring->parent == nullptr ? manager.children : ring->parent->children; remove_from_children(ring, old_children); // Add new relationship auto& children = sibling->parent == nullptr ? manager.children : sibling->parent->children; set_to_children(ring, children); ring->parent = sibling->parent; } template void ring1_replaces_ring2(ring_ptr ring1, ring_ptr ring2, ring_manager& manager) { assert(ring1 != ring2); auto& ring1_children = ring1 == nullptr ? manager.children : ring1->children; for (auto& c : ring2->children) { if (c == nullptr) { continue; } c->parent = ring1; set_to_children(c, ring1_children); c = nullptr; } // Remove the old child relationship auto& old_children = ring2->parent == nullptr ? manager.children : ring2->parent->children; remove_from_children(ring2, old_children); ring2->points = nullptr; ring2->reset_stats(); } template void remove_points(point_ptr pt) { if (pt != nullptr) { pt->prev->next = nullptr; while (pt != nullptr) { point_ptr tmp = pt; pt = pt->next; tmp->next = nullptr; tmp->prev = nullptr; tmp->ring = nullptr; } } } template void remove_ring_and_points(ring_ptr r, ring_manager& manager, bool remove_children = true, bool remove_from_parent = true) { // Removes a ring and any children that might be // under that ring. for (auto& c : r->children) { if (c == nullptr) { continue; } if (remove_children) { remove_ring_and_points(c, manager, true, false); } c = nullptr; } if (remove_from_parent) { // Remove the old child relationship auto& old_children = r->parent == nullptr ? manager.children : r->parent->children; remove_from_children(r, old_children); } point_ptr pt = r->points; if (pt != nullptr) { pt->prev->next = nullptr; while (pt != nullptr) { point_ptr tmp = pt; pt = pt->next; tmp->next = nullptr; tmp->prev = nullptr; tmp->ring = nullptr; } } r->points = nullptr; r->reset_stats(); } template void remove_ring(ring_ptr r, ring_manager& manager, bool remove_children = true, bool remove_from_parent = true) { // Removes a ring and any children that might be // under that ring. for (auto& c : r->children) { if (c == nullptr) { continue; } if (remove_children) { remove_ring(c, manager, true, false); } c = nullptr; } if (remove_from_parent) { // Remove the old child relationship auto& old_children = r->parent == nullptr ? manager.children : r->parent->children; remove_from_children(r, old_children); } r->points = nullptr; r->reset_stats(); } template inline std::size_t ring_depth(ring_ptr r) { std::size_t depth = 0; if (!r) { return depth; } while (r->parent) { depth++; r = r->parent; } return depth; } template inline bool ring_is_hole(ring_ptr r) { // This is different then the "normal" way of determing if // a ring is a hole or not because it uses the depth of the // the ring to determine if it is a hole or not. This is only done // intially when rings are output from Vatti. return ring_depth(r) & 1; } template void set_next(const_point_ptr& node, const const_point_ptr& next_node) { node->next = next_node; } template point_ptr get_next(const_point_ptr& node) { return node->next; } template point_ptr get_prev(const_point_ptr& node) { return node->prev; } template void set_prev(const_point_ptr& node, const const_point_ptr& prev_node) { node->prev = prev_node; } template void init(const_point_ptr& node) { set_next(node, node); set_prev(node, node); } template void link_before(point_ptr& node, point_ptr& new_node) { point_ptr prev_node = get_prev(node); set_prev(new_node, prev_node); set_next(new_node, node); set_prev(node, new_node); set_next(prev_node, new_node); } template void link_after(point_ptr& node, point_ptr& new_node) { point_ptr next_node = get_next(node); set_prev(new_node, node); set_next(new_node, next_node); set_next(node, new_node); set_prev(next_node, new_node); } template void transfer_point(point_ptr& p, point_ptr& b, point_ptr& e) { if (b != e) { point_ptr prev_p = get_prev(p); point_ptr prev_b = get_prev(b); point_ptr prev_e = get_prev(e); set_next(prev_e, p); set_prev(p, prev_e); set_next(prev_b, e); set_prev(e, prev_b); set_next(prev_p, b); set_prev(b, prev_p); } else { link_before(p, b); } } template void reverse_ring(point_ptr pp) { if (!pp) { return; } point_ptr pp1; point_ptr pp2; pp1 = pp; do { pp2 = pp1->next; pp1->next = pp1->prev; pp1->prev = pp2; pp1 = pp2; } while (pp1 != pp); } #ifdef DEBUG template inline std::basic_ostream& operator<<(std::basic_ostream& out, ring& r) { out << " ring_index: " << r.ring_index << std::endl; if (!r.parent) { // out << " parent_ring ptr: nullptr" << std::endl; out << " parent_index: -----" << std::endl; } else { // out << " parent_ring ptr: " << r.parent << std::endl; out << " parent_ring idx: " << r.parent->ring_index << std::endl; } ring_ptr n = const_cast>(&r); if (ring_is_hole(n)) { out << " is_hole: true " << std::endl; } else { out << " is_hole: false " << std::endl; } auto pt_itr = r.points; if (pt_itr) { out << " area: " << r.area() << std::endl; out << " points:" << std::endl; out << " [[[" << pt_itr->x << "," << pt_itr->y << "],"; pt_itr = pt_itr->next; while (pt_itr != r.points) { out << "[" << pt_itr->x << "," << pt_itr->y << "],"; pt_itr = pt_itr->next; } out << "[" << pt_itr->x << "," << pt_itr->y << "]]]" << std::endl; } else { out << " area: NONE" << std::endl; out << " points: NONE" << std::endl; } return out; } template std::string debug_ring_addresses(ring_ptr r) { std::ostringstream out; out << "Ring: " << r->ring_index << std::endl; if (r->points == nullptr) { out << " Ring has no points" << std::endl; return out.str(); } auto pt_itr = r->points; do { out << " [" << pt_itr->x << "," << pt_itr->y << "] - " << pt_itr << std::endl; pt_itr = pt_itr->next; } while (pt_itr != r->points); return out.str(); } template std::string output_as_polygon(ring_ptr r) { std::ostringstream out; auto pt_itr = r->points; if (pt_itr) { out << "["; out << "[[" << pt_itr->x << "," << pt_itr->y << "],"; pt_itr = pt_itr->next; while (pt_itr != r->points) { out << "[" << pt_itr->x << "," << pt_itr->y << "],"; pt_itr = pt_itr->next; } out << "[" << pt_itr->x << "," << pt_itr->y << "]]"; for (auto const& c : r->children) { if (c == nullptr) { continue; } pt_itr = c->points; if (pt_itr) { out << ",[[" << pt_itr->x << "," << pt_itr->y << "],"; pt_itr = pt_itr->next; while (pt_itr != c->points) { out << "[" << pt_itr->x << "," << pt_itr->y << "],"; pt_itr = pt_itr->next; } out << "[" << pt_itr->x << "," << pt_itr->y << "]]"; } } out << "]" << std::endl; } else { out << "[]" << std::endl; } return out.str(); } template inline std::basic_ostream& operator<<(std::basic_ostream& out, ring_vector& rings) { out << "START RING VECTOR" << std::endl; for (auto& r : rings) { if (r == nullptr || !r->points) { continue; } out << " ring: " << r->ring_index << " - " << r << std::endl; out << *r; } out << "END RING VECTOR" << std::endl; return out; } template inline std::basic_ostream& operator<<(std::basic_ostream& out, std::deque>& rings) { out << "START RING VECTOR" << std::endl; for (auto& r : rings) { if (!r.points) { continue; } out << " ring: " << r.ring_index << std::endl; out << r; } out << "END RING VECTOR" << std::endl; return out; } template inline std::basic_ostream& operator<<(std::basic_ostream& out, hot_pixel_vector& hp_vec) { out << "Hot Pixels: " << std::endl; for (auto& hp : hp_vec) { out << hp << std::endl; } return out; } #endif } } }