contrek 1.1.6 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/README.md +50 -23
- data/contrek.gemspec +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +19 -11
- data/ext/cpp_polygon_finder/PolygonFinder/clean.sh +28 -0
- data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +4 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +66 -61
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Primitives.h +14 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ClippedPolygonFinder.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +86 -23
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +3 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +23 -29
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +7 -12
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +12 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +55 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +32 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +21 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +4 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +14 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +14 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.cpp +15 -6
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.h +11 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp +42 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h +34 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +72 -11
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +9 -1
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +4 -0
- data/lib/contrek/finder/concurrent/cluster.rb +58 -9
- data/lib/contrek/finder/concurrent/cursor.rb +10 -6
- data/lib/contrek/finder/concurrent/finder.rb +8 -2
- data/lib/contrek/finder/concurrent/inner_polyline.rb +49 -0
- data/lib/contrek/finder/concurrent/polyline.rb +46 -0
- data/lib/contrek/finder/concurrent/sequence.rb +21 -0
- data/lib/contrek/finder/concurrent/shape.rb +29 -2
- data/lib/contrek/finder/concurrent/tile.rb +36 -7
- data/lib/contrek/finder/node.rb +3 -1
- data/lib/contrek/finder/node_cluster.rb +56 -48
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +1 -0
- metadata +9 -9
- data/docs/images/strict_bounds_off.png +0 -0
- data/docs/images/strict_bounds_on.png +0 -0
- data/docs/images/stripes/whole.png +0 -0
- data/docs/images/stripes/whole_0.png +0 -0
- data/docs/images/stripes/whole_256.png +0 -0
- data/docs/images/stripes/whole_512.png +0 -0
- data/docs/images/stripes/whole_768.png +0 -0
|
@@ -11,14 +11,22 @@
|
|
|
11
11
|
#include <list>
|
|
12
12
|
#include <iostream>
|
|
13
13
|
#include <vector>
|
|
14
|
+
#include "InnerPolyline.h"
|
|
14
15
|
|
|
15
16
|
class Point;
|
|
16
17
|
class Polyline;
|
|
17
18
|
class Shape {
|
|
18
19
|
public:
|
|
19
|
-
Shape(Polyline* outer_polyline, const std::
|
|
20
|
-
virtual ~Shape();
|
|
20
|
+
Shape(Polyline* outer_polyline, const std::vector<InnerPolyline*>& inner_polylines);
|
|
21
21
|
Polyline* outer_polyline = nullptr;
|
|
22
|
-
std::
|
|
22
|
+
std::vector<InnerPolyline*> inner_polylines;
|
|
23
|
+
Shape* merged_to_shape = nullptr;
|
|
24
|
+
InnerPolyline* parent_inner_polyline = nullptr;
|
|
25
|
+
std::vector<Shape*> children_shapes;
|
|
23
26
|
void clear_inner();
|
|
27
|
+
bool reassociation_skip = false;
|
|
28
|
+
Shape* parent_shape() { return parent_shape_; }
|
|
29
|
+
void set_parent_shape(Shape*);
|
|
30
|
+
private:
|
|
31
|
+
Shape* parent_shape_ = nullptr;
|
|
24
32
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ShapePool.cpp
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include <vector>
|
|
11
|
+
#include "Shape.h"
|
|
12
|
+
#include "ShapePool.h"
|
|
13
|
+
|
|
14
|
+
Shape* ShapePool::acquire_shape(Polyline* outer_polyline, const std::vector<InnerPolyline*>& inner_polylines) {
|
|
15
|
+
shapes_storage.emplace_back(outer_polyline, inner_polylines);
|
|
16
|
+
Shape* shape = &shapes_storage.back();
|
|
17
|
+
return shape;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
InnerPolyline* ShapePool::acquire_inner_polyline(std::vector<Point*> coords, Shape* shape, bool rec) {
|
|
21
|
+
inner_polylines_storage.emplace_back(coords, shape, rec);
|
|
22
|
+
InnerPolyline* ip = &inner_polylines_storage.back();
|
|
23
|
+
return ip;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
InnerPolyline* ShapePool::acquire_inner_polyline(Sequence* seq) {
|
|
27
|
+
inner_polylines_storage.emplace_back(seq);
|
|
28
|
+
InnerPolyline* ip = &inner_polylines_storage.back();
|
|
29
|
+
return ip;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Sequence* ShapePool::acquire_sequence() {
|
|
33
|
+
sequences_storage.emplace_back();
|
|
34
|
+
Sequence* s = &sequences_storage.back();
|
|
35
|
+
return s;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Polyline* ShapePool::acquire_polyline(Tile* tile, const std::vector<Point*>& polygon, const std::optional<RectBounds>& bounds = std::nullopt) {
|
|
39
|
+
polylines_storage.emplace_back(tile, polygon, bounds);
|
|
40
|
+
Polyline* p = &polylines_storage.back();
|
|
41
|
+
return p;
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ShapePool.h
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#pragma once
|
|
11
|
+
#include <deque>
|
|
12
|
+
#include <vector>
|
|
13
|
+
#include "Shape.h"
|
|
14
|
+
#include "../RectBounds.h"
|
|
15
|
+
#include "Polyline.h"
|
|
16
|
+
|
|
17
|
+
class Shape;
|
|
18
|
+
class Tile;
|
|
19
|
+
class Polyline;
|
|
20
|
+
class InnerPolyline;
|
|
21
|
+
class ShapePool {
|
|
22
|
+
private:
|
|
23
|
+
std::deque<Shape> shapes_storage;
|
|
24
|
+
std::deque<InnerPolyline> inner_polylines_storage;
|
|
25
|
+
std::deque<Sequence> sequences_storage;
|
|
26
|
+
std::deque<Polyline> polylines_storage;
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
Shape* acquire_shape(Polyline* outer_polyline, const std::vector<InnerPolyline*>& inner_polylines);
|
|
30
|
+
InnerPolyline* acquire_inner_polyline(std::vector<Point*> coords, Shape* s, bool rec);
|
|
31
|
+
InnerPolyline* acquire_inner_polyline(Sequence* seq);
|
|
32
|
+
Sequence* acquire_sequence();
|
|
33
|
+
Polyline* acquire_polyline(Tile* tile, const std::vector<Point*>& polygon, const std::optional<RectBounds>& bounds);
|
|
34
|
+
};
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
#include <iostream>
|
|
12
12
|
#include <list>
|
|
13
13
|
#include <utility>
|
|
14
|
+
#include <vector>
|
|
15
|
+
#include <unordered_map>
|
|
14
16
|
#include "Tile.h"
|
|
15
17
|
#include "Shape.h"
|
|
16
18
|
#include "Polyline.h"
|
|
@@ -24,13 +26,15 @@ Tile::Tile(Finder *finder, int start_x, int end_x, std::string name, const Bench
|
|
|
24
26
|
end_x_(end_x),
|
|
25
27
|
name_(name),
|
|
26
28
|
benchmarks(b) {
|
|
29
|
+
this->shapes_pool = new ShapePool();
|
|
30
|
+
this->shapes_pools.push_back(this->shapes_pool);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
Tile::~Tile() {
|
|
30
|
-
for (Shape* shape : shapes_) {
|
|
31
|
-
delete shape;
|
|
32
|
-
}
|
|
33
34
|
shapes_.clear();
|
|
35
|
+
for (ShapePool* pool : this->shapes_pools) {
|
|
36
|
+
delete pool;
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
bool Tile::whole() {
|
|
@@ -46,18 +50,37 @@ bool Tile::right() {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
void Tile::initial_process(ClippedPolygonFinder *finder) {
|
|
49
|
-
this->assign_raw_polygons(finder->get_cluster()->polygons);
|
|
53
|
+
this->assign_raw_polygons(finder->get_cluster()->polygons, finder->get_cluster()->treemap);
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
void Tile::assign_raw_polygons(const std::list<Polygon>& raw_polylines) {
|
|
56
|
+
void Tile::assign_raw_polygons(const std::list<Polygon>& raw_polylines, const std::vector<std::pair<int, int>>& treemap) {
|
|
57
|
+
std::unordered_map<int, Shape*> shapes_map;
|
|
58
|
+
int polyline_index = 0;
|
|
53
59
|
for (const auto& raw_polyline : raw_polylines)
|
|
54
60
|
{ if (raw_polyline.bounds.width() > 0)
|
|
55
|
-
{ Polyline* polyline =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
{ Polyline* polyline = this->shapes_pool->acquire_polyline(this, raw_polyline.outer, raw_polyline.bounds);
|
|
62
|
+
std::vector<InnerPolyline*> inner_polylines_list;
|
|
63
|
+
for (auto& raw_points : raw_polyline.inner) {
|
|
64
|
+
inner_polylines_list.push_back(this->shapes_pool->acquire_inner_polyline(raw_points, nullptr, false));
|
|
65
|
+
}
|
|
66
|
+
Shape* shape = this->shapes_pool->acquire_shape(polyline, inner_polylines_list);
|
|
67
|
+
polyline->shape = shape;
|
|
68
|
+
this->shapes_.push_back(shape);
|
|
69
|
+
|
|
70
|
+
if (treemap.size() > 0)
|
|
71
|
+
{ auto treemap_entry = treemap[polyline_index];
|
|
72
|
+
shapes_map[polyline_index] = shape;
|
|
73
|
+
if (treemap_entry.first != -1 && treemap_entry.second != -1) {
|
|
74
|
+
auto it = shapes_map.find(treemap_entry.first);
|
|
75
|
+
if (it != shapes_map.end()) {
|
|
76
|
+
Shape* parent = it->second;
|
|
77
|
+
shape->set_parent_shape(parent);
|
|
78
|
+
shape->parent_inner_polyline = parent->inner_polylines[treemap_entry.second];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
59
81
|
}
|
|
60
82
|
}
|
|
83
|
+
polyline_index++;
|
|
61
84
|
}
|
|
62
85
|
}
|
|
63
86
|
|
|
@@ -73,7 +96,12 @@ bool Tile::tg_border(const Point& coord) {
|
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
void Tile::info() {
|
|
76
|
-
std::cout <<
|
|
99
|
+
std::cout << this->toString() << std::endl;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
std::string Tile::toString() {
|
|
103
|
+
std::string s = "TILE name=" + name_ + " start_x=" + std::to_string(start_x_) + " end_x=" + std::to_string(end_x_);
|
|
104
|
+
return s;
|
|
77
105
|
}
|
|
78
106
|
|
|
79
107
|
const std::list<Shape*>& Tile::boundary_shapes()
|
|
@@ -96,7 +124,7 @@ std::list<Polygon> Tile::to_raw_polygons()
|
|
|
96
124
|
poly.outer = s->outer_polyline->raw();
|
|
97
125
|
if (!s->inner_polylines.empty()) {
|
|
98
126
|
for (auto inner : s->inner_polylines) {
|
|
99
|
-
poly.inner.push_back(inner);
|
|
127
|
+
poly.inner.push_back(inner->raw());
|
|
100
128
|
}
|
|
101
129
|
}
|
|
102
130
|
retme.push_back(std::move(poly));
|
|
@@ -104,3 +132,36 @@ std::list<Polygon> Tile::to_raw_polygons()
|
|
|
104
132
|
}
|
|
105
133
|
return(retme);
|
|
106
134
|
}
|
|
135
|
+
|
|
136
|
+
std::vector<std::pair<int, int>> Tile::compute_treemap()
|
|
137
|
+
{ std::unordered_map<Shape*, int> shapes_map;
|
|
138
|
+
int current_index = 0;
|
|
139
|
+
|
|
140
|
+
for (auto* shape : this->shapes_) {
|
|
141
|
+
if (shape->outer_polyline->is_empty()) continue;
|
|
142
|
+
shapes_map[shape] = current_index++;
|
|
143
|
+
}
|
|
144
|
+
std::vector<std::pair<int, int>> treemap;
|
|
145
|
+
treemap.reserve(shapes_map.size());
|
|
146
|
+
|
|
147
|
+
for (auto* shape : this->shapes_) {
|
|
148
|
+
if (shape->outer_polyline->is_empty()) continue;
|
|
149
|
+
if (shape->parent_shape() != nullptr) {
|
|
150
|
+
int p_idx = shapes_map[shape->parent_shape()];
|
|
151
|
+
const auto& inners = shape->parent_shape()->inner_polylines;
|
|
152
|
+
auto it = std::find(inners.begin(), inners.end(), shape->parent_inner_polyline);
|
|
153
|
+
int inner_idx = static_cast<int>(std::distance(inners.begin(), it));
|
|
154
|
+
treemap.push_back({p_idx, inner_idx});
|
|
155
|
+
} else {
|
|
156
|
+
treemap.push_back({-1, -1});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return treemap;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void Tile::adopt(Tile* other) {
|
|
163
|
+
for (ShapePool* pool : other->shapes_pools) {
|
|
164
|
+
this->shapes_pools.push_back(pool);
|
|
165
|
+
}
|
|
166
|
+
other->shapes_pools.clear();
|
|
167
|
+
}
|
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
7
|
* See the LICENSE file in this directory for the full license text.
|
|
8
8
|
*/
|
|
9
|
+
|
|
9
10
|
#pragma once
|
|
10
11
|
#include <string>
|
|
11
12
|
#include <list>
|
|
13
|
+
#include <vector>
|
|
12
14
|
#include "Finder.h"
|
|
13
15
|
#include "ClippedPolygonFinder.h"
|
|
16
|
+
#include "ShapePool.h"
|
|
14
17
|
|
|
15
18
|
class Finder;
|
|
16
19
|
class Cluster;
|
|
@@ -51,7 +54,12 @@ class Tile {
|
|
|
51
54
|
void info();
|
|
52
55
|
bool tg_border(const Point& coord);
|
|
53
56
|
void assign_shapes(std::list<Shape*>& shapes);
|
|
54
|
-
void assign_raw_polygons(const std::list<Polygon>& raw_polylines);
|
|
57
|
+
void assign_raw_polygons(const std::list<Polygon>& raw_polylines, const std::vector<std::pair<int, int>>& treemap);
|
|
55
58
|
std::list<Polygon> to_raw_polygons();
|
|
59
|
+
std::vector<std::pair<int, int>> compute_treemap();
|
|
60
|
+
std::string toString();
|
|
56
61
|
Benchmarks benchmarks;
|
|
62
|
+
std::vector<ShapePool*> shapes_pools;
|
|
63
|
+
ShapePool* shapes_pool;
|
|
64
|
+
void adopt(Tile* other);
|
|
57
65
|
};
|
|
@@ -63,6 +63,8 @@
|
|
|
63
63
|
#include "PolygonFinder/src/polygon/finder/concurrent/Tile.cpp"
|
|
64
64
|
#include "PolygonFinder/src/polygon/finder/concurrent/Polyline.h"
|
|
65
65
|
#include "PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp"
|
|
66
|
+
#include "PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h"
|
|
67
|
+
#include "PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp"
|
|
66
68
|
#include "PolygonFinder/src/polygon/finder/concurrent/Partitionable.h"
|
|
67
69
|
#include "PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp"
|
|
68
70
|
#include "PolygonFinder/src/polygon/finder/concurrent/Shape.h"
|
|
@@ -91,6 +93,8 @@
|
|
|
91
93
|
#include "PolygonFinder/src/polygon/finder/concurrent/HorizontalMerger.cpp"
|
|
92
94
|
#include "PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.h"
|
|
93
95
|
#include "PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp"
|
|
96
|
+
#include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.h"
|
|
97
|
+
#include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp"
|
|
94
98
|
extern "C" {
|
|
95
99
|
#include "PolygonFinder/src/polygon/bitmaps/spng.h"
|
|
96
100
|
}
|
|
@@ -22,10 +22,12 @@ module Contrek
|
|
|
22
22
|
# outer sequences and both the old and new inner ones. Shapes that are not in the contact area are directly
|
|
23
23
|
# reassigned unchanged to the new shape.
|
|
24
24
|
def merge_tiles!
|
|
25
|
+
treemap = @finder.options[:treemap]
|
|
25
26
|
tot_inner = 0
|
|
26
27
|
tot_outer = 0
|
|
27
28
|
|
|
28
29
|
new_shapes = []
|
|
30
|
+
all_new_inner_polylines = []
|
|
29
31
|
tot_outer += Benchmark.measure do
|
|
30
32
|
@tiles.each do |tile|
|
|
31
33
|
tile.shapes.each do |shape|
|
|
@@ -43,30 +45,61 @@ module Contrek
|
|
|
43
45
|
next if shape.outer_polyline.on?(Polyline::TRACKED_OUTER) || shape.outer_polyline.width == 0
|
|
44
46
|
|
|
45
47
|
if shape.outer_polyline.boundary? && shape.outer_polyline.next_tile_eligible_shapes.any?
|
|
46
|
-
new_outer = nil
|
|
47
|
-
new_inners = shape.inner_polylines
|
|
48
48
|
cursor = Cursor.new(cluster: self, shape: shape)
|
|
49
49
|
|
|
50
|
+
new_outer = nil
|
|
50
51
|
tot_outer += Benchmark.measure do
|
|
51
52
|
new_outer = cursor.join_outers!
|
|
52
53
|
end.real
|
|
54
|
+
|
|
55
|
+
new_inners = shape.inner_polylines
|
|
56
|
+
new_inner_polylines = []
|
|
53
57
|
tot_inner += Benchmark.measure do
|
|
54
|
-
|
|
55
|
-
new_inners +=
|
|
58
|
+
new_inner_polylines = cursor.join_inners!(new_outer)
|
|
59
|
+
new_inners += new_inner_polylines
|
|
60
|
+
if treemap
|
|
61
|
+
new_inner_polylines.each { |p| p.sequence.compute_vertical_bounds! }
|
|
62
|
+
all_new_inner_polylines += new_inner_polylines
|
|
63
|
+
cursor.orphan_inners.each do |orphan_inner|
|
|
64
|
+
all_new_inner_polylines << orphan_inner if orphan_inner.recombined
|
|
65
|
+
end
|
|
66
|
+
end
|
|
56
67
|
new_inners += cursor.orphan_inners
|
|
57
68
|
end.real
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
polyline = Polyline.new(tile: tile, polygon: new_outer.to_a)
|
|
71
|
+
inserting_new_shape = Shape.init_by(polyline, new_inners)
|
|
72
|
+
new_shapes << inserting_new_shape
|
|
73
|
+
polyline.shape = inserting_new_shape
|
|
74
|
+
inserting_new_shape.set_parent_shape(shape.parent_shape)
|
|
75
|
+
|
|
76
|
+
new_inner_polylines.each { |inner_polyline| inner_polyline.sequence.shape = inserting_new_shape }
|
|
77
|
+
|
|
78
|
+
if treemap
|
|
79
|
+
cursor.shapes_sequence.each do |merged_shape|
|
|
80
|
+
merged_shape.merged_to_shape = inserting_new_shape
|
|
81
|
+
end
|
|
82
|
+
assign_ancestry(inserting_new_shape, all_new_inner_polylines)
|
|
64
83
|
end
|
|
65
84
|
else
|
|
85
|
+
if treemap && !shape.reassociation_skip && shape.parent_shape.nil?
|
|
86
|
+
assign_ancestry(shape, all_new_inner_polylines)
|
|
87
|
+
end
|
|
66
88
|
new_shapes << shape
|
|
67
89
|
end
|
|
68
90
|
end
|
|
69
91
|
end
|
|
92
|
+
|
|
93
|
+
if treemap
|
|
94
|
+
@tiles.each do |tile|
|
|
95
|
+
tile.shapes.each do |shape|
|
|
96
|
+
if (merged_to_shape = shape.parent_shape&.merged_to_shape)
|
|
97
|
+
shape.set_parent_shape(merged_to_shape)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
70
103
|
past_tot_outer = @tiles.first.benchmarks[:outer] + @tiles.last.benchmarks[:outer]
|
|
71
104
|
past_tot_inner = @tiles.first.benchmarks[:inner] + @tiles.last.benchmarks[:inner]
|
|
72
105
|
|
|
@@ -81,6 +114,22 @@ module Contrek
|
|
|
81
114
|
tile.assign_shapes!(new_shapes)
|
|
82
115
|
tile
|
|
83
116
|
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def assign_ancestry(shape, inner_polylines)
|
|
121
|
+
inner_polylines.each do |inner_polyline|
|
|
122
|
+
if shape.outer_polyline.vert_bounds_intersect?(inner_polyline.vertical_bounds)
|
|
123
|
+
if shape.outer_polyline.within?(inner_polyline.raw)
|
|
124
|
+
shape.set_parent_shape(inner_polyline.shape)
|
|
125
|
+
shape.parent_inner_polyline = inner_polyline
|
|
126
|
+
shape.children_shapes.each do |children_shape|
|
|
127
|
+
children_shape.reassociation_skip = true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
84
133
|
end
|
|
85
134
|
end
|
|
86
135
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module Contrek
|
|
2
2
|
module Concurrent
|
|
3
3
|
class Cursor
|
|
4
|
-
attr_reader :orphan_inners
|
|
4
|
+
attr_reader :orphan_inners, :shapes_sequence
|
|
5
|
+
|
|
5
6
|
def initialize(cluster:, shape:)
|
|
6
7
|
@shapes_sequence = Set.new([shape])
|
|
7
8
|
@cluster = cluster
|
|
@@ -90,7 +91,9 @@ module Contrek
|
|
|
90
91
|
end
|
|
91
92
|
combine!(inject_sequences_right, inject_sequences_left).each do |sewn_sequence|
|
|
92
93
|
sewn_sequence.uniq!
|
|
93
|
-
|
|
94
|
+
if sewn_sequence.size > 1 && sewn_sequence.map { |c| c[:x] }.uniq.size > 1 # only areas
|
|
95
|
+
@orphan_inners << InnerPolyline.new(shape: shape, raw_coordinates: sewn_sequence, recombined: true)
|
|
96
|
+
end
|
|
94
97
|
end
|
|
95
98
|
missing_outer_polyline.clear!
|
|
96
99
|
polyline.mixed_tile_origin = true
|
|
@@ -147,7 +150,7 @@ module Contrek
|
|
|
147
150
|
end
|
|
148
151
|
|
|
149
152
|
def collect_inner_sequences(outer_seq)
|
|
150
|
-
|
|
153
|
+
return_inner_polylines = []
|
|
151
154
|
@shapes_sequence.each do |shape|
|
|
152
155
|
polyline = shape.outer_polyline
|
|
153
156
|
polyline.parts.each do |part|
|
|
@@ -165,16 +168,17 @@ module Contrek
|
|
|
165
168
|
!(polyline.tile.tg_border?(position.payload) && position.end_point.queues.include?(outer_seq))
|
|
166
169
|
end
|
|
167
170
|
end
|
|
168
|
-
|
|
171
|
+
if retme_sequence.is_not_vertical
|
|
172
|
+
return_inner_polylines << InnerPolyline.new(sequence: retme_sequence)
|
|
173
|
+
end
|
|
169
174
|
end
|
|
170
175
|
end
|
|
171
176
|
end
|
|
172
|
-
|
|
177
|
+
return_inner_polylines
|
|
173
178
|
end
|
|
174
179
|
|
|
175
180
|
def traverse_inner(act_part, all_parts, bounds)
|
|
176
181
|
return if act_part == all_parts.first
|
|
177
|
-
|
|
178
182
|
if act_part.size > 0
|
|
179
183
|
min_y, max_y = act_part.to_a.minmax_by { |p| p[:y] }
|
|
180
184
|
bounds[:min] = [bounds[:min], min_y[:y]].min
|
|
@@ -3,7 +3,7 @@ module Contrek
|
|
|
3
3
|
class Finder
|
|
4
4
|
prepend Poolable
|
|
5
5
|
|
|
6
|
-
attr_reader :maximum_width
|
|
6
|
+
attr_reader :maximum_width, :options
|
|
7
7
|
|
|
8
8
|
# Supported options
|
|
9
9
|
# - number_of_threads: number of threads that can be used by the process. If set to 0, it works in
|
|
@@ -47,7 +47,12 @@ module Contrek
|
|
|
47
47
|
finder = ClippedPolygonFinder.new(
|
|
48
48
|
bitmap: bitmap,
|
|
49
49
|
matcher: matcher,
|
|
50
|
-
options: {
|
|
50
|
+
options: {
|
|
51
|
+
versus: current_versus,
|
|
52
|
+
bounds: true,
|
|
53
|
+
treemap: @options[:treemap],
|
|
54
|
+
connectivity: @options[:connectivity]
|
|
55
|
+
}.compact,
|
|
51
56
|
start_x: payload[:tile_start_x],
|
|
52
57
|
end_x: payload[:tile_end_x]
|
|
53
58
|
)
|
|
@@ -88,6 +93,7 @@ module Contrek
|
|
|
88
93
|
width: @maximum_width,
|
|
89
94
|
height: @height
|
|
90
95
|
}
|
|
96
|
+
metadata[:treemap] = @whole_tile.compute_treemap if options[:treemap]
|
|
91
97
|
Contrek::Finder::Result.new(raw_polygons, metadata)
|
|
92
98
|
end
|
|
93
99
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Contrek
|
|
2
|
+
module Concurrent
|
|
3
|
+
class InnerPolyline
|
|
4
|
+
attr_reader :sequence, :recombined
|
|
5
|
+
|
|
6
|
+
def initialize(shape: nil, raw_coordinates: [], sequence: nil, recombined: false)
|
|
7
|
+
@raw = raw_coordinates if raw_coordinates
|
|
8
|
+
@sequence = sequence if sequence
|
|
9
|
+
@recombined = recombined
|
|
10
|
+
@shape = shape
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def raw
|
|
14
|
+
@sequence ? @sequence.to_a : @raw
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def vertical_bounds
|
|
18
|
+
if @sequence
|
|
19
|
+
@sequence.vertical_bounds
|
|
20
|
+
else
|
|
21
|
+
raw_vertical_bounds
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def shape
|
|
26
|
+
if @sequence
|
|
27
|
+
@sequence.shape
|
|
28
|
+
else
|
|
29
|
+
@shape
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def raw_vertical_bounds
|
|
36
|
+
@vertical_bounds ||= begin
|
|
37
|
+
min_y = Float::INFINITY
|
|
38
|
+
max_y = 0
|
|
39
|
+
@raw.each do |pos|
|
|
40
|
+
y = pos[:y]
|
|
41
|
+
min_y = y if y < min_y
|
|
42
|
+
max_y = y if y > max_y
|
|
43
|
+
end
|
|
44
|
+
{min: min_y, max: max_y}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -119,6 +119,33 @@ module Contrek
|
|
|
119
119
|
!(@max_y < other.min_y || other.max_y < @min_y)
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
def vert_bounds_intersect?(vertical_bounds)
|
|
123
|
+
!(@max_y < vertical_bounds[:min] || vertical_bounds[:max] < @min_y)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# verifies if enclosed in the given sequence (raycasting technique)
|
|
127
|
+
def within?(raw_coords)
|
|
128
|
+
is_within = false
|
|
129
|
+
self_y = raw.first[:y]
|
|
130
|
+
self_x = raw.first[:x]
|
|
131
|
+
last_node = raw_coords.last
|
|
132
|
+
return false if last_node.nil?
|
|
133
|
+
lx = last_node[:x]
|
|
134
|
+
ly = last_node[:y]
|
|
135
|
+
raw_coords.each do |pos|
|
|
136
|
+
cx = pos[:x]
|
|
137
|
+
cy = pos[:y]
|
|
138
|
+
if (cy > self_y) != (ly > self_y)
|
|
139
|
+
intersect_x = lx + (self_y - ly).to_f * (cx - lx) / (cy - ly)
|
|
140
|
+
if self_x < intersect_x
|
|
141
|
+
is_within = !is_within
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
lx, ly = cx, cy
|
|
145
|
+
end
|
|
146
|
+
is_within
|
|
147
|
+
end
|
|
148
|
+
|
|
122
149
|
def get_bounds
|
|
123
150
|
{min_x: @min_x,
|
|
124
151
|
max_x: @max_x,
|
|
@@ -126,6 +153,25 @@ module Contrek
|
|
|
126
153
|
max_y: @max_y}
|
|
127
154
|
end
|
|
128
155
|
|
|
156
|
+
def self.is_within?(test_seq, container_seq)
|
|
157
|
+
target = test_seq.first
|
|
158
|
+
return false unless target
|
|
159
|
+
tx, ty = target[:x], target[:y]
|
|
160
|
+
inside = false
|
|
161
|
+
j = container_seq.length - 1
|
|
162
|
+
container_seq.each_with_index do |p_i, i|
|
|
163
|
+
p_j = container_seq[j]
|
|
164
|
+
if (p_i[:y] > ty) != (p_j[:y] > ty)
|
|
165
|
+
intersect_x = (p_j[:x] - p_i[:x]) * (ty - p_i[:y]).to_f / (p_j[:y] - p_i[:y]) + p_i[:x]
|
|
166
|
+
if tx < intersect_x
|
|
167
|
+
inside = !inside
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
j = i
|
|
171
|
+
end
|
|
172
|
+
inside
|
|
173
|
+
end
|
|
174
|
+
|
|
129
175
|
private
|
|
130
176
|
|
|
131
177
|
def find_boundary
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
module Contrek
|
|
2
2
|
module Concurrent
|
|
3
3
|
class Sequence
|
|
4
|
+
attr_accessor :vertical_bounds, :shape
|
|
4
5
|
prepend Queueable
|
|
5
6
|
|
|
7
|
+
def initialize
|
|
8
|
+
@vertical_bounds = nil
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
def is_not_vertical
|
|
7
12
|
return false if size < 2
|
|
8
13
|
x0 = head.payload[:x]
|
|
@@ -13,6 +18,22 @@ module Contrek
|
|
|
13
18
|
end
|
|
14
19
|
false
|
|
15
20
|
end
|
|
21
|
+
|
|
22
|
+
def compute_vertical_bounds!
|
|
23
|
+
return if size == 0
|
|
24
|
+
min_y = Float::INFINITY
|
|
25
|
+
max_y = 0
|
|
26
|
+
get_vector_cache.each do |pos|
|
|
27
|
+
y = pos[:y]
|
|
28
|
+
min_y = y if y < min_y
|
|
29
|
+
max_y = y if y > max_y
|
|
30
|
+
end
|
|
31
|
+
@vertical_bounds = {min: min_y, max: max_y}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def get_vector_cache
|
|
35
|
+
@vector_cache ||= to_a
|
|
36
|
+
end
|
|
16
37
|
end
|
|
17
38
|
end
|
|
18
39
|
end
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
module Contrek
|
|
2
2
|
module Concurrent
|
|
3
3
|
class Shape
|
|
4
|
-
attr_accessor :outer_polyline, :inner_polylines
|
|
4
|
+
attr_accessor :outer_polyline, :inner_polylines, :merged_to_shape, :parent_inner_polyline,
|
|
5
|
+
:reassociation_skip
|
|
6
|
+
attr_reader :parent_shape, :children_shapes
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@parent_shape = nil
|
|
10
|
+
@merged_to_shape = nil
|
|
11
|
+
@parent_inner_polyline = nil
|
|
12
|
+
@children_shapes = []
|
|
13
|
+
@reassociation_skip = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.init_by(set_outer_polyline, set_inner_polylines)
|
|
17
|
+
s = Shape.new
|
|
18
|
+
s.outer_polyline = set_outer_polyline
|
|
19
|
+
s.inner_polylines = set_inner_polylines
|
|
20
|
+
s
|
|
21
|
+
end
|
|
5
22
|
|
|
6
23
|
def clear_inner!
|
|
7
|
-
@inner_polylines
|
|
24
|
+
@inner_polylines.clear
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set_parent_shape(shape)
|
|
28
|
+
@parent_shape&.children_shapes&.delete(self)
|
|
29
|
+
@parent_shape = shape
|
|
30
|
+
shape.children_shapes << self if shape
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def info
|
|
34
|
+
"Shape (outer_polyline = #{outer_polyline.named}, children count = #{@children_shapes.size}"
|
|
8
35
|
end
|
|
9
36
|
end
|
|
10
37
|
end
|