| |
| |
|
|
| #pragma once |
|
|
| #include <vector> |
| #include <random> |
| #include <algorithm> |
|
|
| #include "../geometry.h" |
|
|
| namespace graph_detection { |
|
|
|
|
|
|
| struct Edge { |
| int32_t A; |
| int32_t B; |
|
|
| Edge() = default; |
| Edge(int32_t a, int32_t b) : A(a), B(b) {} |
|
|
| bool Touches(int32_t idx) const { return A == idx || B == idx; } |
| bool Touches(const Edge &other) const; |
| }; |
|
|
| inline |
| bool edge_touches(const Edge &edge, int32_t vertex) { |
| return edge.A == vertex || edge.B == vertex; |
| } |
|
|
| inline |
| bool Edge::Touches(const Edge &other) const { |
| return edge_touches(other, A) || edge_touches(other, B); |
| } |
|
|
| typedef Point_<float> Pointf; |
| typedef AABB_<float> AABBf; |
| typedef Polygon_<float> Polyf; |
| typedef std::vector<Pointf> Polyline; |
|
|
| std::vector<Edge> find_bottom(const Polygon_<float> &poly, bool useVertexOrder); |
|
|
| void find_long_edges(const Polygon_<float> &poly, Edge *bottoms, std::vector<Edge> &outLongEdge1, std::vector<Edge> &outLongEdge2); |
|
|
| void split_edge_sequence_by_step(const Polygon_<float> &poly, const std::vector<Edge> &longEdge1, const std::vector<Edge> &longEdge2, |
| float step, std::vector<Pointf> &outInnerPoints1, std::vector<Pointf> &outInnerPoints2); |
|
|
| std::string print_poly(const Polyf &poly); |
|
|
| template<typename T> |
| inline |
| std::vector<T> vec_cumsum(const std::vector<T> &v) |
| { |
| std::vector<T> ret; |
| ret.reserve(v.size() + 1); |
| ret.push_back(0); |
| for (T val : v) { |
| ret.push_back(ret.back() + val); |
| } |
| return ret; |
| } |
|
|
| template<typename RandEng, typename Fn> |
| inline |
| void n_choose_k(size_t n, size_t k, RandEng &randEng, Fn fn) |
| { |
| if (k == 0) return; |
|
|
| |
| |
| for (size_t i = 0; i < n; ++i) { |
| size_t leftover = n - i; |
| if (leftover <= k) { |
| fn(i); |
| --k; |
| } else { |
| float p = std::uniform_real_distribution<float>(0.0f, 1.0f)(randEng); |
| float probSample = float{k} / float{leftover}; |
| if (p < probSample) { |
| fn(i); |
| --k; |
| } |
| } |
| } |
| } |
|
|
| template<typename T> |
| inline T clamp(T val, T minVal, T maxVal) { |
| return std::max(std::min(val, maxVal), minVal); |
| } |
|
|
| inline |
| Pointf avg_point(const std::vector<Pointf> &points) |
| { |
| return std::accumulate(std::begin(points), std::end(points), Pointf(0,0)) / float(points.size()); |
| } |
|
|
| inline |
| float vector_sin(const Pointf &pt) |
| { |
| |
| return pt.Y / (length(pt) + 1e-8); |
| } |
|
|
| inline |
| float vector_cos(const Pointf &pt) |
| { |
| |
| return pt.X / (length(pt) + 1e-8); |
| } |
|
|
| inline |
| void vector_cos_sin(const Pointf & pt, float &outCos, float &outSin) |
| { |
| float len = length(pt) + 1e-8; |
| outCos = pt.X / len; |
| outSin = pt.Y / len; |
| } |
|
|
| inline |
| float point_dist_to_line(const Pointf &l1, const Pointf &l2, const Pointf &pt) |
| { |
| auto d = l2 - l1; |
|
|
| auto lineLen = length(d); |
|
|
| if (lineLen > 0) { |
| float distance = abs( |
| d.Y * pt.X |
| - d.X * pt.Y |
| + l2.X * l1.Y |
| - l2.Y * l1.X |
| ) / lineLen; |
| return distance; |
| } else { |
| return length(pt - l1); |
| } |
| } |
|
|
| template<typename T> |
| T find_mode(std::vector<T> &inputs) { |
| using std::sort; |
| using std::begin; |
| using std::end; |
|
|
| if (inputs.empty()) { |
| throw std::runtime_error("Cannot find mode of empty distribution!"); |
| } |
|
|
| sort(begin(inputs), end(inputs)); |
|
|
| T currVal = inputs[0]; |
| size_t currCount = 1; |
|
|
| T modeVal = inputs[0]; |
| size_t modeCount = 1; |
|
|
| auto commitCurr = [&] () { |
| if (currCount > modeCount) { |
| modeCount = currCount; |
| modeVal = currVal; |
| } |
| }; |
|
|
| for (size_t i = 1; i < inputs.size(); ++i) { |
| if (inputs[i] == currVal) { |
| ++currCount; |
| } else { |
| |
| commitCurr(); |
|
|
| currCount = 1; |
| currVal = inputs[i]; |
| } |
| } |
|
|
| commitCurr(); |
|
|
| return modeVal; |
| } |
|
|
| } |
|
|