2018-07-31 17:47:52 -07:00

646 lines
18 KiB
C++

#pragma once
#include <assert.h>
#include <cmath>
#include <deque>
#include <list>
#include <map>
#include <mapbox/geometry/box.hpp>
#include <mapbox/geometry/wagyu/point.hpp>
#include <set>
#include <sstream>
#include <vector>
#ifdef DEBUG
#include <execinfo.h>
#include <iostream>
#include <sstream>
#include <stdio.h>
//
// 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 <typename T>
double area_from_point(point_ptr<T> op, std::size_t& size, mapbox::geometry::box<T>& bbox) {
point_ptr<T> 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<double>(op->prev->x + op->x) * static_cast<double>(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 <typename T>
using ring_vector = std::vector<ring_ptr<T>>;
template <typename T>
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<T> bbox; // bounding box of the ring
ring_ptr<T> parent;
ring_vector<T> children;
point_ptr<T> points;
point_ptr<T> 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<double>::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<double>::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<T> 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 <typename T>
using hot_pixel_vector = std::vector<mapbox::geometry::point<T>>;
template <typename T>
using hot_pixel_itr = typename hot_pixel_vector<T>::iterator;
template <typename T>
using hot_pixel_rev_itr = typename hot_pixel_vector<T>::reverse_iterator;
template <typename T>
struct ring_manager {
ring_vector<T> children;
point_vector<T> all_points;
hot_pixel_vector<T> hot_pixels;
hot_pixel_itr<T> current_hp_itr;
std::deque<point<T>> points;
std::deque<ring<T>> rings;
std::vector<point<T>> 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 <typename T>
void preallocate_point_memory(ring_manager<T>& rings, std::size_t size) {
rings.storage.reserve(size);
rings.all_points.reserve(size);
}
template <typename T>
ring_ptr<T> create_new_ring(ring_manager<T>& manager) {
manager.rings.emplace_back();
ring_ptr<T> result = &manager.rings.back();
result->ring_index = manager.index++;
return result;
}
template <typename T>
point_ptr<T>
create_new_point(ring_ptr<T> r, mapbox::geometry::point<T> const& pt, ring_manager<T>& rings) {
point_ptr<T> 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 <typename T>
point_ptr<T> create_new_point(ring_ptr<T> r,
mapbox::geometry::point<T> const& pt,
point_ptr<T> before_this_point,
ring_manager<T>& rings) {
point_ptr<T> 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 <typename T>
void set_to_children(ring_ptr<T> r, ring_vector<T>& children) {
for (auto& c : children) {
if (c == nullptr) {
c = r;
return;
}
}
children.push_back(r);
}
template <typename T>
void remove_from_children(ring_ptr<T> r, ring_vector<T>& children) {
for (auto& c : children) {
if (c == r) {
c = nullptr;
return;
}
}
}
template <typename T>
void assign_as_child(ring_ptr<T> new_ring, ring_ptr<T> parent, ring_manager<T>& 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 <typename T>
void reassign_as_child(ring_ptr<T> ring, ring_ptr<T> parent, ring_manager<T>& 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 <typename T>
void assign_as_sibling(ring_ptr<T> new_ring, ring_ptr<T> sibling, ring_manager<T>& 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 <typename T>
void reassign_as_sibling(ring_ptr<T> ring, ring_ptr<T> sibling, ring_manager<T>& 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 <typename T>
void ring1_replaces_ring2(ring_ptr<T> ring1, ring_ptr<T> ring2, ring_manager<T>& 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 <typename T>
void remove_points(point_ptr<T> pt) {
if (pt != nullptr) {
pt->prev->next = nullptr;
while (pt != nullptr) {
point_ptr<T> tmp = pt;
pt = pt->next;
tmp->next = nullptr;
tmp->prev = nullptr;
tmp->ring = nullptr;
}
}
}
template <typename T>
void remove_ring_and_points(ring_ptr<T> r,
ring_manager<T>& 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<T> pt = r->points;
if (pt != nullptr) {
pt->prev->next = nullptr;
while (pt != nullptr) {
point_ptr<T> tmp = pt;
pt = pt->next;
tmp->next = nullptr;
tmp->prev = nullptr;
tmp->ring = nullptr;
}
}
r->points = nullptr;
r->reset_stats();
}
template <typename T>
void remove_ring(ring_ptr<T> r,
ring_manager<T>& 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 <typename T>
inline std::size_t ring_depth(ring_ptr<T> r) {
std::size_t depth = 0;
if (!r) {
return depth;
}
while (r->parent) {
depth++;
r = r->parent;
}
return depth;
}
template <typename T>
inline bool ring_is_hole(ring_ptr<T> 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 <typename T>
void set_next(const_point_ptr<T>& node, const const_point_ptr<T>& next_node) {
node->next = next_node;
}
template <typename T>
point_ptr<T> get_next(const_point_ptr<T>& node) {
return node->next;
}
template <typename T>
point_ptr<T> get_prev(const_point_ptr<T>& node) {
return node->prev;
}
template <typename T>
void set_prev(const_point_ptr<T>& node, const const_point_ptr<T>& prev_node) {
node->prev = prev_node;
}
template <typename T>
void init(const_point_ptr<T>& node) {
set_next(node, node);
set_prev(node, node);
}
template <typename T>
void link_before(point_ptr<T>& node, point_ptr<T>& new_node) {
point_ptr<T> 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 <typename T>
void link_after(point_ptr<T>& node, point_ptr<T>& new_node) {
point_ptr<T> 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 <typename T>
void transfer_point(point_ptr<T>& p, point_ptr<T>& b, point_ptr<T>& e) {
if (b != e) {
point_ptr<T> prev_p = get_prev(p);
point_ptr<T> prev_b = get_prev(b);
point_ptr<T> 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 <typename T>
void reverse_ring(point_ptr<T> pp) {
if (!pp) {
return;
}
point_ptr<T> pp1;
point_ptr<T> pp2;
pp1 = pp;
do {
pp2 = pp1->next;
pp1->next = pp1->prev;
pp1->prev = pp2;
pp1 = pp2;
} while (pp1 != pp);
}
#ifdef DEBUG
template <class charT, class traits, typename T>
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
ring<T>& 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<T> n = const_cast<ring_ptr<T>>(&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 <typename T>
std::string debug_ring_addresses(ring_ptr<T> 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 <typename T>
std::string output_as_polygon(ring_ptr<T> 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 <class charT, class traits, typename T>
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
ring_vector<T>& 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 <class charT, class traits, typename T>
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
std::deque<ring<T>>& 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 <class charT, class traits, typename T>
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
hot_pixel_vector<T>& hp_vec) {
out << "Hot Pixels: " << std::endl;
for (auto& hp : hp_vec) {
out << hp << std::endl;
}
return out;
}
#endif
}
}
}