contrek 1.1.3 → 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/CHANGELOG.md +6 -1
- data/Gemfile.lock +1 -1
- data/README.md +17 -16
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +38 -20
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +4 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +15 -5
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +3 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +3 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +4 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +3 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -5
- data/lib/contrek/finder/concurrent/cursor.rb +2 -0
- data/lib/contrek/finder/node.rb +38 -21
- data/lib/contrek/finder/node_cluster.rb +12 -15
- data/lib/contrek/finder/polygon_finder.rb +0 -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: 2b2690fa75c9a6671f9c8ea91522d3adaf8600bfc0739dad4c8eb4a14b80d97e
|
|
4
|
+
data.tar.gz: 38db9719cc44356ec32d33031e877edda131a970ec0fb5e385f9c31e19699858
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4827a05d797400b0245c8225274dc070870e83186d51da4b4f16bb8368359597967012b65cedbff39f808fa9d1eafad5e3012e144f10afb801d564a2ff916dca
|
|
7
|
+
data.tar.gz: 690435e9531d0c8c47c703e41c4719333b91edbe60232a5183c30e21a2822c06b827498b49afbda4785cff404e3abdaf776f0a0b4aec9c9b58a0a9fbf29c8844
|
data/CHANGELOG.md
CHANGED
|
@@ -52,4 +52,9 @@ All notable changes to this project will be documented in this file.
|
|
|
52
52
|
## [1.1.3] - 2026-02-21
|
|
53
53
|
### Changed
|
|
54
54
|
- Added support for 8-way pixel connectivity (omnidirectional) in addition to the standard 4-way mode.
|
|
55
|
-
- Optimized C++ and Ruby algorithms for initial spatial pixel tracking to improve performance.
|
|
55
|
+
- Optimized C++ and Ruby algorithms for initial spatial pixel tracking to improve performance.
|
|
56
|
+
|
|
57
|
+
## [1.1.4] - 2026-02-28
|
|
58
|
+
### Changed
|
|
59
|
+
- Fixed an infinite loop bug in multithreading during inner sequence joining in Omnidirectional mode.
|
|
60
|
+
- Optimized C++ and Ruby algorithms for initial spatial tangential sequence determination.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -112,7 +112,7 @@ Regarding multithreading:
|
|
|
112
112
|
|
|
113
113
|
- The treemap option is currently ignored (multithreaded treemap support will be introduced in upcoming revisions).
|
|
114
114
|
|
|
115
|
-
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.
|
|
115
|
+
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.2 secs with image load).
|
|
116
116
|
|
|
117
117
|
```ruby
|
|
118
118
|
result = Contrek.contour!(
|
|
@@ -126,11 +126,11 @@ result = Contrek.contour!(
|
|
|
126
126
|
)
|
|
127
127
|
puts result.metadata[:benchmarks].inspect
|
|
128
128
|
|
|
129
|
-
{ compress:
|
|
130
|
-
init:
|
|
131
|
-
inner:
|
|
132
|
-
outer:
|
|
133
|
-
total:
|
|
129
|
+
{ compress: 26.37,
|
|
130
|
+
init: 354.339,
|
|
131
|
+
inner: 17.9427,
|
|
132
|
+
outer: 93.6252,
|
|
133
|
+
total: 380.709
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
```
|
|
@@ -288,23 +288,24 @@ This process is applied recursively, merging bands until a single final band rem
|
|
|
288
288
|
One of the most complex test you can find under the spec folder is named "scans poly 1200x800", scans this [image](spec/files/images/sample_1200x800.png) computing coordinates to draw polygons drawn in this [result](spec/files/stored_samples/sample_1200x800.png).
|
|
289
289
|
On pure ruby implementation kept time
|
|
290
290
|
```ruby
|
|
291
|
-
{ :scan=>
|
|
292
|
-
:build_tangs_sequence=>
|
|
293
|
-
:plot=>
|
|
291
|
+
{ :scan=>775.435,
|
|
292
|
+
:build_tangs_sequence=>38.916,
|
|
293
|
+
:plot=>101.876,
|
|
294
294
|
:compress=>0.002,
|
|
295
|
-
:total=>
|
|
295
|
+
:total=>916.229
|
|
296
|
+
}
|
|
296
297
|
```
|
|
297
298
|
This the one for the native C++
|
|
298
299
|
```ruby
|
|
299
|
-
{ scan:
|
|
300
|
-
build_tangs_sequence:
|
|
301
|
-
plot:
|
|
302
|
-
compress: 0.
|
|
303
|
-
total:
|
|
300
|
+
{ scan: 5.077878999999999,
|
|
301
|
+
build_tangs_sequence: 0.697222999999999,
|
|
302
|
+
plot: 2.00479,
|
|
303
|
+
compress: 0.00071,
|
|
304
|
+
total: 7.780602
|
|
304
305
|
}
|
|
305
306
|
```
|
|
306
307
|
|
|
307
|
-
About
|
|
308
|
+
About 130x faster. Times are in microseconds; system: AMD Ryzen 7 3700X 8-Core Processor (BogoMIPS: 7199,99) on Ubuntu distro.
|
|
308
309
|
|
|
309
310
|
## 🛠 C++ Standalone Library Usage
|
|
310
311
|
|
|
@@ -70,7 +70,7 @@ inline std::unique_ptr<ProcessResult> trace(const std::string& image_path, const
|
|
|
70
70
|
if (m.flag) internal_args.emplace_back(m.arg);
|
|
71
71
|
}
|
|
72
72
|
internal_args.push_back("--number_of_tiles=" + std::to_string(cfg.tiles));
|
|
73
|
-
if(cfg.connectivity_mode == Connectivity::OMNIDIRECTIONAL) {
|
|
73
|
+
if (cfg.connectivity_mode == Connectivity::OMNIDIRECTIONAL) {
|
|
74
74
|
internal_args.push_back("--connectivity=" + std::to_string(8));
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
#include "Node.h"
|
|
18
18
|
#include "NodeCluster.h"
|
|
19
19
|
|
|
20
|
-
Node::Node(int min_x, int max_x, int y, char name)
|
|
20
|
+
Node::Node(int min_x, int max_x, int y, NodeCluster* cluster, char name)
|
|
21
21
|
: start_point(min_x, y),
|
|
22
|
-
end_point(max_x, y)
|
|
22
|
+
end_point(max_x, y),
|
|
23
|
+
cluster(cluster) {
|
|
23
24
|
this->name = name;
|
|
24
25
|
this->min_x = min_x;
|
|
25
26
|
this->max_x = max_x;
|
|
@@ -67,24 +68,14 @@ void Node::precalc_tangs_sequences(NodeCluster& cluster) {
|
|
|
67
68
|
}
|
|
68
69
|
// --- CLOCKWISE (UP) ---
|
|
69
70
|
for (int upper_pos = this->upper_start; upper_pos <= this->upper_end; upper_pos++) {
|
|
70
|
-
|
|
71
|
-
tangs_sequence.emplace_back(NodeDescriptor{
|
|
72
|
-
&t_node,
|
|
73
|
-
Tangent{ &t_node.end_point, OMAX},
|
|
74
|
-
Tangent{ &t_node.start_point, OMIN}
|
|
75
|
-
});
|
|
71
|
+
tangs_sequence.push_back(-(upper_pos + 1));
|
|
76
72
|
}
|
|
77
73
|
if (this->lower_end >= 0) {
|
|
78
74
|
this->down_indexer = (cluster.vert_nodes[y + T_DOWN][this->lower_start].abs_x_index + lower_size + upper_size - 1);
|
|
79
75
|
}
|
|
80
76
|
// --- COUNTER-CLOCKWISE (DOWN) ---
|
|
81
77
|
for (int lower_pos = this->lower_end; lower_pos >= this->lower_start; lower_pos--) {
|
|
82
|
-
|
|
83
|
-
tangs_sequence.emplace_back(NodeDescriptor{
|
|
84
|
-
&t_node,
|
|
85
|
-
Tangent{&t_node.start_point, OMIN},
|
|
86
|
-
Tangent{&t_node.end_point, OMAX}
|
|
87
|
-
});
|
|
78
|
+
tangs_sequence.push_back(lower_pos);
|
|
88
79
|
}
|
|
89
80
|
this->tangs_count = this->tangs_sequence.size();
|
|
90
81
|
}
|
|
@@ -95,7 +86,7 @@ Node* Node::my_next_inner(Node *last, int versus) {
|
|
|
95
86
|
else last_node_index = this->down_indexer - last->abs_x_index;
|
|
96
87
|
if (versus == Node::O) last_node_index == 0 ? last_node_index = this->tangs_sequence.size() - 1 : last_node_index--;
|
|
97
88
|
else last_node_index == this->tangs_sequence.size() - 1 ? last_node_index = 0 : last_node_index++;
|
|
98
|
-
return(
|
|
89
|
+
return get_tangent_node_by_virtual_index(this->tangs_sequence[last_node_index]);
|
|
99
90
|
}
|
|
100
91
|
|
|
101
92
|
Node* Node::my_next_outer(Node *last, int versus) {
|
|
@@ -104,15 +95,42 @@ Node* Node::my_next_outer(Node *last, int versus) {
|
|
|
104
95
|
else last_node_index = this->down_indexer - last->abs_x_index;
|
|
105
96
|
if (versus == Node::O) last_node_index == this->tangs_sequence.size() - 1 ? last_node_index = 0 : last_node_index++;
|
|
106
97
|
else last_node_index == 0 ? last_node_index = this->tangs_sequence.size() - 1 : last_node_index--;
|
|
107
|
-
return(
|
|
98
|
+
return get_tangent_node_by_virtual_index(this->tangs_sequence[last_node_index]);
|
|
108
99
|
}
|
|
109
100
|
|
|
110
101
|
Point* Node::coords_entering_to(Node *enter_to, int mode, int tracking) {
|
|
111
102
|
int enter_to_index;
|
|
112
103
|
if (enter_to->y < this->y) enter_to_index = enter_to->abs_x_index + this->up_indexer;
|
|
113
104
|
else enter_to_index = this->down_indexer - enter_to->abs_x_index;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
105
|
+
|
|
106
|
+
int tg_index = this->tangs_sequence[enter_to_index];
|
|
107
|
+
Point* point;
|
|
108
|
+
if (tg_index < 0) {
|
|
109
|
+
Node& node_up = cluster->vert_nodes[y + T_UP][-(tg_index + 1)];
|
|
110
|
+
if (mode == Node::A) {
|
|
111
|
+
enter_to->track |= TURNER[tracking][OMAX - 1];
|
|
112
|
+
point = &node_up.end_point;
|
|
113
|
+
} else {
|
|
114
|
+
enter_to->track |= TURNER[tracking][OMIN - 1];
|
|
115
|
+
point = &node_up.start_point;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
Node& node_down = cluster->vert_nodes[y + T_DOWN][tg_index];
|
|
119
|
+
if (mode == Node::A) {
|
|
120
|
+
enter_to->track |= TURNER[tracking][OMIN - 1];
|
|
121
|
+
point = &node_down.start_point;
|
|
122
|
+
} else {
|
|
123
|
+
enter_to->track |= TURNER[tracking][OMAX - 1];
|
|
124
|
+
point = &node_down.end_point;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return point;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Node* Node::get_tangent_node_by_virtual_index(int virtual_index) {
|
|
131
|
+
if (virtual_index < 0) {
|
|
132
|
+
return &(this->cluster->vert_nodes[y + T_UP][-(virtual_index + 1)]);
|
|
133
|
+
} else {
|
|
134
|
+
return &(this->cluster->vert_nodes[y + T_DOWN][virtual_index]);
|
|
135
|
+
}
|
|
118
136
|
}
|
|
@@ -28,11 +28,6 @@ struct Point {
|
|
|
28
28
|
}
|
|
29
29
|
Point(int x_, int y_) : x(x_), y(y_) {}
|
|
30
30
|
};
|
|
31
|
-
struct Tangent {
|
|
32
|
-
Point *point;
|
|
33
|
-
int mode;
|
|
34
|
-
};
|
|
35
|
-
struct NodeDescriptor;
|
|
36
31
|
|
|
37
32
|
class Node : public Listable {
|
|
38
33
|
public:
|
|
@@ -63,24 +58,20 @@ class Node : public Listable {
|
|
|
63
58
|
int lower_start = std::numeric_limits<int>::max();
|
|
64
59
|
int lower_end = -1;
|
|
65
60
|
Point start_point, end_point;
|
|
61
|
+
NodeCluster* cluster;
|
|
66
62
|
void add_intersection(Node& other_node, int other_node_index);
|
|
67
|
-
std::vector<
|
|
63
|
+
std::vector<int> tangs_sequence;
|
|
68
64
|
Point* coords_entering_to(Node *enter_to, int mode, int tracking);
|
|
69
65
|
Node* my_next_outer(Node *last, int versus);
|
|
70
66
|
Node* my_next_inner(Node *last, int versus);
|
|
67
|
+
Node* get_tangent_node_by_virtual_index(int vitual_index);
|
|
71
68
|
bool track_uncomplete();
|
|
72
69
|
bool track_complete();
|
|
73
70
|
bool get_trackmax();
|
|
74
71
|
|
|
75
72
|
public:
|
|
76
73
|
int min_x, max_x;
|
|
77
|
-
Node(int min_x, int max_x, int y, char name);
|
|
74
|
+
Node(int min_x, int max_x, int y, NodeCluster* cluster, char name);
|
|
78
75
|
void precalc_tangs_sequences(NodeCluster& cluster);
|
|
79
76
|
bool processed = false;
|
|
80
77
|
};
|
|
81
|
-
|
|
82
|
-
struct NodeDescriptor {
|
|
83
|
-
Node *node;
|
|
84
|
-
Tangent a;
|
|
85
|
-
Tangent o;
|
|
86
|
-
};
|
|
@@ -75,7 +75,7 @@ void NodeCluster::build_tangs_sequence() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
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, name);
|
|
78
|
+
vert_nodes[y].emplace_back(min_x, max_x, y, this, name);
|
|
79
79
|
|
|
80
80
|
Node& node = vert_nodes[y].back();
|
|
81
81
|
node.abs_x_index = vert_nodes[y].size() - 1;
|
|
@@ -115,8 +115,11 @@ void NodeCluster::plot(int versus) {
|
|
|
115
115
|
this->plot_sequence.push_back(root_node);
|
|
116
116
|
Polygon poly;
|
|
117
117
|
|
|
118
|
-
if ((root_node)->tangs_sequence.size() > 0) // front() on empty list is undefined
|
|
119
|
-
{ 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
|
+
|
|
120
123
|
if (next_node != nullptr)
|
|
121
124
|
{ Point* p = next_node->coords_entering_to(root_node, versus_inverter[versus], Node::OUTER);
|
|
122
125
|
poly.outer.push_back(p);
|
|
@@ -254,7 +257,11 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
|
|
|
254
257
|
|
|
255
258
|
bool plot = true;
|
|
256
259
|
if (next_node->y == last_node->y) {
|
|
257
|
-
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);
|
|
258
265
|
plot = (n == next_node);
|
|
259
266
|
}
|
|
260
267
|
if (plot) {
|
|
@@ -289,7 +296,10 @@ void NodeCluster::plot_node(std::vector<Point*>& sequence_coords, Node *node, No
|
|
|
289
296
|
|
|
290
297
|
bool plot = true;
|
|
291
298
|
if (next_node->y == last_node->y) {
|
|
292
|
-
|
|
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);
|
|
293
303
|
plot = (n == next_node);
|
|
294
304
|
}
|
|
295
305
|
if (plot) {
|
|
@@ -242,7 +242,7 @@ std::vector<Sequence*> Cursor::collect_inner_sequences(Sequence* outer_seq) {
|
|
|
242
242
|
for (Part* part : all_parts)
|
|
243
243
|
{ part->touch();
|
|
244
244
|
retme_sequence->move_from(*part, [&](QNode<Point>* pos) -> bool {
|
|
245
|
-
Position *position =
|
|
245
|
+
Position *position = static_cast<Position*>(pos);
|
|
246
246
|
if (part->is(Part::ADDED) &&
|
|
247
247
|
!(position->payload->y >= bounds.min &&
|
|
248
248
|
position->payload->y <= bounds.max)) {
|
|
@@ -283,6 +283,8 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
|
|
|
283
283
|
for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
|
|
284
284
|
for (Part* dest_part : shape->outer_polyline->parts()) {
|
|
285
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;
|
|
286
288
|
if (dest_part->intersect_part(act_part)) {
|
|
287
289
|
std::vector<EndPoint*> link_seq = duplicates_intersection(*dest_part, *act_part);
|
|
288
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
|
};
|
|
@@ -64,7 +64,7 @@ void Part::touch()
|
|
|
64
64
|
bool Part::intersect_part(Part* other_part)
|
|
65
65
|
{ bool intersect = false;
|
|
66
66
|
other_part->each([&](QNode<Point>* pos) -> bool {
|
|
67
|
-
Position *position =
|
|
67
|
+
Position *position = static_cast<Position*>(pos);
|
|
68
68
|
if (position->end_point()->queues_include(this))
|
|
69
69
|
{ intersect = true;
|
|
70
70
|
return(false);
|
|
@@ -78,7 +78,7 @@ std::vector<EndPoint*> Part::to_endpoints() {
|
|
|
78
78
|
std::vector<EndPoint*> out;
|
|
79
79
|
QNode<Point>* current = head;
|
|
80
80
|
while (current) {
|
|
81
|
-
out.push_back((
|
|
81
|
+
out.push_back((static_cast<Position*>(current))->end_point());
|
|
82
82
|
current = current->next;
|
|
83
83
|
}
|
|
84
84
|
return out;
|
|
@@ -48,7 +48,7 @@ void Partitionable::insert_after(Part* part, Part* new_part) {
|
|
|
48
48
|
|
|
49
49
|
void Partitionable::partition()
|
|
50
50
|
{ this->parts_.clear();
|
|
51
|
-
Polyline *polyline =
|
|
51
|
+
Polyline *polyline = static_cast<Polyline*>(this);
|
|
52
52
|
PartPool& pool = polyline->tile->cluster->parts_pool;
|
|
53
53
|
Part *current_part = nullptr;
|
|
54
54
|
int n = 0;
|
|
@@ -159,7 +159,7 @@ std::optional<SewReturnData> Partitionable::sew(std::vector<std::pair<int, int>>
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
std::vector<Part*> all_parts;
|
|
162
|
-
Polyline* polyline =
|
|
162
|
+
Polyline* polyline = static_cast<Polyline*>(this);
|
|
163
163
|
all_parts.reserve(before_parts.size() + after_parts.size());
|
|
164
164
|
all_parts.insert(all_parts.end(), before_parts.begin(), before_parts.end());
|
|
165
165
|
all_parts.insert(all_parts.end(), after_parts.begin(), after_parts.end());
|
|
@@ -208,9 +208,9 @@ void Partitionable::trasmute_parts()
|
|
|
208
208
|
if (inside == inside_compare || !inside_compare->is(Part::SEAM) ) continue;
|
|
209
209
|
int count = 0;
|
|
210
210
|
inside->each([&](QNode<Point>* pos) -> bool {
|
|
211
|
-
Position *position =
|
|
211
|
+
Position *position = static_cast<Position*>(pos);
|
|
212
212
|
if (position->end_point()->queues_include(inside_compare))
|
|
213
|
-
{ count
|
|
213
|
+
{ count++;
|
|
214
214
|
return true;
|
|
215
215
|
}
|
|
216
216
|
return false;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#include <vector>
|
|
11
11
|
#include <limits>
|
|
12
|
+
#include <string>
|
|
12
13
|
#include <unordered_set>
|
|
13
14
|
#include <sstream>
|
|
14
15
|
#include "Polyline.h"
|
|
@@ -77,7 +78,7 @@ std::vector<std::pair<int, int>> Polyline::intersection(const Polyline* other) c
|
|
|
77
78
|
auto& part = parts_[i];
|
|
78
79
|
if (!part->is(Part::SEAM) && part->trasmuted) continue;
|
|
79
80
|
part->each([&](QNode<Point>* pos) -> bool {
|
|
80
|
-
Position *position =
|
|
81
|
+
Position *position = static_cast<Position*>(pos);
|
|
81
82
|
if (position->end_point() != nullptr)
|
|
82
83
|
{ this->tracked_endpoints[position->end_point()] = i;
|
|
83
84
|
}
|
|
@@ -93,7 +94,7 @@ std::vector<std::pair<int, int>> Polyline::intersection(const Polyline* other) c
|
|
|
93
94
|
continue;
|
|
94
95
|
}
|
|
95
96
|
other_part->each([&](QNode<Point>* pos) -> bool {
|
|
96
|
-
Position *position =
|
|
97
|
+
Position *position = static_cast<Position*>(pos);
|
|
97
98
|
auto it = this->tracked_endpoints.find(position->end_point());
|
|
98
99
|
if (it != this->tracked_endpoints.end()) {
|
|
99
100
|
int self_index = it->second;
|
|
@@ -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
|
}
|
|
@@ -189,6 +189,8 @@ module Contrek
|
|
|
189
189
|
act_part.polyline.next_tile_eligible_shapes.each do |shape|
|
|
190
190
|
shape.outer_polyline.parts.each do |dest_part|
|
|
191
191
|
next if dest_part.trasmuted || dest_part.is?(Part::EXCLUSIVE)
|
|
192
|
+
dest_part_versus = dest_part.versus
|
|
193
|
+
next if dest_part_versus != 0 && dest_part_versus == act_part.versus
|
|
192
194
|
|
|
193
195
|
if dest_part.intersect_part?(act_part)
|
|
194
196
|
link_seq = duplicates_intersection(dest_part, act_part)
|
data/lib/contrek/finder/node.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Contrek
|
|
|
4
4
|
include Listable
|
|
5
5
|
|
|
6
6
|
attr_reader :min_x, :max_x, :y, :name, :tangs_sequence, :tangs_count, :data_pointer,
|
|
7
|
-
:upper_start, :upper_end, :lower_start, :lower_end
|
|
7
|
+
:upper_start, :upper_end, :lower_start, :lower_end, :start_point, :end_point
|
|
8
8
|
attr_accessor :track, :abs_x_index, :outer_index, :inner_index
|
|
9
9
|
|
|
10
10
|
T_UP = -1
|
|
@@ -43,10 +43,21 @@ module Contrek
|
|
|
43
43
|
@upper_end = -1
|
|
44
44
|
@lower_start = Float::INFINITY
|
|
45
45
|
@lower_end = -1
|
|
46
|
-
|
|
46
|
+
@start_point = {x: min_x, y: y}
|
|
47
|
+
@end_point = {x: max_x, y: y}
|
|
48
|
+
@cluster = cluster
|
|
47
49
|
cluster.add_node(self, connectivity_offset)
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
def get_tangent_node_by_virtual_index(virtual_index)
|
|
53
|
+
return nil if virtual_index.nil?
|
|
54
|
+
if virtual_index < 0
|
|
55
|
+
@cluster.vert_nodes[y + T_UP][-(virtual_index + 1)]
|
|
56
|
+
else
|
|
57
|
+
@cluster.vert_nodes[y + T_DOWN][virtual_index]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
50
61
|
def my_next(last, versus, mode)
|
|
51
62
|
last_node_index = if last.y < y
|
|
52
63
|
last.abs_x_index + @up_indexer
|
|
@@ -69,7 +80,7 @@ module Contrek
|
|
|
69
80
|
(last_node_index == tangs_sequence.size - 1) ? last_node_index = 0 : last_node_index += 1
|
|
70
81
|
end
|
|
71
82
|
end
|
|
72
|
-
tangs_sequence.at(last_node_index)
|
|
83
|
+
get_tangent_node_by_virtual_index(@tangs_sequence.at(last_node_index))
|
|
73
84
|
end
|
|
74
85
|
|
|
75
86
|
def coords_entering_to(enter_to, enter_mode, tracking)
|
|
@@ -78,10 +89,28 @@ module Contrek
|
|
|
78
89
|
else
|
|
79
90
|
@down_indexer - enter_to.abs_x_index
|
|
80
91
|
end
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
|
|
93
|
+
tg_index = @tangs_sequence[enter_to_index]
|
|
94
|
+
if tg_index < 0
|
|
95
|
+
node_up = @cluster.vert_nodes[y + T_UP][-(tg_index + 1)]
|
|
96
|
+
if enter_mode == :a
|
|
97
|
+
enter_to.track |= TURNER[tracking][OMAX - 1]
|
|
98
|
+
point = node_up.end_point
|
|
99
|
+
else
|
|
100
|
+
enter_to.track |= TURNER[tracking][OMIN - 1]
|
|
101
|
+
point = node_up.start_point
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
node_down = @cluster.vert_nodes[y + T_DOWN][tg_index]
|
|
105
|
+
if enter_mode == :a
|
|
106
|
+
enter_to.track |= TURNER[tracking][OMIN - 1]
|
|
107
|
+
point = node_down.start_point
|
|
108
|
+
else
|
|
109
|
+
enter_to.track |= TURNER[tracking][OMAX - 1]
|
|
110
|
+
point = node_down.end_point
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
point
|
|
85
114
|
end
|
|
86
115
|
|
|
87
116
|
def track_uncomplete
|
|
@@ -107,29 +136,17 @@ module Contrek
|
|
|
107
136
|
@up_indexer = -cluster.vert_nodes[@y + T_UP][@upper_start].abs_x_index if @upper_end >= 0
|
|
108
137
|
if @upper_end >= 0
|
|
109
138
|
(@upper_start..@upper_end).each do |upper_pos|
|
|
110
|
-
|
|
111
|
-
@tangs_sequence << Contrek::Finder::PolygonFinder::NodeDescriptor.new(
|
|
112
|
-
t_node,
|
|
113
|
-
{point: {x: t_node.max_x, y: t_node.y}, m: OMAX},
|
|
114
|
-
{point: {x: t_node.min_x, y: t_node.y}, m: OMIN}
|
|
115
|
-
)
|
|
139
|
+
@tangs_sequence << -(upper_pos + 1)
|
|
116
140
|
end
|
|
117
141
|
end
|
|
118
|
-
|
|
119
142
|
if @lower_end >= 0
|
|
120
143
|
lower_size = (@lower_end >= 0) ? (@lower_end - @lower_start + 1) : 0
|
|
121
144
|
upper_size = (@upper_end >= 0) ? (@upper_end - @upper_start + 1) : 0
|
|
122
145
|
@down_indexer = (cluster.vert_nodes[@y + T_DOWN][@lower_start].abs_x_index + lower_size + upper_size - 1)
|
|
123
146
|
end
|
|
124
|
-
|
|
125
147
|
if @lower_end >= 0
|
|
126
148
|
@lower_end.downto(@lower_start).each do |lower_pos|
|
|
127
|
-
|
|
128
|
-
@tangs_sequence << Contrek::Finder::PolygonFinder::NodeDescriptor.new(
|
|
129
|
-
t_node,
|
|
130
|
-
{point: {x: t_node.min_x, y: t_node.y}, m: OMIN},
|
|
131
|
-
{point: {x: t_node.max_x, y: t_node.y}, m: OMAX}
|
|
132
|
-
)
|
|
149
|
+
@tangs_sequence << lower_pos
|
|
133
150
|
end
|
|
134
151
|
end
|
|
135
152
|
@tangs_count = tangs_sequence.size
|
|
@@ -62,21 +62,18 @@ module Contrek
|
|
|
62
62
|
bounds = Bounds.empty
|
|
63
63
|
# external polygon
|
|
64
64
|
@plot_sequence << root_node
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
|
|
66
|
+
next_node = if versus == :a
|
|
67
|
+
root_node.get_tangent_node_by_virtual_index(root_node.tangs_sequence.last)
|
|
67
68
|
else
|
|
68
|
-
root_node.tangs_sequence.first
|
|
69
|
+
root_node.get_tangent_node_by_virtual_index(root_node.tangs_sequence.first)
|
|
69
70
|
end
|
|
70
|
-
|
|
71
|
-
if !next_node_nd.nil?
|
|
72
|
-
next_node = next_node_nd.node
|
|
71
|
+
if !next_node.nil?
|
|
73
72
|
coord = next_node.coords_entering_to(root_node, VERSUS_INVERTER[versus], Contrek::Finder::Node::OUTER)
|
|
74
73
|
@sequence_coords << coord
|
|
75
74
|
bounds.expand(x: coord[:x], y: coord[:y])
|
|
76
75
|
end
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
plot_node(next_node, root_node, bounds, versus) if @nodes > 0 && !next_node_nd.nil?
|
|
76
|
+
plot_node(next_node, root_node, bounds, versus) if @nodes > 0 && !next_node.nil?
|
|
80
77
|
|
|
81
78
|
draw_sequence(bitmap, "X") unless bitmap.nil?
|
|
82
79
|
@polygons << {outer: @sequence_coords, inner: [], bounds: (bounds.to_h if @options[:bounds])}.compact if @sequence_coords.size >= 2
|
|
@@ -198,13 +195,13 @@ module Contrek
|
|
|
198
195
|
@root_nodes.delete(node)
|
|
199
196
|
@inner_plot.delete(node)
|
|
200
197
|
last_node = @plot_sequence.last
|
|
201
|
-
|
|
202
|
-
next_node = next_node_nd.node
|
|
198
|
+
next_node = node.my_next(last_node, versus, :inner)
|
|
203
199
|
@plot_sequence << node
|
|
204
200
|
|
|
205
201
|
plot = true
|
|
206
202
|
if next_node.y == last_node.y
|
|
207
|
-
|
|
203
|
+
virtual_index = node.tangs_sequence.send((versus == :a) ? :first : :last)
|
|
204
|
+
plot = (node.get_tangent_node_by_virtual_index(virtual_index) == next_node)
|
|
208
205
|
end
|
|
209
206
|
if plot
|
|
210
207
|
@sequence_coords << last_node.coords_entering_to(node, VERSUS_INVERTER[versus], Contrek::Finder::Node::INNER)
|
|
@@ -231,14 +228,14 @@ module Contrek
|
|
|
231
228
|
|
|
232
229
|
node.outer_index = start_node.outer_index
|
|
233
230
|
last_node = @plot_sequence.last
|
|
234
|
-
|
|
235
|
-
next_node = next_node_nd.node
|
|
231
|
+
next_node = node.my_next(last_node, versus, :outer)
|
|
236
232
|
|
|
237
233
|
@plot_sequence << node
|
|
238
234
|
|
|
239
235
|
plot = true
|
|
240
236
|
if next_node.y == last_node.y
|
|
241
|
-
|
|
237
|
+
virtual_index = node.tangs_sequence.send((versus == :a) ? :last : :first)
|
|
238
|
+
plot = (node.get_tangent_node_by_virtual_index(virtual_index) == next_node)
|
|
242
239
|
end
|
|
243
240
|
|
|
244
241
|
# coord
|
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.1.
|
|
4
|
+
version: 1.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emanuele Cesaroni
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|
|
@@ -279,7 +279,7 @@ metadata:
|
|
|
279
279
|
homepage_uri: https://github.com/runout77/contrek
|
|
280
280
|
documentation_uri: https://github.com/runout77/contrek#readme
|
|
281
281
|
changelog_uri: https://github.com/runout77/contrek/blob/main/CHANGELOG.md
|
|
282
|
-
post_install_message:
|
|
282
|
+
post_install_message:
|
|
283
283
|
rdoc_options: []
|
|
284
284
|
require_paths:
|
|
285
285
|
- lib
|
|
@@ -295,7 +295,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
295
295
|
version: '0'
|
|
296
296
|
requirements: []
|
|
297
297
|
rubygems_version: 3.5.22
|
|
298
|
-
signing_key:
|
|
298
|
+
signing_key:
|
|
299
299
|
specification_version: 4
|
|
300
300
|
summary: Fast PNG contour tracing and shape detection for Ruby
|
|
301
301
|
test_files: []
|