contrek 1.2.9 → 1.3.1
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 +7 -0
- data/Gemfile.lock +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +13 -27
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +5 -7
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +4 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +14 -15
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +2 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Polygon.h +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +13 -12
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +10 -58
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +6 -6
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +3 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +8 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +4 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +6 -6
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +3 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +4 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +59 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +6 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +48 -33
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +4 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +13 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +4 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +4 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +7 -7
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.cpp +8 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.h +4 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp +20 -5
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h +6 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp +8 -5
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +9 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp +4 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/LinearReducer.cpp +8 -8
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/LinearReducer.h +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/Reducer.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/Reducer.h +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/UniqReducer.cpp +3 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/UniqReducer.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.cpp +3 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.h +10 -10
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +14 -14
- data/lib/contrek/bitmaps/chunky_bitmap.rb +11 -0
- data/lib/contrek/finder/concurrent/cluster.rb +1 -1
- data/lib/contrek/finder/concurrent/cursor.rb +0 -3
- data/lib/contrek/finder/concurrent/finder.rb +4 -0
- data/lib/contrek/finder/concurrent/merger.rb +1 -0
- data/lib/contrek/finder/concurrent/part.rb +36 -1
- data/lib/contrek/finder/concurrent/partitionable.rb +42 -23
- data/lib/contrek/finder/concurrent/vertical_merger.rb +4 -0
- data/lib/contrek/finder/polygon_finder.rb +2 -1
- data/lib/contrek/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fff9996602ac84d2a5d96e840150e59cb457bc8050c53758efac88f170bef79
|
|
4
|
+
data.tar.gz: 40958fef1d22e2458beae7291cfdca85102eda4983bf01ef468c334ddce99ec8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f8f1f1368c472fb9770a892874f54817a67745929a0d772c5a9cc3e387fcd868e5662ad67e341d6a53232b6be4eb028438545535adfb05be3d69c497c3918da
|
|
7
|
+
data.tar.gz: cc7991d57ce63d43c98165c1f3684d1f31d05993a6e4a20e40437d70d5b251a1de0032ca354f44d2bb8efbf30286423f6b2925e01da0f039c71661427f4d2716
|
data/CHANGELOG.md
CHANGED
|
@@ -122,3 +122,10 @@ All notable changes to this project will be documented in this file.
|
|
|
122
122
|
|
|
123
123
|
## [1.2.9] - 2026-06-13
|
|
124
124
|
- **Streaming merger:** The streaming merger class extends VerticalMerger and adds a useful feature: the progressive extraction of contours into a disk buffer (SVG file). In this way, all extracted polygons that are no longer within the junction zone of the next stripe are removed from the system and streamed directly to disk. This incredibly reduces memory consumption, allowing the processing of very large files on machines with low memory availability, at the expense of increased processing times. An example of this technique is available in both C++ and Ruby in the repository.
|
|
125
|
+
|
|
126
|
+
## [1.3.0] - 2026-06-17
|
|
127
|
+
- **Streaming merger:** Improvements and bug fixing.
|
|
128
|
+
- **CPP code:** All structures now own 'Point' instances by value instead of raw pointers. Removed now-redundant `clone()` method; results from `process_info()` are already self-contained since points are owned by value, so the defensive deep copy is no longer needed.
|
|
129
|
+
|
|
130
|
+
## [1.3.1] - 2026-06-20
|
|
131
|
+
- **Streaming merger:** The progressive streaming extraction mode has now reached new heights of efficiency on the C++ side. This mode allows the data source to be processed in contiguous blocks. All isolated polygons, as well as those extending into the bottom strips, are removed from the list and streamed directly to the SVG file. This drastically reduces RAM requirements. An extreme test on an 81920×81920 pixel image containing a massive number of polygons (20,000,000) was processed using roughly 40 strips of 2000 pixels each in less than 300 seconds, peaking at a RAM usage of just 13GB.
|
data/Gemfile.lock
CHANGED
|
@@ -60,16 +60,16 @@ void Tests::test_a()
|
|
|
60
60
|
std::vector<int> array_compare;
|
|
61
61
|
|
|
62
62
|
for (const auto& x : o->polygons)
|
|
63
|
-
{ for (const Point
|
|
64
|
-
array_compare.push_back(p
|
|
65
|
-
array_compare.push_back(p
|
|
63
|
+
{ for (const Point& p : x.outer) {
|
|
64
|
+
array_compare.push_back(p.x);
|
|
65
|
+
array_compare.push_back(p.y);
|
|
66
66
|
}
|
|
67
67
|
if (outer_array != array_compare) throw std::runtime_error("Wrong OUTER results!");
|
|
68
68
|
array_compare.clear();
|
|
69
69
|
for (const auto& z : x.inner)
|
|
70
|
-
{ for (const Point
|
|
71
|
-
{ array_compare.push_back(y
|
|
72
|
-
array_compare.push_back(y
|
|
70
|
+
{ for (const Point& y : z)
|
|
71
|
+
{ array_compare.push_back(y.x);
|
|
72
|
+
array_compare.push_back(y.y);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
if (inner_array != array_compare) throw std::runtime_error("Wrong INNER results!");
|
|
@@ -97,9 +97,9 @@ void Tests::test_b()
|
|
|
97
97
|
|
|
98
98
|
void Tests::test_c()
|
|
99
99
|
{ Sequence sequence;
|
|
100
|
-
Point
|
|
101
|
-
Point
|
|
102
|
-
Point
|
|
100
|
+
Point p1{1, 1};
|
|
101
|
+
Point p2{2, 2};
|
|
102
|
+
Point p3{3, 3};
|
|
103
103
|
|
|
104
104
|
Hub* hub = new Hub(4);
|
|
105
105
|
|
|
@@ -114,7 +114,7 @@ void Tests::test_c()
|
|
|
114
114
|
if (sequence.size != 3) throw std::runtime_error("Wrong sequence size");
|
|
115
115
|
|
|
116
116
|
// iterator() initially gives head
|
|
117
|
-
Point
|
|
117
|
+
const Point& head = sequence.head->payload;
|
|
118
118
|
if (head != p1) throw std::runtime_error("Wrong head");
|
|
119
119
|
if (sequence.iterator()->payload != p1) throw std::runtime_error("Wrong iterator to head");
|
|
120
120
|
if (sequence.iterator() != pos1) throw std::runtime_error("Wrong iterator to head");
|
|
@@ -135,10 +135,6 @@ void Tests::test_c()
|
|
|
135
135
|
sequence.rewind();
|
|
136
136
|
if (sequence.iterator()->payload != p1) throw std::runtime_error("Wrong iterator to head");
|
|
137
137
|
|
|
138
|
-
delete p1;
|
|
139
|
-
delete p2;
|
|
140
|
-
delete p3;
|
|
141
|
-
|
|
142
138
|
delete hub;
|
|
143
139
|
|
|
144
140
|
delete pos1;
|
|
@@ -348,10 +344,8 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height, bool
|
|
|
348
344
|
ProcessResult *result = polygon_finder.process_info();
|
|
349
345
|
if (result) {
|
|
350
346
|
std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
vmerger.add_tile(*safe_result);
|
|
354
|
-
delete result;
|
|
347
|
+
result_clones.push_back(result);
|
|
348
|
+
vmerger.add_tile(*result);
|
|
355
349
|
}
|
|
356
350
|
stripe_count++;
|
|
357
351
|
}
|
|
@@ -467,22 +461,14 @@ void stream_progressive_png_image(const std::string& filepath, uint32_t stripe_h
|
|
|
467
461
|
ProcessResult *result = polygon_finder.process_info();
|
|
468
462
|
if (result) {
|
|
469
463
|
std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
|
|
470
|
-
|
|
471
|
-
result_clones.push_back(safe_result);
|
|
472
|
-
vmerger.add_tile(*safe_result, !(current_y_offset + stripe_height < total_height));
|
|
464
|
+
vmerger.add_tile(*result, !(current_y_offset + stripe_height < total_height));
|
|
473
465
|
delete result;
|
|
474
466
|
}
|
|
475
467
|
stripe_count++;
|
|
476
468
|
}
|
|
477
|
-
|
|
478
469
|
ProcessResult *merged_result = vmerger.process_info();
|
|
479
470
|
std::cout << "total found polygons " << merged_result->groups << std::endl;
|
|
480
471
|
delete merged_result;
|
|
481
|
-
|
|
482
|
-
// frees memory
|
|
483
|
-
for (auto c : result_clones) {
|
|
484
|
-
delete c;
|
|
485
|
-
}
|
|
486
472
|
} catch (const std::exception& e) {
|
|
487
473
|
std::cerr << "\n[ERROR] Processing exception: " << e.what() << std::endl;
|
|
488
474
|
if (shared_stream.is_open()) shared_stream.close();
|
|
@@ -100,33 +100,31 @@ Node* Node::my_next_outer(Node *last, int versus) {
|
|
|
100
100
|
return get_tangent_node_by_virtual_index(this->tangs_sequence[last_node_index]);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
Point
|
|
103
|
+
Point Node::coords_entering_to(Node *enter_to, int mode, int tracking) {
|
|
104
104
|
int enter_to_index;
|
|
105
105
|
if (enter_to->y < this->y) enter_to_index = enter_to->abs_x_index + this->up_indexer;
|
|
106
106
|
else enter_to_index = this->down_indexer - enter_to->abs_x_index;
|
|
107
107
|
|
|
108
108
|
int tg_index = this->tangs_sequence[enter_to_index];
|
|
109
|
-
Point* point;
|
|
110
109
|
if (tg_index < 0) {
|
|
111
110
|
Node& node_up = cluster->vert_nodes[y + T_UP][-(tg_index + 1)];
|
|
112
111
|
if (mode == Node::A) {
|
|
113
112
|
enter_to->track |= TURNER[tracking][OMAX - 1];
|
|
114
|
-
|
|
113
|
+
return node_up.end_point;
|
|
115
114
|
} else {
|
|
116
115
|
enter_to->track |= TURNER[tracking][OMIN - 1];
|
|
117
|
-
|
|
116
|
+
return node_up.start_point;
|
|
118
117
|
}
|
|
119
118
|
} else {
|
|
120
119
|
Node& node_down = cluster->vert_nodes[y + T_DOWN][tg_index];
|
|
121
120
|
if (mode == Node::A) {
|
|
122
121
|
enter_to->track |= TURNER[tracking][OMIN - 1];
|
|
123
|
-
|
|
122
|
+
return node_down.start_point;
|
|
124
123
|
} else {
|
|
125
124
|
enter_to->track |= TURNER[tracking][OMAX - 1];
|
|
126
|
-
|
|
125
|
+
return node_down.end_point;
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
|
-
return point;
|
|
130
128
|
}
|
|
131
129
|
|
|
132
130
|
Node* Node::get_tangent_node_by_virtual_index(int virtual_index) {
|
|
@@ -61,6 +61,9 @@ struct Point {
|
|
|
61
61
|
bool operator==(const Point& other) const {
|
|
62
62
|
return x == other.x && y == other.y;
|
|
63
63
|
}
|
|
64
|
+
bool operator!=(const Point& other) const {
|
|
65
|
+
return !(*this == other);
|
|
66
|
+
}
|
|
64
67
|
Point(int x_, int y_) : x(x_), y(y_) {}
|
|
65
68
|
Point() : x(0), y(0) {}
|
|
66
69
|
};
|
|
@@ -96,7 +99,7 @@ class Node : public Listable {
|
|
|
96
99
|
NodeCluster* cluster;
|
|
97
100
|
void add_intersection(Node& other_node, int other_node_index);
|
|
98
101
|
SmallVec tangs_sequence;
|
|
99
|
-
Point
|
|
102
|
+
Point coords_entering_to(Node *enter_to, int mode, int tracking);
|
|
100
103
|
Node* my_next_outer(Node *last, int versus);
|
|
101
104
|
Node* my_next_inner(Node *last, int versus);
|
|
102
105
|
Node* get_tangent_node_by_virtual_index(int vitual_index);
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
#include "NodeCluster.h"
|
|
19
19
|
#include "Node.h"
|
|
20
20
|
#include "RectBounds.h"
|
|
21
|
-
#include "PointPool.h"
|
|
22
21
|
#include "../reducers/UniqReducer.h"
|
|
23
22
|
#include "../reducers/LinearReducer.h"
|
|
24
23
|
#include "../reducers/VisvalingamReducer.h"
|
|
@@ -44,7 +43,7 @@ NodeCluster::~NodeCluster() {
|
|
|
44
43
|
void NodeCluster::compress_coords(std::list<Polygon>& polygons, pf_Options options) {
|
|
45
44
|
if (!(options.compress_linear || options.compress_uniq || options.compress_visvalingam)) return;
|
|
46
45
|
|
|
47
|
-
auto compress_sequence = [&](std::vector<Point
|
|
46
|
+
auto compress_sequence = [&](std::vector<Point>& points_vec) {
|
|
48
47
|
if (points_vec.empty()) return;
|
|
49
48
|
|
|
50
49
|
if (options.compress_uniq) {
|
|
@@ -122,9 +121,9 @@ void NodeCluster::plot(int versus) {
|
|
|
122
121
|
next_node = root_node->get_tangent_node_by_virtual_index(root_node->tangs_sequence.front());
|
|
123
122
|
|
|
124
123
|
if (next_node != nullptr)
|
|
125
|
-
{ Point
|
|
124
|
+
{ Point p = next_node->coords_entering_to(root_node, versus_inverter[versus], Node::OUTER);
|
|
125
|
+
poly.bounds.expand(p.x, p.y);
|
|
126
126
|
poly.outer.push_back(p);
|
|
127
|
-
poly.bounds.expand(p->x, p->y);
|
|
128
127
|
}
|
|
129
128
|
if ((this->nodes > 0) && (next_node != nullptr))
|
|
130
129
|
{ plot_node(poly.outer, next_node, root_node, versus, poly.bounds);
|
|
@@ -137,7 +136,7 @@ void NodeCluster::plot(int versus) {
|
|
|
137
136
|
int index_inner = 0;
|
|
138
137
|
while (inner_plot->size() > 0)
|
|
139
138
|
{ this->plot_sequence.clear();
|
|
140
|
-
std::vector<Point
|
|
139
|
+
std::vector<Point> inner_sequence;
|
|
141
140
|
std::list<Node*>::iterator first_i;
|
|
142
141
|
Node *first = nullptr;
|
|
143
142
|
|
|
@@ -243,7 +242,7 @@ std::pair<int, int> NodeCluster::test_in_hole_o(Node* node)
|
|
|
243
242
|
return {-1, -1};
|
|
244
243
|
}
|
|
245
244
|
|
|
246
|
-
void NodeCluster::plot_inner_node(std::vector<Point
|
|
245
|
+
void NodeCluster::plot_inner_node(std::vector<Point>& sequence_coords, Node *node, int versus, Node *stop_at, Node *start_node) {
|
|
247
246
|
Node *current_node = node;
|
|
248
247
|
bool strict_bounds = this->options->strict_bounds;
|
|
249
248
|
while (current_node != nullptr) {
|
|
@@ -279,8 +278,8 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
|
|
|
279
278
|
}
|
|
280
279
|
}
|
|
281
280
|
} else if (strict_bounds) {
|
|
282
|
-
sequence_coords.push_back(
|
|
283
|
-
sequence_coords.push_back(
|
|
281
|
+
sequence_coords.push_back(Point{(first_is_max ? last_node->max_x : last_node->min_x), current_node->y});
|
|
282
|
+
sequence_coords.push_back(Point{(first_is_max ? next_node->min_x : next_node->max_x), current_node->y});
|
|
284
283
|
}
|
|
285
284
|
|
|
286
285
|
if (current_node->track_uncomplete()) {
|
|
@@ -293,7 +292,7 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
|
|
|
293
292
|
}
|
|
294
293
|
}
|
|
295
294
|
|
|
296
|
-
void NodeCluster::plot_node(std::vector<Point
|
|
295
|
+
void NodeCluster::plot_node(std::vector<Point>& sequence_coords, Node *node, Node *start_node, int versus, RectBounds& bounds) {
|
|
297
296
|
Node *current_node = node;
|
|
298
297
|
bool strict_bounds = this->options->strict_bounds;
|
|
299
298
|
|
|
@@ -315,23 +314,23 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
|
|
|
315
314
|
plot = (n == next_node);
|
|
316
315
|
}
|
|
317
316
|
if (plot) {
|
|
318
|
-
Point
|
|
317
|
+
Point p = last_node->coords_entering_to(current_node, versus, Node::OUTER);
|
|
318
|
+
bounds.expand(p.x, p.y);
|
|
319
319
|
sequence_coords.push_back(p);
|
|
320
|
-
bounds.expand(p->x, p->y);
|
|
321
320
|
if (current_node != start_node) {
|
|
322
321
|
inner_plot->contains(current_node) ? inner_plot->remove(current_node) : inner_plot->push_back(current_node);
|
|
323
322
|
if (last_node->y == next_node->y) {
|
|
324
|
-
Point
|
|
323
|
+
Point p1 = next_node->coords_entering_to(current_node, versus_inverter[versus], Node::OUTER);
|
|
324
|
+
bounds.expand(p1.x, p1.y);
|
|
325
325
|
sequence_coords.push_back(p1);
|
|
326
|
-
bounds.expand(p1->x, p1->y);
|
|
327
326
|
inner_plot->contains(current_node) ? inner_plot->remove(current_node) : inner_plot->push_back(current_node);
|
|
328
327
|
}
|
|
329
328
|
}
|
|
330
329
|
} else if (strict_bounds) {
|
|
331
330
|
bool is_down = current_node->y > last_node->y;
|
|
332
331
|
bool is_a = (versus == Node::A);
|
|
333
|
-
sequence_coords.push_back(
|
|
334
|
-
sequence_coords.push_back(
|
|
332
|
+
sequence_coords.push_back(Point{(is_down == is_a ? last_node->min_x : last_node->max_x), current_node->y});
|
|
333
|
+
sequence_coords.push_back(Point{(is_down == is_a ? next_node->max_x : next_node->min_x), current_node->y});
|
|
335
334
|
}
|
|
336
335
|
|
|
337
336
|
if (current_node == start_node) {
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
#include "Lists.h"
|
|
19
19
|
#include "RectBounds.h"
|
|
20
20
|
#include "Polygon.h"
|
|
21
|
-
#include "PointPool.h"
|
|
22
21
|
|
|
23
22
|
class Node;
|
|
24
23
|
struct Point;
|
|
@@ -26,8 +25,8 @@ struct pf_Options;
|
|
|
26
25
|
|
|
27
26
|
class NodeCluster {
|
|
28
27
|
private:
|
|
29
|
-
void plot_node(std::vector<Point
|
|
30
|
-
void plot_inner_node(std::vector<Point
|
|
28
|
+
void plot_node(std::vector<Point>& sequence_coords, Node *node, Node *start_node, int versus, RectBounds& bounds);
|
|
29
|
+
void plot_inner_node(std::vector<Point>& sequence_coords, Node *node, int versus, Node *stop_at, Node *start_node);
|
|
31
30
|
std::vector<Node*> plot_sequence;
|
|
32
31
|
List *inner_plot;
|
|
33
32
|
List *inner_new;
|
|
@@ -35,7 +34,6 @@ class NodeCluster {
|
|
|
35
34
|
int count = 0;
|
|
36
35
|
int nodes;
|
|
37
36
|
int width;
|
|
38
|
-
PointPool points_pool;
|
|
39
37
|
|
|
40
38
|
public:
|
|
41
39
|
pf_Options *options;
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
#include "RectBounds.h"
|
|
15
15
|
|
|
16
16
|
struct Polygon {
|
|
17
|
-
std::vector<Point
|
|
18
|
-
std::list<std::vector<Point
|
|
17
|
+
std::vector<Point> outer;
|
|
18
|
+
std::list<std::vector<Point>> inner;
|
|
19
19
|
RectBounds bounds;
|
|
20
20
|
Polygon() : bounds(RectBounds::empty()) {}
|
|
21
21
|
};
|
|
@@ -108,6 +108,7 @@ ProcessResult* PolygonFinder::process_info() {
|
|
|
108
108
|
pr->width = this->source_bitmap->w();
|
|
109
109
|
pr->height = this->source_bitmap->h();
|
|
110
110
|
pr->has_bounds = this->node_cluster->options->bounds;
|
|
111
|
+
pr->versus = this->options.versus;
|
|
111
112
|
|
|
112
113
|
if (this->node_cluster->options->named_sequences && typeid(*this->source_bitmap) == typeid(Bitmap))
|
|
113
114
|
{ std::string sequence;
|
|
@@ -133,25 +134,25 @@ void ProcessResult::draw_on_bitmap(RawBitmap& bitmap) const {
|
|
|
133
134
|
// --- OUTER ---
|
|
134
135
|
if (!poly.outer.empty()) {
|
|
135
136
|
for (size_t i = 0; i < poly.outer.size() - 1; ++i) {
|
|
136
|
-
Point
|
|
137
|
-
Point
|
|
138
|
-
bitmap.draw_line(p1
|
|
137
|
+
const Point& p1 = poly.outer[i];
|
|
138
|
+
const Point& p2 = poly.outer[i+1];
|
|
139
|
+
bitmap.draw_line(p1.x, p1.y, p2.x, p2.y, 255, 0, 0, 255);
|
|
139
140
|
}
|
|
140
|
-
Point
|
|
141
|
-
Point
|
|
142
|
-
bitmap.draw_line(last
|
|
141
|
+
const Point& last = poly.outer.back();
|
|
142
|
+
const Point& first = poly.outer.front();
|
|
143
|
+
bitmap.draw_line(last.x, last.y, first.x, first.y, 255, 0, 0, 255);
|
|
143
144
|
}
|
|
144
145
|
// --- INNER ---
|
|
145
146
|
for (const auto& sequence : poly.inner) {
|
|
146
147
|
if (sequence.empty()) continue;
|
|
147
148
|
for (size_t i = 0; i < sequence.size() - 1; ++i) {
|
|
148
|
-
Point
|
|
149
|
-
Point
|
|
150
|
-
bitmap.draw_line(p1
|
|
149
|
+
const Point& p1 = sequence[i];
|
|
150
|
+
const Point& p2 = sequence[i+1];
|
|
151
|
+
bitmap.draw_line(p1.x, p1.y, p2.x, p2.y, 0, 128, 0, 255);
|
|
151
152
|
}
|
|
152
|
-
Point
|
|
153
|
-
Point
|
|
154
|
-
bitmap.draw_line(last
|
|
153
|
+
const Point& last = sequence.back();
|
|
154
|
+
const Point& first = sequence.front();
|
|
155
|
+
bitmap.draw_line(last.x, last.y, first.x, first.y, 0, 128, 0, 255);
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
}
|
|
@@ -61,12 +61,12 @@ struct pf_Options {
|
|
|
61
61
|
struct ProcessResult {
|
|
62
62
|
int groups;
|
|
63
63
|
int width, height;
|
|
64
|
+
int versus;
|
|
64
65
|
bool has_bounds = false;
|
|
65
66
|
std::map<std::string, double> benchmarks;
|
|
66
67
|
std::list<Polygon> polygons;
|
|
67
68
|
std::string named_sequence;
|
|
68
69
|
std::vector<std::pair<int, int>> treemap;
|
|
69
|
-
std::vector<Point> cloned_points_storage;
|
|
70
70
|
|
|
71
71
|
void draw_on_bitmap(RawBitmap& canvas) const;
|
|
72
72
|
|
|
@@ -74,12 +74,12 @@ struct ProcessResult {
|
|
|
74
74
|
int counter = 0;
|
|
75
75
|
for (const auto& polygon : polygons) {
|
|
76
76
|
std::cout << counter << " - " << "outer" << "\n";
|
|
77
|
-
for (const Point
|
|
77
|
+
for (const Point& p : polygon.outer) std::cout << p.toString();
|
|
78
78
|
bool first = true;
|
|
79
79
|
for (const auto& seq : polygon.inner) {
|
|
80
80
|
if (!first) std::cout << "\n";
|
|
81
81
|
first = false;
|
|
82
|
-
for (const Point
|
|
82
|
+
for (const Point& p : seq) std::cout << p.toString();
|
|
83
83
|
}
|
|
84
84
|
std::cout << "\n" << polygon.bounds.to_string() <<"\n";
|
|
85
85
|
counter++;
|
|
@@ -95,61 +95,15 @@ struct ProcessResult {
|
|
|
95
95
|
|
|
96
96
|
void translate(int x) {
|
|
97
97
|
for (auto& polygon : polygons) {
|
|
98
|
-
for (Point
|
|
99
|
-
for (
|
|
100
|
-
for (Point
|
|
98
|
+
for (Point& p : polygon.outer) p.x += x;
|
|
99
|
+
for (auto& seq : polygon.inner) {
|
|
100
|
+
for (Point& p : seq) p.x += x;
|
|
101
101
|
}
|
|
102
102
|
polygon.bounds.min_x += x;
|
|
103
103
|
polygon.bounds.max_x += x;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
ProcessResult* clone() const {
|
|
108
|
-
ProcessResult* new_res = new ProcessResult();
|
|
109
|
-
new_res->groups = this->groups;
|
|
110
|
-
new_res->width = this->width;
|
|
111
|
-
new_res->height = this->height;
|
|
112
|
-
new_res->benchmarks = this->benchmarks;
|
|
113
|
-
new_res->named_sequence = this->named_sequence;
|
|
114
|
-
new_res->treemap = this->treemap;
|
|
115
|
-
|
|
116
|
-
size_t estimated_points = 0;
|
|
117
|
-
for (const auto& poly : this->polygons) {
|
|
118
|
-
estimated_points += poly.outer.size();
|
|
119
|
-
for (const auto& seq : poly.inner) estimated_points += seq.size();
|
|
120
|
-
}
|
|
121
|
-
new_res->cloned_points_storage.reserve(estimated_points);
|
|
122
|
-
|
|
123
|
-
for (const auto& poly : this->polygons) {
|
|
124
|
-
Polygon new_poly;
|
|
125
|
-
new_poly.bounds = poly.bounds;
|
|
126
|
-
new_poly.outer.reserve(poly.outer.size());
|
|
127
|
-
// outer
|
|
128
|
-
for (const Point* p : poly.outer) {
|
|
129
|
-
if (p) {
|
|
130
|
-
new_res->cloned_points_storage.push_back(Point(p->x, p->y));
|
|
131
|
-
size_t idx = new_res->cloned_points_storage.size() - 1;
|
|
132
|
-
new_poly.outer.push_back(&new_res->cloned_points_storage[idx]);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// inner
|
|
136
|
-
for (const auto& seq : poly.inner) {
|
|
137
|
-
std::vector<Point*> new_seq;
|
|
138
|
-
new_seq.reserve(seq.size());
|
|
139
|
-
for (const Point* p : seq) {
|
|
140
|
-
if (p) {
|
|
141
|
-
new_res->cloned_points_storage.push_back(Point(p->x, p->y));
|
|
142
|
-
size_t idx = new_res->cloned_points_storage.size() - 1;
|
|
143
|
-
new_seq.push_back(&new_res->cloned_points_storage[idx]);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
new_poly.inner.push_back(new_seq);
|
|
147
|
-
}
|
|
148
|
-
new_res->polygons.push_back(new_poly);
|
|
149
|
-
}
|
|
150
|
-
return new_res;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
107
|
void to_svg_stream(std::ostream& os) const {
|
|
154
108
|
os << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
|
155
109
|
<< "width=\"" << width << "\" height=\"" << height << "\">\n";
|
|
@@ -158,11 +112,10 @@ struct ProcessResult {
|
|
|
158
112
|
if (!poly.outer.empty()) {
|
|
159
113
|
os << "<polygon points=\"";
|
|
160
114
|
bool first = true;
|
|
161
|
-
for (const Point
|
|
162
|
-
if (!p) continue;
|
|
115
|
+
for (const Point& p : poly.outer) {
|
|
163
116
|
if (!first) os << " ";
|
|
164
117
|
first = false;
|
|
165
|
-
os << p
|
|
118
|
+
os << p.x << "," << p.y;
|
|
166
119
|
}
|
|
167
120
|
os << "\" fill=\"none\" stroke=\"red\" stroke-width=\"1\"/>\n";
|
|
168
121
|
}
|
|
@@ -171,11 +124,10 @@ struct ProcessResult {
|
|
|
171
124
|
if (sequence.empty()) continue;
|
|
172
125
|
os << "<polygon points=\"";
|
|
173
126
|
bool first = true;
|
|
174
|
-
for (const Point
|
|
175
|
-
if (!p) continue;
|
|
127
|
+
for (const Point& p : sequence) {
|
|
176
128
|
if (!first) os << " ";
|
|
177
129
|
first = false;
|
|
178
|
-
os << p
|
|
130
|
+
os << p.x << "," << p.y;
|
|
179
131
|
}
|
|
180
132
|
os << "\" fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>\n";
|
|
181
133
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
#include "../../CpuTimer.h"
|
|
19
19
|
|
|
20
20
|
Cluster::Cluster(Finder *finder, int height, int start_x, int end_x)
|
|
21
|
-
:
|
|
21
|
+
: finder_(finder)
|
|
22
22
|
{ tiles_.reserve(2); // only two (left|right)
|
|
23
23
|
this->hub_ = new Hub(height);
|
|
24
24
|
}
|
|
@@ -40,16 +40,16 @@ void Cluster::add(Tile* tile) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
void Cluster::list_to_string(std::vector<Point
|
|
43
|
+
void Cluster::list_to_string(std::vector<Point> list)
|
|
44
44
|
{ std::cout << "(" << &list << ") ";
|
|
45
|
-
for (Point
|
|
46
|
-
std::cout << point
|
|
45
|
+
for (const Point& point : list) {
|
|
46
|
+
std::cout << point.toString();
|
|
47
47
|
}
|
|
48
48
|
std::cout << std::endl;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
Tile* Cluster::merge_tiles() {
|
|
52
|
-
bool treemap = this->
|
|
52
|
+
bool treemap = this->finder_->options().treemap;
|
|
53
53
|
double tot_inner = 0;
|
|
54
54
|
double tot_outer = 0;
|
|
55
55
|
CpuTimer timer;
|
|
@@ -141,7 +141,7 @@ Tile* Cluster::merge_tiles() {
|
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
Tile* tile = new Tile(
|
|
144
|
-
this->
|
|
144
|
+
this->finder_, tiles_.front()->start_x(), tiles_.back()->end_x(), tiles_.front()->name() + tiles_.back()->name(), b);
|
|
145
145
|
|
|
146
146
|
tile->assign_shapes(new_shapes);
|
|
147
147
|
for (Tile* old_tile : tiles_) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
class Cluster {
|
|
20
20
|
private:
|
|
21
|
-
Finder *
|
|
21
|
+
Finder *finder_;
|
|
22
22
|
std::vector<Tile*> tiles_;
|
|
23
23
|
Hub *hub_ = nullptr;
|
|
24
24
|
void assign_ancestry(Shape *shape, InnerPolyline* inner_polyline);
|
|
@@ -31,7 +31,8 @@ class Cluster {
|
|
|
31
31
|
Tile* merge_tiles();
|
|
32
32
|
const std::vector<Tile*> tiles() const { return tiles_; }
|
|
33
33
|
Hub* hub() { return hub_; }
|
|
34
|
-
static void list_to_string(std::vector<Point
|
|
34
|
+
static void list_to_string(std::vector<Point> list);
|
|
35
35
|
PartPool parts_pool;
|
|
36
36
|
std::deque<Position> positions_pool;
|
|
37
|
+
const Finder* finder() const { return finder_; }
|
|
37
38
|
};
|
|
@@ -29,7 +29,7 @@ Sequence* Cursor::join_outers()
|
|
|
29
29
|
this->shapes_sequence_,
|
|
30
30
|
outer_joined_polyline);
|
|
31
31
|
|
|
32
|
-
if (
|
|
32
|
+
if (outer_joined_polyline->head->payload == outer_joined_polyline->tail->payload &&
|
|
33
33
|
this->cluster.tiles().front()->left() &&
|
|
34
34
|
this->cluster.tiles().back()->right()) outer_joined_polyline->pop();
|
|
35
35
|
|
|
@@ -61,11 +61,6 @@ void Cursor::traverse_outer(Part* act_part,
|
|
|
61
61
|
if (act_part->size == 0) return;
|
|
62
62
|
|
|
63
63
|
while (Position *position = act_part->next_position(nullptr)) {
|
|
64
|
-
if (outer_joined_polyline->size > 1 &&
|
|
65
|
-
outer_joined_polyline->head->payload == position->payload &&
|
|
66
|
-
act_part == all_parts.front()) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
64
|
outer_joined_polyline->add(position);
|
|
70
65
|
}
|
|
71
66
|
} else {
|
|
@@ -151,11 +146,11 @@ std::vector<InnerPolyline*> Cursor::join_inners(Sequence* outer_seq, bool treema
|
|
|
151
146
|
retme_sequence->move_from(*part, [&](QNode<Point>* pos) -> bool {
|
|
152
147
|
Position *position = static_cast<Position*>(pos);
|
|
153
148
|
if (part->is(Part::ADDED) &&
|
|
154
|
-
!(position->payload
|
|
155
|
-
position->payload
|
|
149
|
+
!(position->payload.y >= bounds.min &&
|
|
150
|
+
position->payload.y <= bounds.max)) {
|
|
156
151
|
return(false);
|
|
157
152
|
}
|
|
158
|
-
return(!(polyline->tile->tg_border(
|
|
153
|
+
return(!(polyline->tile->tg_border(position->payload) && position->end_point()->tracked_outer));
|
|
159
154
|
});
|
|
160
155
|
}
|
|
161
156
|
if (retme_sequence->is_not_vertical()) {
|
|
@@ -175,7 +170,7 @@ void Cursor::mark_children(std::vector<EndPoint*>& end_points, const Polyline* o
|
|
|
175
170
|
for (size_t i = 0; i + 1 < end_points.size(); i += 2) {
|
|
176
171
|
const auto& a = end_points[i];
|
|
177
172
|
const auto& b = end_points[i + 1];
|
|
178
|
-
auto [y_min, y_max] = std::minmax(a->get_point()
|
|
173
|
+
auto [y_min, y_max] = std::minmax(a->get_point().y, b->get_point().y);
|
|
179
174
|
for (int y = y_min + 1; y < y_max; ++y) {
|
|
180
175
|
EndPoint* end_point = this->cluster.hub()->get(y);
|
|
181
176
|
if (end_point) {
|
|
@@ -199,9 +194,9 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
|
|
|
199
194
|
auto [min_it, max_it] = std::minmax_element(
|
|
200
195
|
points.begin(),
|
|
201
196
|
points.end(),
|
|
202
|
-
[](Point
|
|
203
|
-
bounds.min = std::min(bounds.min,
|
|
204
|
-
bounds.max = std::max(bounds.max,
|
|
197
|
+
[](const Point& a, const Point& b) { return a.y < b.y; });
|
|
198
|
+
bounds.min = std::min(bounds.min, min_it->y);
|
|
199
|
+
bounds.max = std::max(bounds.max, max_it->y);
|
|
205
200
|
}
|
|
206
201
|
if (act_part->innerable()) {
|
|
207
202
|
all_parts.push_back(act_part);
|
|
@@ -16,15 +16,15 @@ class Part;
|
|
|
16
16
|
|
|
17
17
|
class EndPoint {
|
|
18
18
|
public:
|
|
19
|
-
EndPoint()
|
|
19
|
+
EndPoint() {}
|
|
20
20
|
|
|
21
|
-
void set_point(Point
|
|
22
|
-
Point
|
|
21
|
+
void set_point(const Point& p) { point_ = p; }
|
|
22
|
+
const Point& get_point() const { return point_; }
|
|
23
23
|
std::vector<Queueable<Point>*>& queues() { return queues_; }
|
|
24
24
|
bool queues_include(Queueable<Point>* q) const;
|
|
25
25
|
bool tracked_outer = false;
|
|
26
26
|
|
|
27
27
|
private:
|
|
28
|
-
Point
|
|
28
|
+
Point point_;
|
|
29
29
|
std::vector<Queueable<Point>*> queues_;
|
|
30
30
|
};
|