contrek 1.0.9 → 1.1.0
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 +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +8 -8
- data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +48 -59
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +7 -7
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +5 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +24 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +5 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +6 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.h +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +0 -15
- data/lib/contrek/bitmaps/custom_bitmap.rb +2 -2
- data/lib/contrek/finder/concurrent/cursor.rb +40 -33
- data/lib/contrek/finder/concurrent/end_point.rb +3 -2
- data/lib/contrek/finder/concurrent/part.rb +16 -0
- data/lib/contrek/finder/concurrent/polyline.rb +2 -1
- data/lib/contrek/finder/concurrent/position.rb +12 -3
- data/lib/contrek/finder/concurrent/queueable.rb +0 -22
- data/lib/contrek/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a5b664225e4cd276127b4d2861bb74382dc178cbdfd23ab36afd169c46eaca0
|
|
4
|
+
data.tar.gz: '06601846595b647920216a6035fc00885972416222114a8f306b0f3219cafaa5'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: baf09342bd36fb5c0f73d6fa9a679fd7eae42a26ba419832964da24cbd27ff8a58d5725d82a87cf0458351d2c8acc3ed6d18690f31d91b69f77c7de91c891ea4
|
|
7
|
+
data.tar.gz: 19b61c4937cf4c944de9312114e8be41dfd2b524d1cb9ff34449e567d007870b318d71b0c4f90dda5257ae9e66c949ac58b0e209ddc5c18887f23fb7df65af68
|
data/CHANGELOG.md
CHANGED
|
@@ -34,3 +34,9 @@ All notable changes to this project will be documented in this file.
|
|
|
34
34
|
### Added
|
|
35
35
|
- **CMake Integration**: The library is now fully modularized via CMake, enabling seamless integration as a dependency in external C++ projects.
|
|
36
36
|
- Multithreading Algorithm Optimization.
|
|
37
|
+
|
|
38
|
+
## [1.1.0] - 2026-02-08
|
|
39
|
+
### Changes
|
|
40
|
+
- Resolved connectivity issues between internal zones and the outer boundary.
|
|
41
|
+
- Refined the internal parts stitching algorithm (sew method) for better performance.
|
|
42
|
+
- Miscellaneous optimizations and improvements.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -99,25 +99,25 @@ Regarding multithreading:
|
|
|
99
99
|
|
|
100
100
|
- The treemap option is currently ignored (multithreaded treemap support will be introduced in upcoming revisions).
|
|
101
101
|
|
|
102
|
-
By not declaring native option CPP Multithreading optimized code is used. In the above example a [105 MP image](spec/files/images/sample_10240x10240.png) is examined by
|
|
102
|
+
By not declaring native option CPP Multithreading optimized code is used. In the above example a [105 MP image](spec/files/images/sample_10240x10240.png) is examined by 4 threads working on 4 tiles (total compute time about 1.53 secs with image load).
|
|
103
103
|
|
|
104
104
|
```ruby
|
|
105
105
|
result = Contrek.contour!(
|
|
106
106
|
png_file_path: "./spec/files/images/sample_10240x10240.png",
|
|
107
107
|
options: {
|
|
108
|
-
number_of_threads:
|
|
108
|
+
number_of_threads: 4,
|
|
109
109
|
class: "value_not_matcher",
|
|
110
110
|
color: {r: 255, g: 255, b: 255, a: 255},
|
|
111
|
-
finder: {number_of_tiles:
|
|
111
|
+
finder: {number_of_tiles: 4, compress: {uniq: true}}
|
|
112
112
|
}
|
|
113
113
|
)
|
|
114
114
|
puts result.metadata[:benchmarks].inspect
|
|
115
115
|
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
{ compress: 13.0815,
|
|
117
|
+
init: 453.245,
|
|
118
|
+
inner: 27.0072,
|
|
119
|
+
outer: 66.9162,
|
|
120
|
+
total: 466.326
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
```
|
|
@@ -24,39 +24,23 @@ Cursor::~Cursor() {
|
|
|
24
24
|
|
|
25
25
|
Sequence* Cursor::join_outers()
|
|
26
26
|
{ Polyline* outer_polyline = shape->outer_polyline;
|
|
27
|
-
this->
|
|
27
|
+
this->shapes_sequence.push_back(this->shape);
|
|
28
|
+
this->shapes_sequence_lookup.insert(this->shape);
|
|
28
29
|
Sequence* outer_joined_polyline = new Sequence();
|
|
29
30
|
this->allocated_sequences.push_back(outer_joined_polyline);
|
|
30
|
-
int counter = 0;
|
|
31
31
|
std::vector<Part*> all_parts;
|
|
32
|
-
std::vector<Shape*> shapes;
|
|
33
|
-
shapes.push_back(this->shape);
|
|
34
32
|
this->traverse_outer(outer_polyline->parts().front(),
|
|
35
33
|
all_parts,
|
|
36
|
-
this->
|
|
37
|
-
|
|
38
|
-
outer_joined_polyline, counter);
|
|
34
|
+
this->shapes_sequence,
|
|
35
|
+
outer_joined_polyline);
|
|
39
36
|
|
|
40
37
|
if (*outer_joined_polyline->head->payload == *outer_joined_polyline->tail->payload &&
|
|
41
38
|
this->cluster.tiles().front()->left() &&
|
|
42
39
|
this->cluster.tiles().back()->right()) outer_joined_polyline->pop();
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
for (Polyline* x : polylines_sequence) {
|
|
48
|
-
if (seen.insert(x).second) {
|
|
49
|
-
result.push_back(x);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
polylines_sequence = result;
|
|
53
|
-
|
|
54
|
-
for (Polyline* polyline : polylines_sequence) {
|
|
55
|
-
polyline->turn_on(Polyline::TRACKED_OUTER);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
for (Shape *shape : shapes)
|
|
59
|
-
{ if (shape == outer_polyline->shape) {
|
|
41
|
+
for (Shape* shape : shapes_sequence) {
|
|
42
|
+
shape->outer_polyline->turn_on(Polyline::TRACKED_OUTER);
|
|
43
|
+
if (shape == outer_polyline->shape) {
|
|
60
44
|
continue;
|
|
61
45
|
}
|
|
62
46
|
orphan_inners_.insert(
|
|
@@ -70,22 +54,13 @@ Sequence* Cursor::join_outers()
|
|
|
70
54
|
|
|
71
55
|
void Cursor::traverse_outer(Part* act_part,
|
|
72
56
|
std::vector<Part*>& all_parts,
|
|
73
|
-
std::vector<
|
|
74
|
-
|
|
75
|
-
Sequence* outer_joined_polyline,
|
|
76
|
-
int& counter) {
|
|
57
|
+
std::vector<Shape*>& shapes_sequence,
|
|
58
|
+
Sequence* outer_joined_polyline) {
|
|
77
59
|
while (act_part != nullptr) {
|
|
78
|
-
counter += 1;
|
|
79
|
-
|
|
80
60
|
if (all_parts.empty() || all_parts.back() != act_part) {
|
|
81
61
|
all_parts.push_back(act_part);
|
|
82
62
|
}
|
|
83
63
|
|
|
84
|
-
Shape* target_shape = act_part->polyline()->shape;
|
|
85
|
-
if (std::find(shapes.begin(), shapes.end(), target_shape) == shapes.end()) {
|
|
86
|
-
shapes.push_back(target_shape);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
64
|
bool jumped_to_new_part = false;
|
|
90
65
|
if (act_part->is(Part::EXCLUSIVE)) {
|
|
91
66
|
if (act_part->size == 0) return;
|
|
@@ -123,7 +98,9 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
123
98
|
}
|
|
124
99
|
if (all_seam) break;
|
|
125
100
|
}
|
|
126
|
-
|
|
101
|
+
if (shapes_sequence_lookup.insert(part->polyline()->shape).second) {
|
|
102
|
+
shapes_sequence.push_back(part->polyline()->shape);
|
|
103
|
+
}
|
|
127
104
|
part->next_position(new_position);
|
|
128
105
|
act_part = part;
|
|
129
106
|
jumped_to_new_part = true;
|
|
@@ -152,33 +129,40 @@ std::vector<Sequence*> Cursor::join_inners(Sequence* outer_seq) {
|
|
|
152
129
|
{ if (shape->outer_polyline->is_on(Polyline::TRACKED_OUTER) ||
|
|
153
130
|
shape->outer_polyline->is_on(Polyline::TRACKED_INNER) ||
|
|
154
131
|
!shape->outer_polyline->boundary() ||
|
|
155
|
-
std::find(
|
|
132
|
+
std::find(shapes_sequence.begin(), shapes_sequence.end(), shape) != shapes_sequence.end()) {
|
|
156
133
|
continue;
|
|
157
134
|
}
|
|
158
135
|
missing_shapes.push_back(shape);
|
|
159
136
|
}
|
|
160
137
|
}
|
|
161
138
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
139
|
+
if (missing_shapes.size() > 0) {
|
|
140
|
+
std::vector<Shape*> to_delay_shapes;
|
|
141
|
+
to_delay_shapes = connect_missings(this->shapes_sequence, missing_shapes);
|
|
142
|
+
if (!to_delay_shapes.empty())
|
|
143
|
+
{ connect_missings(to_delay_shapes, missing_shapes);
|
|
144
|
+
while (!to_delay_shapes.empty()) {
|
|
145
|
+
to_delay_shapes = connect_missings(this->shapes_sequence, to_delay_shapes);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
166
148
|
}
|
|
167
149
|
|
|
168
150
|
retme = collect_inner_sequences(outer_seq);
|
|
169
|
-
for (
|
|
170
|
-
|
|
151
|
+
for (Shape* shape : shapes_sequence) {
|
|
152
|
+
shape->outer_polyline->turn_on(Polyline::TRACKED_INNER);
|
|
171
153
|
}
|
|
172
154
|
return(retme);
|
|
173
155
|
}
|
|
174
156
|
|
|
175
|
-
std::vector<Shape*> Cursor::connect_missings(std::vector<Shape*> missing_shapes) {
|
|
157
|
+
std::vector<Shape*> Cursor::connect_missings(std::vector<Shape*> shapes_sequence, std::vector<Shape*> missing_shapes) {
|
|
176
158
|
std::vector<Shape*> delay_shapes;
|
|
177
159
|
|
|
178
|
-
for (
|
|
179
|
-
{
|
|
160
|
+
for (Shape* shape : shapes_sequence)
|
|
161
|
+
{ Polyline* polyline = shape->outer_polyline;
|
|
162
|
+
for (Shape* missing_shape : missing_shapes)
|
|
180
163
|
{ Polyline* outer_polyline = missing_shape->outer_polyline;
|
|
181
|
-
if ( outer_polyline->
|
|
164
|
+
if ( (polyline->mixed_tile_origin == false && outer_polyline->tile == polyline->tile) ||
|
|
165
|
+
outer_polyline->is_on(Polyline::TRACKED_OUTER) ||
|
|
182
166
|
!polyline->vert_intersect(*outer_polyline)) continue;
|
|
183
167
|
std::vector<std::pair<int, int>> intersection = polyline->intersection(outer_polyline);
|
|
184
168
|
if (intersection.size() > 0)
|
|
@@ -218,6 +202,7 @@ std::vector<Shape*> Cursor::connect_missings(std::vector<Shape*> missing_shapes)
|
|
|
218
202
|
orphan_inners_.push_back(sewn_sequence);
|
|
219
203
|
}
|
|
220
204
|
}
|
|
205
|
+
polyline->mixed_tile_origin = true;
|
|
221
206
|
outer_polyline->clear();
|
|
222
207
|
outer_polyline->turn_on(Polyline::TRACKED_OUTER);
|
|
223
208
|
outer_polyline->turn_on(Polyline::TRACKED_INNER);
|
|
@@ -235,8 +220,9 @@ std::vector<Shape*> Cursor::connect_missings(std::vector<Shape*> missing_shapes)
|
|
|
235
220
|
std::vector<Sequence*> Cursor::collect_inner_sequences(Sequence* outer_seq) {
|
|
236
221
|
std::vector<Sequence*> return_sequences;
|
|
237
222
|
|
|
238
|
-
for (
|
|
239
|
-
{
|
|
223
|
+
for (Shape* shape : shapes_sequence)
|
|
224
|
+
{ Polyline* polyline = shape->outer_polyline;
|
|
225
|
+
for (Part* part : polyline->parts())
|
|
240
226
|
{ if (part->innerable())
|
|
241
227
|
{ std::vector<Part*> all_parts;
|
|
242
228
|
Bounds bounds{
|
|
@@ -291,15 +277,17 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
|
|
|
291
277
|
for (Part* dest_part : shape->outer_polyline->parts()) {
|
|
292
278
|
if (dest_part->trasmuted || dest_part->is(Part::EXCLUSIVE)) continue;
|
|
293
279
|
if (dest_part->intersect_part(act_part)) {
|
|
294
|
-
std::vector<
|
|
280
|
+
std::vector<EndPoint*> link_seq = duplicates_intersection(*dest_part, *act_part);
|
|
295
281
|
if (!link_seq.empty()) {
|
|
296
282
|
Part* ins_part = pool.acquire(Part::ADDED, act_part->polyline());
|
|
297
|
-
for (
|
|
298
|
-
this->cluster.positions_pool.emplace_back(
|
|
283
|
+
for (EndPoint* pos : link_seq) {
|
|
284
|
+
this->cluster.positions_pool.emplace_back(pos);
|
|
299
285
|
ins_part->add(&this->cluster.positions_pool.back());
|
|
300
286
|
}
|
|
301
287
|
all_parts.push_back(ins_part);
|
|
302
288
|
}
|
|
289
|
+
shape->outer_polyline->turn_on(Polyline::TRACKED_OUTER);
|
|
290
|
+
shape->outer_polyline->turn_on(Polyline::TRACKED_INNER);
|
|
303
291
|
act_part = dest_part->circular_next;
|
|
304
292
|
jumped = true;
|
|
305
293
|
break;
|
|
@@ -324,14 +312,13 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
|
|
|
324
312
|
}
|
|
325
313
|
|
|
326
314
|
template <typename T>
|
|
327
|
-
std::vector<T*> difference_ptr(
|
|
315
|
+
std::vector<T*> difference_ptr(std::vector<T*>& a, std::vector<T*>& b)
|
|
328
316
|
{ std::vector<T*> result;
|
|
329
317
|
for (T* item : a) {
|
|
330
|
-
if (!item) continue;
|
|
331
318
|
auto it = std::find_if(
|
|
332
319
|
b.begin(), b.end(),
|
|
333
320
|
[&](T* other) {
|
|
334
|
-
|
|
321
|
+
return other == item;
|
|
335
322
|
});
|
|
336
323
|
if (it == b.end()) {
|
|
337
324
|
result.push_back(item);
|
|
@@ -340,11 +327,13 @@ std::vector<T*> difference_ptr(const std::vector<T*>& a, const std::vector<T*>&
|
|
|
340
327
|
return result;
|
|
341
328
|
}
|
|
342
329
|
|
|
343
|
-
std::vector<
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
330
|
+
std::vector<EndPoint*> Cursor::duplicates_intersection(Part& part_a, Part& part_b) {
|
|
331
|
+
auto a1 = part_a.to_endpoints();
|
|
332
|
+
auto b1 = part_b.to_endpoints();
|
|
333
|
+
if (part_a.inverts) a1 = Part::remove_adjacent_pairs(a1);
|
|
334
|
+
if (part_b.inverts) b1 = Part::remove_adjacent_pairs(b1);
|
|
335
|
+
std::vector<EndPoint*> result = difference_ptr(a1, b1);
|
|
336
|
+
std::vector<EndPoint*> temp = difference_ptr(b1, a1);
|
|
348
337
|
result.insert(result.end(), temp.begin(), temp.end());
|
|
349
338
|
return result;
|
|
350
339
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#pragma once
|
|
10
10
|
#include <list>
|
|
11
11
|
#include <vector>
|
|
12
|
+
#include <unordered_set>
|
|
12
13
|
#include "Sequence.h"
|
|
13
14
|
#include "Cluster.h"
|
|
14
15
|
#include "Shape.h"
|
|
@@ -31,17 +32,16 @@ class Cursor {
|
|
|
31
32
|
Cluster& cluster;
|
|
32
33
|
Shape* shape;
|
|
33
34
|
std::vector<Sequence*> allocated_sequences;
|
|
34
|
-
std::vector<
|
|
35
|
+
std::vector<Shape*> shapes_sequence;
|
|
36
|
+
std::unordered_set<Shape*> shapes_sequence_lookup;
|
|
35
37
|
std::list<std::vector<Point*>> orphan_inners_;
|
|
36
38
|
void traverse_outer(Part* act_part,
|
|
37
39
|
std::vector<Part*>& all_parts,
|
|
38
|
-
std::vector<
|
|
39
|
-
|
|
40
|
-
Sequence* outer_joined_polyline,
|
|
41
|
-
int& counter);
|
|
40
|
+
std::vector<Shape*>& shapes_sequence,
|
|
41
|
+
Sequence* outer_joined_polyline);
|
|
42
42
|
std::vector<Sequence*> collect_inner_sequences(Sequence* outer_seq);
|
|
43
43
|
void traverse_inner(Part* act_part, std::vector<Part*> &all_parts, Bounds& bounds);
|
|
44
|
-
std::vector<
|
|
44
|
+
std::vector<EndPoint*> duplicates_intersection(Part& part_a, Part& part_b);
|
|
45
45
|
std::vector<std::vector<Point*>> combine(std::vector<std::vector<Point*>>& seqa, std::vector<std::vector<Point*>>& seqb);
|
|
46
|
-
std::vector<Shape*> connect_missings(std::vector<Shape*> missing_shapes);
|
|
46
|
+
std::vector<Shape*> connect_missings(std::vector<Shape*> shapes_sequence, std::vector<Shape*> missing_shapes);
|
|
47
47
|
};
|
|
@@ -14,9 +14,13 @@ class Part;
|
|
|
14
14
|
|
|
15
15
|
class EndPoint {
|
|
16
16
|
public:
|
|
17
|
-
EndPoint() {}
|
|
17
|
+
EndPoint() : point_(nullptr) {}
|
|
18
|
+
|
|
19
|
+
void set_point(Point* p) { point_ = p; }
|
|
20
|
+
Point* get_point() const { return point_; }
|
|
18
21
|
std::vector<Queueable<Point>*>& queues() { return queues_; }
|
|
19
22
|
bool queues_include(Queueable<Point>* q) const;
|
|
20
23
|
private:
|
|
24
|
+
Point* point_;
|
|
21
25
|
std::vector<Queueable<Point>*> queues_;
|
|
22
26
|
};
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
#include "Part.h"
|
|
10
10
|
#include <iostream>
|
|
11
|
+
#include <vector>
|
|
11
12
|
#include "Polyline.h"
|
|
12
13
|
#include "Tile.h"
|
|
13
14
|
#include "Cluster.h"
|
|
@@ -69,3 +70,26 @@ bool Part::intersect_part(Part* other_part)
|
|
|
69
70
|
});
|
|
70
71
|
return(intersect);
|
|
71
72
|
}
|
|
73
|
+
|
|
74
|
+
std::vector<EndPoint*> Part::to_endpoints() {
|
|
75
|
+
std::vector<EndPoint*> out;
|
|
76
|
+
QNode<Point>* current = head;
|
|
77
|
+
while (current) {
|
|
78
|
+
out.push_back((dynamic_cast<Position*>(current))->end_point());
|
|
79
|
+
current = current->next;
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
std::vector<EndPoint*> Part::remove_adjacent_pairs(const std::vector<EndPoint*>& input = {}) {
|
|
85
|
+
std::vector<EndPoint*> result;
|
|
86
|
+
result.reserve(input.size());
|
|
87
|
+
for (EndPoint* current : input) {
|
|
88
|
+
if (!result.empty() && result.back() == current) {
|
|
89
|
+
result.pop_back();
|
|
90
|
+
} else {
|
|
91
|
+
result.push_back(current);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
#include <cstdint>
|
|
11
11
|
#include <string>
|
|
12
12
|
#include <deque>
|
|
13
|
+
#include <vector>
|
|
13
14
|
#include "Queueable.h"
|
|
14
15
|
#include "Position.h"
|
|
16
|
+
#include "EndPoint.h"
|
|
15
17
|
#include "../Node.h"
|
|
16
18
|
|
|
17
19
|
class Polyline;
|
|
18
20
|
class Position;
|
|
21
|
+
class EndPoint;
|
|
19
22
|
class Part : public Queueable<Point> {
|
|
20
23
|
public:
|
|
21
24
|
enum Types : uint32_t {
|
|
@@ -42,6 +45,8 @@ class Part : public Queueable<Point> {
|
|
|
42
45
|
void touch();
|
|
43
46
|
bool intersect_part(Part* other_part);
|
|
44
47
|
void set_polyline(Polyline* polyline) { this->polyline_ = polyline; }
|
|
48
|
+
std::vector<EndPoint*> to_endpoints();
|
|
49
|
+
static std::vector<EndPoint*> remove_adjacent_pairs(const std::vector<EndPoint*>& input);
|
|
45
50
|
|
|
46
51
|
private:
|
|
47
52
|
bool touched_ = false;
|
|
@@ -16,12 +16,18 @@ Position::Position(Hub* hub, Point* point)
|
|
|
16
16
|
EndPoint* existing_ep = hub->get(key);
|
|
17
17
|
if (existing_ep == nullptr)
|
|
18
18
|
{ end_point_ = hub->put(key, hub->spawn_end_point());
|
|
19
|
+
end_point_->set_point(point);
|
|
19
20
|
} else {
|
|
20
21
|
end_point_ = existing_ep;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
Position::Position(EndPoint* end_point)
|
|
27
|
+
: QNode<Point>(end_point->get_point())
|
|
28
|
+
{ this->end_point_ = end_point;
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
void Position::before_rem(Queueable<Point>* q) {
|
|
26
32
|
if (this->end_point_ != nullptr) {
|
|
27
33
|
auto& queues = this->end_point_->queues();
|
|
@@ -17,7 +17,8 @@ class Hub;
|
|
|
17
17
|
class Position : public QNode<Point>{
|
|
18
18
|
public:
|
|
19
19
|
explicit Position(Hub* hub, Point* point);
|
|
20
|
-
|
|
20
|
+
explicit Position(EndPoint* end_point);
|
|
21
|
+
EndPoint* end_point() { return end_point_; }
|
|
21
22
|
void before_rem(Queueable<Point>* q) override;
|
|
22
23
|
void after_add(Queueable<Point>* q) override;
|
|
23
24
|
private:
|
|
@@ -206,19 +206,4 @@ class Queueable {
|
|
|
206
206
|
}
|
|
207
207
|
return false;
|
|
208
208
|
}
|
|
209
|
-
|
|
210
|
-
std::vector<T*> remove_adjacent_pairs(const std::vector<T*>& input = {}) const {
|
|
211
|
-
const std::vector<T*>& source = input.empty() ? to_vector() : input;
|
|
212
|
-
std::vector<T*> result;
|
|
213
|
-
result.reserve(source.size());
|
|
214
|
-
|
|
215
|
-
for (T* current : source) {
|
|
216
|
-
if (!result.empty() && *result.back() == *current) {
|
|
217
|
-
result.pop_back();
|
|
218
|
-
} else {
|
|
219
|
-
result.push_back(current);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return result;
|
|
223
|
-
}
|
|
224
209
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Contrek
|
|
2
2
|
module Bitmaps
|
|
3
3
|
class CustomBitmap < PngBitmap
|
|
4
|
-
def initialize(w:, h:)
|
|
5
|
-
@image = ChunkyPNG::Image.new(w, h,
|
|
4
|
+
def initialize(w:, h:, color: ChunkyPNG::Color::TRANSPARENT)
|
|
5
|
+
@image = ChunkyPNG::Image.new(w, h, color)
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -3,11 +3,10 @@ module Contrek
|
|
|
3
3
|
class Cursor
|
|
4
4
|
attr_reader :orphan_inners
|
|
5
5
|
def initialize(cluster:, shape:)
|
|
6
|
-
@
|
|
6
|
+
@shapes_sequence = Set.new([shape])
|
|
7
7
|
@cluster = cluster
|
|
8
8
|
@outer_polyline = shape.outer_polyline
|
|
9
9
|
@orphan_inners = []
|
|
10
|
-
@shapes = [shape]
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
def inspect
|
|
@@ -15,26 +14,22 @@ module Contrek
|
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
# Given the initial polyline, draw its outer boundary, possibly extending into
|
|
18
|
-
# adjacent polylines, and then connect them. At the end, @
|
|
17
|
+
# adjacent polylines, and then connect them. At the end, @shapes_sequence
|
|
19
18
|
# contains the merged polylines. Returns a new resulting polyline.
|
|
20
19
|
def join_outers!
|
|
21
20
|
seq_log = []
|
|
22
|
-
@polylines_sequence << @outer_polyline
|
|
23
21
|
|
|
24
22
|
outer_joined_polyline = Sequence.new
|
|
25
|
-
|
|
26
23
|
traverse_outer(@outer_polyline.parts.first,
|
|
27
24
|
seq_log,
|
|
28
|
-
@
|
|
29
|
-
@shapes,
|
|
25
|
+
@shapes_sequence,
|
|
30
26
|
outer_joined_polyline)
|
|
31
27
|
outer_joined_polyline.pop! if outer_joined_polyline.head.payload == outer_joined_polyline.tail.payload &&
|
|
32
28
|
@cluster.tiles.first.left? && @cluster.tiles.last.right?
|
|
33
29
|
|
|
34
|
-
@
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
(@shapes - [@outer_polyline.shape]).each do |shape|
|
|
30
|
+
@shapes_sequence.each do |shape|
|
|
31
|
+
shape.outer_polyline.turn_on(Polyline::TRACKED_OUTER)
|
|
32
|
+
next if shape == @outer_polyline.shape
|
|
38
33
|
@orphan_inners += shape.inner_polylines
|
|
39
34
|
shape.clear_inner!
|
|
40
35
|
end
|
|
@@ -50,32 +45,41 @@ module Contrek
|
|
|
50
45
|
next if shape.outer_polyline.on?(Polyline::TRACKED_OUTER) ||
|
|
51
46
|
shape.outer_polyline.on?(Polyline::TRACKED_INNER) ||
|
|
52
47
|
!shape.outer_polyline.boundary? ||
|
|
53
|
-
@
|
|
48
|
+
@shapes_sequence.include?(shape)
|
|
54
49
|
missing_shapes << shape
|
|
55
50
|
end
|
|
56
51
|
end
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
|
|
53
|
+
if missing_shapes.any?
|
|
54
|
+
to_delay_shapes = connect_missings(@shapes_sequence, missing_shapes)
|
|
55
|
+
if to_delay_shapes.any?
|
|
56
|
+
connect_missings(to_delay_shapes, missing_shapes)
|
|
57
|
+
while to_delay_shapes.any?
|
|
58
|
+
to_delay_shapes = connect_missings(@shapes_sequence, to_delay_shapes)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
retme = collect_inner_sequences(outer_seq)
|
|
63
64
|
|
|
64
|
-
@
|
|
65
|
-
|
|
65
|
+
@shapes_sequence.each do |shape|
|
|
66
|
+
shape.outer_polyline.turn_on(Polyline::TRACKED_INNER)
|
|
66
67
|
end
|
|
67
68
|
retme
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
private
|
|
71
72
|
|
|
72
|
-
def connect_missings(missing_shapes)
|
|
73
|
+
def connect_missings(shapes_sequence, missing_shapes)
|
|
73
74
|
delay_shapes = []
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
shapes_sequence.each do |shape|
|
|
77
|
+
polyline = shape.outer_polyline
|
|
76
78
|
missing_shapes.each do |missing_shape|
|
|
77
79
|
missing_outer_polyline = missing_shape.outer_polyline
|
|
78
|
-
next if missing_outer_polyline.
|
|
80
|
+
next if (polyline.mixed_tile_origin == false && missing_outer_polyline.tile == polyline.tile) || # accepts only other side ones
|
|
81
|
+
missing_outer_polyline.on?(Polyline::TRACKED_OUTER) ||
|
|
82
|
+
polyline == missing_outer_polyline ||
|
|
79
83
|
!polyline.vert_intersect?(missing_outer_polyline)
|
|
80
84
|
|
|
81
85
|
if (intersection = polyline.intersection(missing_outer_polyline)).any?
|
|
@@ -89,6 +93,7 @@ module Contrek
|
|
|
89
93
|
@orphan_inners << sewn_sequence if sewn_sequence.size > 1 && sewn_sequence.map { |c| c[:x] }.uniq.size > 1 # segmenti non sono ammessi, solo aree
|
|
90
94
|
end
|
|
91
95
|
missing_outer_polyline.clear!
|
|
96
|
+
polyline.mixed_tile_origin = true
|
|
92
97
|
missing_outer_polyline.turn_on(Polyline::TRACKED_OUTER)
|
|
93
98
|
missing_outer_polyline.turn_on(Polyline::TRACKED_INNER)
|
|
94
99
|
@orphan_inners += missing_shape.inner_polylines
|
|
@@ -100,9 +105,9 @@ module Contrek
|
|
|
100
105
|
end
|
|
101
106
|
|
|
102
107
|
# rubocop:disable Lint/NonLocalExitFromIterator
|
|
103
|
-
def traverse_outer(act_part, all_parts,
|
|
108
|
+
def traverse_outer(act_part, all_parts, shapes_sequence, outer_joined_polyline)
|
|
104
109
|
all_parts << act_part if all_parts.last != act_part
|
|
105
|
-
|
|
110
|
+
|
|
106
111
|
if act_part.is?(Part::EXCLUSIVE)
|
|
107
112
|
return if act_part.size == 0
|
|
108
113
|
while (position = act_part.next_position)
|
|
@@ -125,9 +130,9 @@ module Contrek
|
|
|
125
130
|
map = all_parts[-2..].map(&:type).uniq
|
|
126
131
|
break if map.size == 1 && map.first == Part::SEAM
|
|
127
132
|
end
|
|
128
|
-
|
|
133
|
+
shapes_sequence.add(part.polyline.shape)
|
|
129
134
|
part.next_position(new_position)
|
|
130
|
-
traverse_outer(part, all_parts,
|
|
135
|
+
traverse_outer(part, all_parts, shapes_sequence, outer_joined_polyline)
|
|
131
136
|
return
|
|
132
137
|
end
|
|
133
138
|
end
|
|
@@ -138,12 +143,13 @@ module Contrek
|
|
|
138
143
|
end
|
|
139
144
|
next_part = act_part.circular_next
|
|
140
145
|
next_part.rewind!
|
|
141
|
-
traverse_outer(act_part.circular_next, all_parts,
|
|
146
|
+
traverse_outer(act_part.circular_next, all_parts, shapes_sequence, outer_joined_polyline)
|
|
142
147
|
end
|
|
143
148
|
|
|
144
149
|
def collect_inner_sequences(outer_seq)
|
|
145
150
|
return_sequences = []
|
|
146
|
-
@
|
|
151
|
+
@shapes_sequence.each do |shape|
|
|
152
|
+
polyline = shape.outer_polyline
|
|
147
153
|
polyline.parts.each do |part|
|
|
148
154
|
if part.innerable?
|
|
149
155
|
all_parts = []
|
|
@@ -154,9 +160,9 @@ module Contrek
|
|
|
154
160
|
retme_sequence = Sequence.new
|
|
155
161
|
all_parts.each do |part|
|
|
156
162
|
part.touch!
|
|
157
|
-
retme_sequence.move_from(part) do |
|
|
158
|
-
next false if part.is?(Part::ADDED) && !(range_of_bounds ===
|
|
159
|
-
!(polyline.tile.tg_border?(
|
|
163
|
+
retme_sequence.move_from(part) do |position|
|
|
164
|
+
next false if part.is?(Part::ADDED) && !(range_of_bounds === position.payload[:y])
|
|
165
|
+
!(polyline.tile.tg_border?(position.payload) && position.end_point.queues.include?(outer_seq))
|
|
160
166
|
end
|
|
161
167
|
end
|
|
162
168
|
return_sequences << retme_sequence if retme_sequence.is_not_vertical
|
|
@@ -189,10 +195,12 @@ module Contrek
|
|
|
189
195
|
if link_seq.any?
|
|
190
196
|
ins_part = Part.new(Part::ADDED, act_part.polyline)
|
|
191
197
|
link_seq.each do |pos|
|
|
192
|
-
ins_part.add(Position.new(position:
|
|
198
|
+
ins_part.add(Position.new(position: nil, hub: nil, known_endpoint: pos))
|
|
193
199
|
end
|
|
194
200
|
all_parts << ins_part
|
|
195
201
|
end
|
|
202
|
+
shape.outer_polyline.turn_on(Polyline::TRACKED_INNER)
|
|
203
|
+
shape.outer_polyline.turn_on(Polyline::TRACKED_OUTER)
|
|
196
204
|
traverse_inner(dest_part.circular_next, all_parts, bounds)
|
|
197
205
|
return
|
|
198
206
|
end
|
|
@@ -213,9 +221,8 @@ module Contrek
|
|
|
213
221
|
# a connection between parts inserted afterwards.
|
|
214
222
|
# TODO evaluate the adoption of remove_adjacent_pairs!
|
|
215
223
|
def duplicates_intersection(part_a, part_b)
|
|
216
|
-
a1 = part_a.inverts ? part_a.
|
|
217
|
-
b1 = part_b.inverts ? part_b.
|
|
218
|
-
|
|
224
|
+
a1 = part_a.inverts ? Part.remove_adjacent_pairs(part_a.to_endpoints) : part_a.to_endpoints
|
|
225
|
+
b1 = part_b.inverts ? Part.remove_adjacent_pairs(part_b.to_endpoints) : part_b.to_endpoints
|
|
219
226
|
(a1 - b1) + (b1 - a1)
|
|
220
227
|
end
|
|
221
228
|
|
|
@@ -73,6 +73,22 @@ module Contrek
|
|
|
73
73
|
end
|
|
74
74
|
false
|
|
75
75
|
end
|
|
76
|
+
|
|
77
|
+
def to_endpoints
|
|
78
|
+
map(&:end_point)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.remove_adjacent_pairs(array = nil)
|
|
82
|
+
n = array.size
|
|
83
|
+
(0...(n - 1)).each do |i|
|
|
84
|
+
if array[i] == array[i + 1]
|
|
85
|
+
# Remove the pair and call recursively
|
|
86
|
+
new_array = array[0...i] + array[(i + 2)..]
|
|
87
|
+
return remove_adjacent_pairs(new_array)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
array
|
|
91
|
+
end
|
|
76
92
|
end
|
|
77
93
|
end
|
|
78
94
|
end
|
|
@@ -7,7 +7,7 @@ module Contrek
|
|
|
7
7
|
TRACKED_INNER = 1 << 1
|
|
8
8
|
|
|
9
9
|
attr_reader :raw, :name, :min_y, :max_y, :next_tile_eligible_shapes
|
|
10
|
-
attr_accessor :shape, :tile
|
|
10
|
+
attr_accessor :shape, :tile, :mixed_tile_origin
|
|
11
11
|
|
|
12
12
|
def initialize(tile:, polygon:, shape: nil, bounds: nil)
|
|
13
13
|
@tile = tile
|
|
@@ -15,6 +15,7 @@ module Contrek
|
|
|
15
15
|
@raw = polygon
|
|
16
16
|
@shape = shape
|
|
17
17
|
@flags = 0
|
|
18
|
+
@mixed_tile_origin = false # becomes true when is sewn with polyline coming from other side tile
|
|
18
19
|
|
|
19
20
|
if bounds.nil?
|
|
20
21
|
find_boundary
|
|
@@ -5,9 +5,14 @@ module Contrek
|
|
|
5
5
|
|
|
6
6
|
attr_reader :end_point
|
|
7
7
|
|
|
8
|
-
def initialize(hub:, position:)
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
def initialize(hub:, position:, known_endpoint: nil)
|
|
9
|
+
if !known_endpoint.nil?
|
|
10
|
+
@end_point = known_endpoint
|
|
11
|
+
@position = @end_point.position
|
|
12
|
+
else
|
|
13
|
+
@end_point = hub.payloads[position[:y]] ||= EndPoint.new(position) if hub
|
|
14
|
+
@position = position
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
def payload
|
|
@@ -21,6 +26,10 @@ module Contrek
|
|
|
21
26
|
def before_rem(old_queue)
|
|
22
27
|
@end_point&.queues&.delete(old_queue)
|
|
23
28
|
end
|
|
29
|
+
|
|
30
|
+
def inspect
|
|
31
|
+
"#{self.class} (#{payload})"
|
|
32
|
+
end
|
|
24
33
|
end
|
|
25
34
|
end
|
|
26
35
|
end
|
|
@@ -153,19 +153,6 @@ module Contrek
|
|
|
153
153
|
rem(@tail)
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
-
def remove_adjacent_pairs(array = nil)
|
|
157
|
-
array = to_a if array.nil?
|
|
158
|
-
n = array.size
|
|
159
|
-
(0...(n - 1)).each do |i|
|
|
160
|
-
if array[i] == array[i + 1]
|
|
161
|
-
# Remove the pair and call recursively
|
|
162
|
-
new_array = array[0...i] + array[(i + 2)..]
|
|
163
|
-
return remove_adjacent_pairs(new_array)
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
array
|
|
167
|
-
end
|
|
168
|
-
|
|
169
156
|
def remove_adjacent_pairs!
|
|
170
157
|
unless @tail.nil?
|
|
171
158
|
pointer = @tail
|
|
@@ -182,15 +169,6 @@ module Contrek
|
|
|
182
169
|
end
|
|
183
170
|
end
|
|
184
171
|
end
|
|
185
|
-
|
|
186
|
-
# def index(node)
|
|
187
|
-
# index = 0
|
|
188
|
-
# each do |loop_node|
|
|
189
|
-
# return index if node == loop_node
|
|
190
|
-
# index += 1
|
|
191
|
-
# end
|
|
192
|
-
# -1
|
|
193
|
-
# end
|
|
194
172
|
end
|
|
195
173
|
end
|
|
196
174
|
end
|
data/lib/contrek/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: contrek
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emanuele Cesaroni
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-02-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|