contrek 1.1.2 → 1.1.4
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/.gitignore +1 -0
- data/CHANGELOG.md +11 -1
- data/Gemfile.lock +1 -1
- data/LICENSE-MIT.md +9 -0
- data/README.md +58 -17
- data/contrek.gemspec +0 -1
- data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +14 -2
- data/ext/cpp_polygon_finder/PolygonFinder/LICENSE_AGPL.txt +661 -0
- data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +9 -7
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +9 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +4 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +13 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/List.cpp +74 -82
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/List.h +12 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Lists.cpp +0 -10
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Lists.h +0 -7
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +64 -46
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +14 -23
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +39 -44
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +5 -7
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +6 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +4 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +12 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +3 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +35 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +5 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +11 -11
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +16 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -5
- data/ext/cpp_polygon_finder/extconf.rb +12 -2
- data/lib/contrek/bitmaps/painting.rb +1 -0
- data/lib/contrek/finder/concurrent/cursor.rb +7 -5
- data/lib/contrek/finder/concurrent/finder.rb +2 -1
- data/lib/contrek/finder/concurrent/part.rb +12 -3
- data/lib/contrek/finder/concurrent/partitionable.rb +7 -5
- data/lib/contrek/finder/node.rb +61 -32
- data/lib/contrek/finder/node_cluster.rb +30 -27
- data/lib/contrek/finder/polygon_finder.rb +4 -4
- data/lib/contrek/version.rb +1 -1
- metadata +5 -4
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <string>
|
|
15
15
|
#include <vector>
|
|
16
16
|
#include <utility>
|
|
17
|
+
#include <deque>
|
|
17
18
|
#include "NodeCluster.h"
|
|
18
19
|
#include "Node.h"
|
|
19
20
|
#include "RectBounds.h"
|
|
@@ -30,15 +31,9 @@ NodeCluster::NodeCluster(int h, int w, pf_Options *options) {
|
|
|
30
31
|
this->nodes = 0;
|
|
31
32
|
|
|
32
33
|
this->vert_nodes.resize(h);
|
|
33
|
-
for (int i = 0; i < h; ++i) {
|
|
34
|
-
this->vert_nodes[i].reserve(w/2);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
34
|
this->root_nodes = this->lists.add_list();
|
|
38
35
|
this->inner_plot = this->lists.add_list();
|
|
39
36
|
this->inner_new = this->lists.add_list();
|
|
40
|
-
|
|
41
|
-
this->points.reserve(w * h);
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
NodeCluster::~NodeCluster() {
|
|
@@ -74,32 +69,33 @@ void NodeCluster::compress_coords(std::list<Polygon>& polygons, pf_Options optio
|
|
|
74
69
|
void NodeCluster::build_tangs_sequence() {
|
|
75
70
|
for (auto& line : vert_nodes) {
|
|
76
71
|
for (Node& node : line) {
|
|
77
|
-
node.precalc_tangs_sequences(this
|
|
72
|
+
node.precalc_tangs_sequences(*this);
|
|
78
73
|
}
|
|
79
74
|
}
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
Node* NodeCluster::add_node(int min_x, int max_x, int y, char name) {
|
|
83
|
-
vert_nodes[y].emplace_back(min_x, max_x, y, name);
|
|
77
|
+
Node* NodeCluster::add_node(int min_x, int max_x, int y, char name, int offset) {
|
|
78
|
+
vert_nodes[y].emplace_back(min_x, max_x, y, this, name);
|
|
84
79
|
|
|
85
80
|
Node& node = vert_nodes[y].back();
|
|
86
|
-
node.data_pointer = this->lists.get_data_pointer();
|
|
87
81
|
node.abs_x_index = vert_nodes[y].size() - 1;
|
|
88
82
|
this->nodes++;
|
|
89
83
|
|
|
90
84
|
root_nodes->push_back(&node);
|
|
91
85
|
|
|
92
86
|
if (y > 0) {
|
|
93
|
-
std::
|
|
87
|
+
std::deque<Node>& up_nodes = vert_nodes[y - 1];
|
|
94
88
|
if (!up_nodes.empty()) {
|
|
95
89
|
auto it = std::lower_bound(up_nodes.begin(), up_nodes.end(), node.min_x,
|
|
96
|
-
[](const Node& a, int val) {
|
|
97
|
-
|
|
90
|
+
[&](const Node& a, int val) {
|
|
91
|
+
return ((a.max_x + offset) < val);
|
|
98
92
|
});
|
|
93
|
+
|
|
99
94
|
while (it != up_nodes.end()) {
|
|
100
|
-
if (it->min_x > node.max_x) break;
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
if ((it->min_x - offset) > node.max_x) break;
|
|
96
|
+
int current_index = std::distance(up_nodes.begin(), it);
|
|
97
|
+
node.add_intersection(*it, current_index);
|
|
98
|
+
it->add_intersection(node, node.abs_x_index);
|
|
103
99
|
++it;
|
|
104
100
|
}
|
|
105
101
|
}
|
|
@@ -119,8 +115,11 @@ void NodeCluster::plot(int versus) {
|
|
|
119
115
|
this->plot_sequence.push_back(root_node);
|
|
120
116
|
Polygon poly;
|
|
121
117
|
|
|
122
|
-
if ((root_node)->tangs_sequence.size() > 0) // front() on empty list is undefined
|
|
123
|
-
{ versus == Node::A ?
|
|
118
|
+
if ((root_node)->tangs_sequence.size() > 0) // front() or back() on empty list is undefined
|
|
119
|
+
{ versus == Node::A ?
|
|
120
|
+
next_node = root_node->get_tangent_node_by_virtual_index(root_node->tangs_sequence.back()) :
|
|
121
|
+
next_node = root_node->get_tangent_node_by_virtual_index(root_node->tangs_sequence.front());
|
|
122
|
+
|
|
124
123
|
if (next_node != nullptr)
|
|
125
124
|
{ Point* p = next_node->coords_entering_to(root_node, versus_inverter[versus], Node::OUTER);
|
|
126
125
|
poly.outer.push_back(p);
|
|
@@ -159,13 +158,20 @@ void NodeCluster::plot(int versus) {
|
|
|
159
158
|
|
|
160
159
|
first->inner_index = index_inner;
|
|
161
160
|
|
|
162
|
-
Node
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
Node* next_node = nullptr;
|
|
162
|
+
|
|
163
|
+
if (first->get_trackmax()) {
|
|
164
|
+
if (inner_v == Node::A) {
|
|
165
|
+
if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_start];
|
|
166
|
+
} else {
|
|
167
|
+
if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_start];
|
|
168
|
+
}
|
|
166
169
|
} else {
|
|
167
|
-
if (inner_v == Node::A)
|
|
168
|
-
|
|
170
|
+
if (inner_v == Node::A) {
|
|
171
|
+
if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_end];
|
|
172
|
+
} else {
|
|
173
|
+
if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_end];
|
|
174
|
+
}
|
|
169
175
|
}
|
|
170
176
|
|
|
171
177
|
if (next_node != nullptr)
|
|
@@ -251,7 +257,11 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
|
|
|
251
257
|
|
|
252
258
|
bool plot = true;
|
|
253
259
|
if (next_node->y == last_node->y) {
|
|
254
|
-
Node *n
|
|
260
|
+
Node *n;
|
|
261
|
+
int virtual_index = (versus == Node::A ?
|
|
262
|
+
current_node->tangs_sequence.front() :
|
|
263
|
+
current_node->tangs_sequence.back());
|
|
264
|
+
n = current_node->get_tangent_node_by_virtual_index(virtual_index);
|
|
255
265
|
plot = (n == next_node);
|
|
256
266
|
}
|
|
257
267
|
if (plot) {
|
|
@@ -286,7 +296,10 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
|
|
|
286
296
|
|
|
287
297
|
bool plot = true;
|
|
288
298
|
if (next_node->y == last_node->y) {
|
|
289
|
-
|
|
299
|
+
int virtual_index = (versus == Node::A ?
|
|
300
|
+
current_node->tangs_sequence.back() :
|
|
301
|
+
current_node->tangs_sequence.front());
|
|
302
|
+
Node *n = current_node->get_tangent_node_by_virtual_index(virtual_index);
|
|
290
303
|
plot = (n == next_node);
|
|
291
304
|
}
|
|
292
305
|
if (plot) {
|
|
@@ -309,21 +322,3 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
|
|
|
309
322
|
current_node = next_node;
|
|
310
323
|
}
|
|
311
324
|
}
|
|
312
|
-
|
|
313
|
-
void NodeCluster::list_track(Node *node, std::list<Node*> *list) {
|
|
314
|
-
std::list<Node*>::iterator i = std::find(list->begin(), list->end(), node);
|
|
315
|
-
if (i == list->end()) list->push_back(node);
|
|
316
|
-
else list_delete(node, list);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
void NodeCluster::list_delete(Node *node, std::list<Node*> *list) {
|
|
320
|
-
std::list<Node*>::iterator i;
|
|
321
|
-
while ((i = std::find(list->begin(), list->end(), node)) != list->end())
|
|
322
|
-
{ list->erase(i);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
bool NodeCluster::list_present(Node *node, std::list<Node*> *list) {
|
|
327
|
-
std::list<Node*>::iterator i = std::find(list->begin(), list->end(), node);
|
|
328
|
-
return(!(i == list->end()));
|
|
329
|
-
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#pragma once
|
|
11
11
|
#include <list>
|
|
12
12
|
#include <vector>
|
|
13
|
+
#include <deque>
|
|
13
14
|
#include <map>
|
|
14
15
|
#include <string>
|
|
15
16
|
#include <utility>
|
|
@@ -32,24 +33,21 @@ class NodeCluster {
|
|
|
32
33
|
int versus_inverter[2];
|
|
33
34
|
int count = 0;
|
|
34
35
|
int nodes;
|
|
35
|
-
|
|
36
|
+
int width;
|
|
36
37
|
|
|
37
38
|
public:
|
|
38
39
|
pf_Options *options;
|
|
39
40
|
std::vector<std::pair<int, int>> treemap; // [a,b] a = index of parent outer, b = index of inner of parent outer
|
|
40
41
|
std::pair<int, int> test_in_hole_a(Node* node);
|
|
41
42
|
std::pair<int, int> test_in_hole_o(Node* node);
|
|
42
|
-
std::vector<std::
|
|
43
|
-
void list_track(Node *node, std::list<Node*> *list);
|
|
44
|
-
void list_delete(Node *node, std::list<Node*> *list);
|
|
45
|
-
bool list_present(Node *node, std::list<Node*> *list);
|
|
43
|
+
std::vector<std::deque<Node>> vert_nodes;
|
|
46
44
|
void compress_coords(std::list<Polygon>& polygons, pf_Options options);
|
|
47
45
|
List *root_nodes;
|
|
48
|
-
int height
|
|
46
|
+
int height;
|
|
49
47
|
std::list<Polygon> polygons;
|
|
50
48
|
NodeCluster(int h, int w, pf_Options *options);
|
|
51
49
|
virtual ~NodeCluster();
|
|
52
|
-
Node* add_node(int min_x, int max_x, int y, char name);
|
|
50
|
+
Node* add_node(int min_x, int max_x, int y, char name, int offset);
|
|
53
51
|
void calc_root_nodes();
|
|
54
52
|
void build_tangs_sequence();
|
|
55
53
|
void plot(int versus);
|
|
@@ -37,6 +37,7 @@ PolygonFinder::PolygonFinder(Bitmap *bitmap,
|
|
|
37
37
|
end_x(end_x == -1 ? bitmap->w() : end_x)
|
|
38
38
|
{ this->rgb_matcher = dynamic_cast<RGBMatcher*>(matcher);
|
|
39
39
|
if (options != nullptr) FinderUtils::sanitize_options(this->options, options);
|
|
40
|
+
|
|
40
41
|
this->node_cluster = new NodeCluster(source_bitmap->h(), source_bitmap->w(), &this->options);
|
|
41
42
|
|
|
42
43
|
//= SCAN ==============//
|
|
@@ -82,17 +83,18 @@ PolygonFinder::~PolygonFinder() {
|
|
|
82
83
|
|
|
83
84
|
void PolygonFinder::scan() {
|
|
84
85
|
int bpp = this->source_bitmap->get_bytes_per_pixel();
|
|
86
|
+
int offset = options.connectivity_offset;
|
|
85
87
|
|
|
86
88
|
RGBMatcher* rgb_m = dynamic_cast<RGBMatcher*>(this->matcher);
|
|
87
89
|
|
|
88
90
|
if (bpp == 1) {
|
|
89
91
|
auto fetcher = [](const unsigned char* p) { return static_cast<unsigned int>(p[0]); };
|
|
90
|
-
if (rgb_m) run_loop(rgb_m, fetcher);
|
|
91
|
-
else run_loop(this->matcher, fetcher);
|
|
92
|
+
if (rgb_m) run_loop(rgb_m, fetcher, offset);
|
|
93
|
+
else run_loop(this->matcher, fetcher, offset);
|
|
92
94
|
} else {
|
|
93
95
|
auto fetcher = [](const unsigned char* p) { return *reinterpret_cast<const uint32_t*>(p); };
|
|
94
|
-
if (rgb_m) run_loop(rgb_m, fetcher);
|
|
95
|
-
else run_loop(this->matcher, fetcher);
|
|
96
|
+
if (rgb_m) run_loop(rgb_m, fetcher, offset);
|
|
97
|
+
else run_loop(this->matcher, fetcher, offset);
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -41,6 +41,7 @@ struct pf_Options {
|
|
|
41
41
|
bool compress_visvalingam = false;
|
|
42
42
|
bool named_sequences = false;
|
|
43
43
|
bool bounds = false;
|
|
44
|
+
int connectivity_offset = 0;
|
|
44
45
|
float compress_visvalingam_tolerance = 10.0;
|
|
45
46
|
int number_of_tiles = 1;
|
|
46
47
|
std::string get_alpha_versus() {
|
|
@@ -94,7 +95,7 @@ class PolygonFinder {
|
|
|
94
95
|
CpuTimer cpu_timer;
|
|
95
96
|
|
|
96
97
|
template <typename M, typename F>
|
|
97
|
-
void run_loop(M* specific_matcher, F&& fetch_color) {
|
|
98
|
+
void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
|
|
98
99
|
int img_h = this->source_bitmap->h();
|
|
99
100
|
int bpp = this->source_bitmap->get_bytes_per_pixel();
|
|
100
101
|
for (int y = 0; y < img_h; y++) {
|
|
@@ -114,11 +115,11 @@ class PolygonFinder {
|
|
|
114
115
|
matching = true;
|
|
115
116
|
}
|
|
116
117
|
if (x == this->end_x - 1) {
|
|
117
|
-
this->node_cluster->add_node(min_x, x, y, last_red_value);
|
|
118
|
+
this->node_cluster->add_node(min_x, x, y, last_red_value, offset);
|
|
118
119
|
matching = false;
|
|
119
120
|
}
|
|
120
121
|
} else if (matching) {
|
|
121
|
-
this->node_cluster->add_node(min_x, x - 1, y, last_red_value);
|
|
122
|
+
this->node_cluster->add_node(min_x, x - 1, y, last_red_value, offset);
|
|
122
123
|
matching = false;
|
|
123
124
|
}
|
|
124
125
|
}
|
|
@@ -58,7 +58,8 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
58
58
|
std::vector<Shape*>& shapes_sequence,
|
|
59
59
|
Sequence* outer_joined_polyline) {
|
|
60
60
|
while (act_part != nullptr) {
|
|
61
|
-
|
|
61
|
+
Part* last_part = (!all_parts.empty()) ? all_parts.back() : nullptr;
|
|
62
|
+
if (all_parts.empty() || last_part != act_part) {
|
|
62
63
|
all_parts.push_back(act_part);
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -75,6 +76,10 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
75
76
|
outer_joined_polyline->add(position);
|
|
76
77
|
}
|
|
77
78
|
} else {
|
|
79
|
+
if (act_part->dead_end &&
|
|
80
|
+
all_parts.size() > 1 &&
|
|
81
|
+
last_part->is(Part::SEAM) &&
|
|
82
|
+
last_part->polyline() == act_part->polyline()) return;
|
|
78
83
|
while (Position *new_position = static_cast<Position*>(act_part->iterator())) {
|
|
79
84
|
if (outer_joined_polyline->size > 1 &&
|
|
80
85
|
outer_joined_polyline->head->payload == new_position->payload &&
|
|
@@ -83,8 +88,9 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
83
88
|
}
|
|
84
89
|
this->cluster.positions_pool.emplace_back(this->cluster.hub(), new_position->payload);
|
|
85
90
|
outer_joined_polyline->add(&this->cluster.positions_pool.back());
|
|
91
|
+
|
|
86
92
|
for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
|
|
87
|
-
if (Part *part = shape->outer_polyline->find_first_part_by_position(new_position)) {
|
|
93
|
+
if (Part *part = shape->outer_polyline->find_first_part_by_position(new_position, act_part->versus())) {
|
|
88
94
|
const auto n = all_parts.size();
|
|
89
95
|
Part *last_last_part = n >= 2 ? all_parts[n - 2] : nullptr;
|
|
90
96
|
|
|
@@ -103,6 +109,7 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
103
109
|
shapes_sequence.push_back(part->polyline()->shape);
|
|
104
110
|
}
|
|
105
111
|
part->next_position(new_position);
|
|
112
|
+
part->dead_end = true;
|
|
106
113
|
act_part = part;
|
|
107
114
|
jumped_to_new_part = true;
|
|
108
115
|
break;
|
|
@@ -110,7 +117,6 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
if (jumped_to_new_part) break;
|
|
113
|
-
act_part->passes += 1;
|
|
114
120
|
act_part->next_position(nullptr);
|
|
115
121
|
}
|
|
116
122
|
}
|
|
@@ -236,7 +242,7 @@ std::vector<Sequence*> Cursor::collect_inner_sequences(Sequence* outer_seq) {
|
|
|
236
242
|
for (Part* part : all_parts)
|
|
237
243
|
{ part->touch();
|
|
238
244
|
retme_sequence->move_from(*part, [&](QNode<Point>* pos) -> bool {
|
|
239
|
-
Position *position =
|
|
245
|
+
Position *position = static_cast<Position*>(pos);
|
|
240
246
|
if (part->is(Part::ADDED) &&
|
|
241
247
|
!(position->payload->y >= bounds.min &&
|
|
242
248
|
position->payload->y <= bounds.max)) {
|
|
@@ -277,6 +283,8 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
|
|
|
277
283
|
for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
|
|
278
284
|
for (Part* dest_part : shape->outer_polyline->parts()) {
|
|
279
285
|
if (dest_part->trasmuted || dest_part->is(Part::EXCLUSIVE)) continue;
|
|
286
|
+
int dest_part_versus = dest_part->versus();
|
|
287
|
+
if (dest_part_versus != 0 && dest_part_versus == act_part->versus()) continue;
|
|
280
288
|
if (dest_part->intersect_part(act_part)) {
|
|
281
289
|
std::vector<EndPoint*> link_seq = duplicates_intersection(*dest_part, *act_part);
|
|
282
290
|
if (!link_seq.empty()) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#pragma once
|
|
11
11
|
#include <vector>
|
|
12
|
+
#include <unordered_set>
|
|
12
13
|
#include "Part.h"
|
|
13
14
|
|
|
14
15
|
class Part;
|
|
@@ -19,9 +20,9 @@ class EndPoint {
|
|
|
19
20
|
|
|
20
21
|
void set_point(Point* p) { point_ = p; }
|
|
21
22
|
Point* get_point() const { return point_; }
|
|
22
|
-
std::
|
|
23
|
+
std::unordered_set<Queueable<Point>*>& queues() { return queues_; }
|
|
23
24
|
bool queues_include(Queueable<Point>* q) const;
|
|
24
25
|
private:
|
|
25
26
|
Point* point_;
|
|
26
|
-
std::
|
|
27
|
+
std::unordered_set<Queueable<Point>*> queues_;
|
|
27
28
|
};
|
|
@@ -41,6 +41,7 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, std::vec
|
|
|
41
41
|
TilePayload p { tile_index, x, tile_end_x };
|
|
42
42
|
enqueue(p, [this](const TilePayload& payload) {
|
|
43
43
|
std::vector<std::string> base_arguments = {"--bounds", "--versus=" + this->options.get_alpha_versus()};
|
|
44
|
+
if (this->options.connectivity_offset == 1) base_arguments.push_back("--connectivity=8");
|
|
44
45
|
CpuTimer t;
|
|
45
46
|
t.start();
|
|
46
47
|
auto* finder = new ClippedPolygonFinder(
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
#include "Part.h"
|
|
11
11
|
#include <iostream>
|
|
12
12
|
#include <vector>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <sstream>
|
|
13
15
|
#include "Polyline.h"
|
|
14
16
|
#include "Tile.h"
|
|
15
17
|
#include "Cluster.h"
|
|
@@ -62,7 +64,7 @@ void Part::touch()
|
|
|
62
64
|
bool Part::intersect_part(Part* other_part)
|
|
63
65
|
{ bool intersect = false;
|
|
64
66
|
other_part->each([&](QNode<Point>* pos) -> bool {
|
|
65
|
-
Position *position =
|
|
67
|
+
Position *position = static_cast<Position*>(pos);
|
|
66
68
|
if (position->end_point()->queues_include(this))
|
|
67
69
|
{ intersect = true;
|
|
68
70
|
return(false);
|
|
@@ -76,7 +78,7 @@ std::vector<EndPoint*> Part::to_endpoints() {
|
|
|
76
78
|
std::vector<EndPoint*> out;
|
|
77
79
|
QNode<Point>* current = head;
|
|
78
80
|
while (current) {
|
|
79
|
-
out.push_back((
|
|
81
|
+
out.push_back((static_cast<Position*>(current))->end_point());
|
|
80
82
|
current = current->next;
|
|
81
83
|
}
|
|
82
84
|
return out;
|
|
@@ -94,3 +96,34 @@ std::vector<EndPoint*> Part::remove_adjacent_pairs(const std::vector<EndPoint*>&
|
|
|
94
96
|
}
|
|
95
97
|
return result;
|
|
96
98
|
}
|
|
99
|
+
|
|
100
|
+
void Part::orient()
|
|
101
|
+
{ if (this->size <= 1) {
|
|
102
|
+
this->versus_ = 0;
|
|
103
|
+
} else {
|
|
104
|
+
this->versus_ = (this->tail->payload->y - this->head->payload->y) > 0 ? 1 : -1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
std::string Part::inspect() {
|
|
109
|
+
size_t part_index = 0;
|
|
110
|
+
auto it = std::find(this->polyline()->parts().begin(), this->polyline()->parts().end(), this);
|
|
111
|
+
if (it != this->polyline()->parts().end()) {
|
|
112
|
+
part_index = std::distance(this->polyline()->parts().begin(), it);
|
|
113
|
+
}
|
|
114
|
+
std::stringstream ss;
|
|
115
|
+
ss << "part " << part_index
|
|
116
|
+
<< " (versus=" << this->versus_
|
|
117
|
+
<< " inv=" << this->inverts
|
|
118
|
+
<< " trm=" << this->trasmuted
|
|
119
|
+
<< " touched=" << this->touched_
|
|
120
|
+
<< " dead_end=" << this->dead_end
|
|
121
|
+
<< ", " << this->size << "x) of " << this->polyline()->tile->name()
|
|
122
|
+
<< " (" << std::to_string(static_cast<uint32_t>(type)) << ") (";
|
|
123
|
+
this->each([&](QNode<Point>* pos) -> bool {
|
|
124
|
+
ss << pos->payload->toString();
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
ss << ")";
|
|
128
|
+
return ss.str();
|
|
129
|
+
}
|
|
@@ -32,6 +32,7 @@ class Part : public Queueable<Point> {
|
|
|
32
32
|
bool inverts = false;
|
|
33
33
|
bool trasmuted = false;
|
|
34
34
|
bool delayed = false;
|
|
35
|
+
bool dead_end = false;
|
|
35
36
|
Part* next = nullptr;
|
|
36
37
|
Part* prev = nullptr;
|
|
37
38
|
Part* circular_next = nullptr;
|
|
@@ -39,17 +40,20 @@ class Part : public Queueable<Point> {
|
|
|
39
40
|
Polyline* polyline() { return polyline_; }
|
|
40
41
|
Position* next_position(Position* force_position);
|
|
41
42
|
void add_position(Point* point);
|
|
42
|
-
int passes = 0;
|
|
43
43
|
Types type;
|
|
44
44
|
bool innerable();
|
|
45
45
|
const bool touched() const { return touched_; }
|
|
46
|
+
const int versus() const { return versus_; }
|
|
46
47
|
void touch();
|
|
47
48
|
bool intersect_part(Part* other_part);
|
|
48
49
|
void set_polyline(Polyline* polyline) { this->polyline_ = polyline; }
|
|
49
50
|
std::vector<EndPoint*> to_endpoints();
|
|
50
51
|
static std::vector<EndPoint*> remove_adjacent_pairs(const std::vector<EndPoint*>& input);
|
|
52
|
+
void orient();
|
|
53
|
+
std::string inspect();
|
|
51
54
|
|
|
52
55
|
private:
|
|
56
|
+
int versus_ = 0;
|
|
53
57
|
bool touched_ = false;
|
|
54
58
|
Polyline* polyline_;
|
|
55
59
|
};
|
|
@@ -31,6 +31,8 @@ void Partitionable::add_part(Part* new_part)
|
|
|
31
31
|
}
|
|
32
32
|
new_part->prev = last;
|
|
33
33
|
new_part->circular_next = this->parts_.front();
|
|
34
|
+
|
|
35
|
+
if (new_part->is(Part::SEAM)) new_part->orient();
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
void Partitionable::insert_after(Part* part, Part* new_part) {
|
|
@@ -46,7 +48,7 @@ void Partitionable::insert_after(Part* part, Part* new_part) {
|
|
|
46
48
|
|
|
47
49
|
void Partitionable::partition()
|
|
48
50
|
{ this->parts_.clear();
|
|
49
|
-
Polyline *polyline =
|
|
51
|
+
Polyline *polyline = static_cast<Polyline*>(this);
|
|
50
52
|
PartPool& pool = polyline->tile->cluster->parts_pool;
|
|
51
53
|
Part *current_part = nullptr;
|
|
52
54
|
int n = 0;
|
|
@@ -77,10 +79,10 @@ void Partitionable::partition()
|
|
|
77
79
|
this->trasmute_parts();
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
Part* Partitionable::find_first_part_by_position(Position* position) {
|
|
82
|
+
Part* Partitionable::find_first_part_by_position(Position* position, int versus) {
|
|
81
83
|
for (Part* part : this->parts_)
|
|
82
84
|
{ if ( part->is(Part::SEAM) &&
|
|
83
|
-
part->
|
|
85
|
+
part->versus() == -(versus) &&
|
|
84
86
|
position->end_point()->queues_include(part)) return(part);
|
|
85
87
|
}
|
|
86
88
|
return(nullptr);
|
|
@@ -157,7 +159,7 @@ std::optional<SewReturnData> Partitionable::sew(std::vector<std::pair<int, int>>
|
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
std::vector<Part*> all_parts;
|
|
160
|
-
Polyline* polyline =
|
|
162
|
+
Polyline* polyline = static_cast<Polyline*>(this);
|
|
161
163
|
all_parts.reserve(before_parts.size() + after_parts.size());
|
|
162
164
|
all_parts.insert(all_parts.end(), before_parts.begin(), before_parts.end());
|
|
163
165
|
all_parts.insert(all_parts.end(), after_parts.begin(), after_parts.end());
|
|
@@ -194,7 +196,6 @@ std::optional<SewReturnData> Partitionable::sew(std::vector<std::pair<int, int>>
|
|
|
194
196
|
return std::make_pair(left, right);
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
|
|
198
199
|
void Partitionable::trasmute_parts()
|
|
199
200
|
{ std::vector<Part*> insides;
|
|
200
201
|
for (Part* p : parts_) {
|
|
@@ -205,16 +206,16 @@ void Partitionable::trasmute_parts()
|
|
|
205
206
|
for (Part* inside : insides)
|
|
206
207
|
{ for (Part* inside_compare : insides) {
|
|
207
208
|
if (inside == inside_compare || !inside_compare->is(Part::SEAM) ) continue;
|
|
208
|
-
|
|
209
|
+
int count = 0;
|
|
209
210
|
inside->each([&](QNode<Point>* pos) -> bool {
|
|
210
|
-
Position *position =
|
|
211
|
+
Position *position = static_cast<Position*>(pos);
|
|
211
212
|
if (position->end_point()->queues_include(inside_compare))
|
|
212
|
-
{
|
|
213
|
+
{ count++;
|
|
214
|
+
return true;
|
|
213
215
|
}
|
|
214
|
-
all_match = false;
|
|
215
216
|
return false;
|
|
216
217
|
});
|
|
217
|
-
if (
|
|
218
|
+
if (count == inside->size && count < inside_compare->size) {
|
|
218
219
|
inside->type = Part::EXCLUSIVE;
|
|
219
220
|
inside->trasmuted = true;
|
|
220
221
|
break;
|
|
@@ -222,4 +223,3 @@ void Partitionable::trasmute_parts()
|
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
|
-
|
|
@@ -20,8 +20,9 @@ class Partitionable {
|
|
|
20
20
|
explicit Partitionable() {}
|
|
21
21
|
virtual ~Partitionable() = default;
|
|
22
22
|
void partition();
|
|
23
|
-
Part* find_first_part_by_position(Position* position);
|
|
23
|
+
Part* find_first_part_by_position(Position* position, int versus);
|
|
24
24
|
std::optional<SewReturnData> sew(std::vector<std::pair<int, int>> intersection, Polyline* other);
|
|
25
|
+
const std::vector<Part*> parts() const { return parts_; }
|
|
25
26
|
|
|
26
27
|
protected:
|
|
27
28
|
std::vector<Part*> parts_;
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
#include <vector>
|
|
11
11
|
#include <limits>
|
|
12
|
+
#include <string>
|
|
12
13
|
#include <unordered_set>
|
|
14
|
+
#include <sstream>
|
|
13
15
|
#include "Polyline.h"
|
|
14
16
|
#include "Tile.h"
|
|
15
17
|
#include "Shape.h"
|
|
@@ -76,7 +78,7 @@ std::vector<std::pair<int, int>> Polyline::intersection(const Polyline* other) c
|
|
|
76
78
|
auto& part = parts_[i];
|
|
77
79
|
if (!part->is(Part::SEAM) && part->trasmuted) continue;
|
|
78
80
|
part->each([&](QNode<Point>* pos) -> bool {
|
|
79
|
-
Position *position =
|
|
81
|
+
Position *position = static_cast<Position*>(pos);
|
|
80
82
|
if (position->end_point() != nullptr)
|
|
81
83
|
{ this->tracked_endpoints[position->end_point()] = i;
|
|
82
84
|
}
|
|
@@ -92,7 +94,7 @@ std::vector<std::pair<int, int>> Polyline::intersection(const Polyline* other) c
|
|
|
92
94
|
continue;
|
|
93
95
|
}
|
|
94
96
|
other_part->each([&](QNode<Point>* pos) -> bool {
|
|
95
|
-
Position *position =
|
|
97
|
+
Position *position = static_cast<Position*>(pos);
|
|
96
98
|
auto it = this->tracked_endpoints.find(position->end_point());
|
|
97
99
|
if (it != this->tracked_endpoints.end()) {
|
|
98
100
|
int self_index = it->second;
|
|
@@ -114,3 +116,15 @@ void Polyline::clear() {
|
|
|
114
116
|
bool Polyline::is_empty() {
|
|
115
117
|
return raw_.empty();
|
|
116
118
|
}
|
|
119
|
+
|
|
120
|
+
std::string Polyline::info() {
|
|
121
|
+
Shape* shape = this->shape;
|
|
122
|
+
size_t part_index = 0;
|
|
123
|
+
auto it = std::find(this->tile->shapes().begin(), this->tile->shapes().end(), shape);
|
|
124
|
+
if (it != this->tile->shapes().end()) {
|
|
125
|
+
part_index = std::distance(this->tile->shapes().begin(), it);
|
|
126
|
+
}
|
|
127
|
+
std::stringstream ss;
|
|
128
|
+
ss << "b" << this->tile->name() << " S" << part_index;
|
|
129
|
+
return ss.str();
|
|
130
|
+
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <utility>
|
|
15
15
|
#include <vector>
|
|
16
16
|
#include <unordered_map>
|
|
17
|
+
#include <string>
|
|
17
18
|
#include "Partitionable.h"
|
|
18
19
|
#include "../RectBounds.h"
|
|
19
20
|
|
|
@@ -47,6 +48,7 @@ class Polyline : public Partitionable {
|
|
|
47
48
|
bool vert_intersect(Polyline& other);
|
|
48
49
|
void reset_tracked_endpoints();
|
|
49
50
|
bool mixed_tile_origin = false;
|
|
51
|
+
std::string info();
|
|
50
52
|
|
|
51
53
|
private:
|
|
52
54
|
std::vector<Point*> raw_;
|
|
@@ -30,12 +30,9 @@ Position::Position(EndPoint* end_point)
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
void Position::before_rem(Queueable<Point>* q) {
|
|
33
|
-
if (this->end_point_ != nullptr)
|
|
34
|
-
auto& queues = this->end_point_->queues();
|
|
35
|
-
queues.erase(std::remove(queues.begin(), queues.end(), q), queues.end());
|
|
36
|
-
}
|
|
33
|
+
if (this->end_point_ != nullptr) this->end_point_->queues().erase(q);
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
void Position::after_add(Queueable<Point>* q) {
|
|
40
|
-
if (this->end_point_ != nullptr) this->end_point_->queues().
|
|
37
|
+
if (this->end_point_ != nullptr) this->end_point_->queues().insert(q);
|
|
41
38
|
}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
require "mkmf-rice"
|
|
2
2
|
|
|
3
|
+
has_tcmalloc = find_library("tcmalloc", "malloc")
|
|
4
|
+
|
|
3
5
|
# rubocop:disable Style/GlobalVars
|
|
4
6
|
|
|
5
|
-
$CXXFLAGS << " -std=c++17 -pthread -march=native -DNDEBUG -Ofast -flto
|
|
6
|
-
$CFLAGS << " -std=
|
|
7
|
+
$CXXFLAGS << " -std=c++17 -pthread -march=native -DNDEBUG -Ofast -flto"
|
|
8
|
+
$CFLAGS << " -std=c11 -pthread -march=native -DNDEBUG -Ofast -flto"
|
|
9
|
+
|
|
10
|
+
if has_tcmalloc
|
|
11
|
+
$LDFLAGS << " -Wl,--no-as-needed -ltcmalloc"
|
|
12
|
+
puts "tcmalloc linked to gem."
|
|
13
|
+
else
|
|
14
|
+
puts "tcmalloc not found; standard malloc will be used."
|
|
15
|
+
end
|
|
16
|
+
|
|
7
17
|
$LDFLAGS << " -lz -lstdc++ -flto -pthread"
|
|
8
18
|
|
|
9
19
|
$objs = [
|
|
@@ -28,6 +28,7 @@ module Contrek
|
|
|
28
28
|
png_image.draw_line(poly[:outer][0][:x], poly[:outer][0][:y], poly[:outer][-1][:x], poly[:outer][-1][:y], color)
|
|
29
29
|
color = ChunkyPNG::Color("green @ 1.0")
|
|
30
30
|
poly[:inner].each do |sequence|
|
|
31
|
+
next if sequence.empty?
|
|
31
32
|
sequence.each_cons(2) do |coords|
|
|
32
33
|
png_image.draw_line(coords[0][:x], coords[0][:y], coords[1][:x], coords[1][:y], color)
|
|
33
34
|
end
|