contrek 1.1.2 → 1.1.3
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 +6 -1
- data/Gemfile.lock +1 -1
- data/LICENSE-MIT.md +9 -0
- data/README.md +41 -1
- 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 +2 -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 +39 -39
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +10 -10
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +24 -39
- 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 +9 -3
- 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 +33 -0
- 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 +8 -8
- 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 +13 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +2 -0
- data/ext/cpp_polygon_finder/extconf.rb +12 -2
- data/lib/contrek/bitmaps/painting.rb +1 -0
- data/lib/contrek/finder/concurrent/cursor.rb +5 -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 +41 -29
- data/lib/contrek/finder/node_cluster.rb +18 -12
- data/lib/contrek/finder/polygon_finder.rb +4 -3
- data/lib/contrek/version.rb +1 -1
- metadata +8 -7
|
@@ -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
|
}
|
|
@@ -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"
|
|
@@ -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) {
|
|
@@ -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);
|
|
@@ -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
211
|
Position *position = dynamic_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_;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#include <vector>
|
|
11
11
|
#include <limits>
|
|
12
12
|
#include <unordered_set>
|
|
13
|
+
#include <sstream>
|
|
13
14
|
#include "Polyline.h"
|
|
14
15
|
#include "Tile.h"
|
|
15
16
|
#include "Shape.h"
|
|
@@ -114,3 +115,15 @@ void Polyline::clear() {
|
|
|
114
115
|
bool Polyline::is_empty() {
|
|
115
116
|
return raw_.empty();
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
std::string Polyline::info() {
|
|
120
|
+
Shape* shape = this->shape;
|
|
121
|
+
size_t part_index = 0;
|
|
122
|
+
auto it = std::find(this->tile->shapes().begin(), this->tile->shapes().end(), shape);
|
|
123
|
+
if (it != this->tile->shapes().end()) {
|
|
124
|
+
part_index = std::distance(this->tile->shapes().begin(), it);
|
|
125
|
+
}
|
|
126
|
+
std::stringstream ss;
|
|
127
|
+
ss << "b" << this->tile->name() << " S" << part_index;
|
|
128
|
+
return ss.str();
|
|
129
|
+
}
|
|
@@ -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_;
|
|
@@ -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
|
|
@@ -106,8 +106,8 @@ module Contrek
|
|
|
106
106
|
|
|
107
107
|
# rubocop:disable Lint/NonLocalExitFromIterator
|
|
108
108
|
def traverse_outer(act_part, all_parts, shapes_sequence, outer_joined_polyline)
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
last_part = all_parts.last
|
|
110
|
+
all_parts << act_part if last_part != act_part
|
|
111
111
|
if act_part.is?(Part::EXCLUSIVE)
|
|
112
112
|
return if act_part.size == 0
|
|
113
113
|
while (position = act_part.next_position)
|
|
@@ -117,14 +117,14 @@ module Contrek
|
|
|
117
117
|
outer_joined_polyline.add(position)
|
|
118
118
|
end
|
|
119
119
|
else
|
|
120
|
+
return if act_part.dead_end && all_parts.size > 1 && last_part.is?(Part::SEAM) && last_part.polyline == act_part.polyline
|
|
120
121
|
while (new_position = act_part.iterator)
|
|
121
122
|
return if outer_joined_polyline.size > 1 &&
|
|
122
123
|
outer_joined_polyline.head.payload == new_position.payload &&
|
|
123
124
|
act_part == all_parts.first
|
|
124
125
|
outer_joined_polyline.add(Position.new(position: new_position.payload, hub: @cluster.hub))
|
|
125
|
-
|
|
126
126
|
act_part.polyline.next_tile_eligible_shapes.each do |shape|
|
|
127
|
-
if (part = shape.outer_polyline.find_first_part_by_position(new_position))
|
|
127
|
+
if (part = shape.outer_polyline.find_first_part_by_position(new_position, act_part.versus))
|
|
128
128
|
if all_parts[-2] != part
|
|
129
129
|
if all_parts.size >= 2
|
|
130
130
|
map = all_parts[-2..].map(&:type).uniq
|
|
@@ -132,12 +132,12 @@ module Contrek
|
|
|
132
132
|
end
|
|
133
133
|
shapes_sequence.add(part.polyline.shape)
|
|
134
134
|
part.next_position(new_position)
|
|
135
|
+
part.dead_end = true
|
|
135
136
|
traverse_outer(part, all_parts, shapes_sequence, outer_joined_polyline)
|
|
136
137
|
return
|
|
137
138
|
end
|
|
138
139
|
end
|
|
139
140
|
end
|
|
140
|
-
act_part.passes += 1
|
|
141
141
|
act_part.next_position
|
|
142
142
|
end
|
|
143
143
|
end
|
|
@@ -29,6 +29,7 @@ module Contrek
|
|
|
29
29
|
@clusters = []
|
|
30
30
|
@maximum_width = bitmap.w
|
|
31
31
|
@number_of_tiles = options[:number_of_tiles] || (raise "number_of_tiles params is needed!")
|
|
32
|
+
@number_of_tiles = 1 if @number_of_tiles <= 0
|
|
32
33
|
|
|
33
34
|
cw = @maximum_width.to_f / @number_of_tiles
|
|
34
35
|
raise "One pixel tile width minimum!" if cw < 1.0
|
|
@@ -45,7 +46,7 @@ module Contrek
|
|
|
45
46
|
finder = ClippedPolygonFinder.new(
|
|
46
47
|
bitmap: bitmap,
|
|
47
48
|
matcher: matcher,
|
|
48
|
-
options: {versus: current_versus, bounds: true},
|
|
49
|
+
options: {versus: current_versus, bounds: true, connectivity: @options[:connectivity]}.compact,
|
|
49
50
|
start_x: payload[:tile_start_x],
|
|
50
51
|
end_x: payload[:tile_end_x]
|
|
51
52
|
)
|
|
@@ -8,18 +8,19 @@ module Contrek
|
|
|
8
8
|
ADDED = 2
|
|
9
9
|
|
|
10
10
|
attr_reader :polyline, :index, :touched
|
|
11
|
-
attr_accessor :next, :circular_next, :prev, :type, :
|
|
11
|
+
attr_accessor :next, :circular_next, :prev, :type, :dead_end, :inverts, :trasmuted, :delayed, :versus
|
|
12
12
|
def initialize(type, polyline)
|
|
13
13
|
@type = type
|
|
14
14
|
@polyline = polyline
|
|
15
15
|
@next = nil
|
|
16
16
|
@circular_next = nil
|
|
17
17
|
@prev = nil
|
|
18
|
-
@
|
|
18
|
+
@dead_end = false
|
|
19
19
|
@touched = false
|
|
20
20
|
@inverts = false
|
|
21
21
|
@trasmuted = false
|
|
22
22
|
@delayed = false
|
|
23
|
+
@versus = 0
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def is?(type)
|
|
@@ -60,7 +61,7 @@ module Contrek
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
def inspect
|
|
63
|
-
"part #{polyline.parts.index(self)} (inv=#{@inverts} trm=#{@trasmuted} touched=#{@touched}
|
|
64
|
+
"part #{polyline.parts.index(self)} (versus=#{@versus} inv=#{@inverts} trm=#{@trasmuted} touched=#{@touched} dead_end =#{@dead_end}, #{size}x) of #{polyline.info} (#{name}) (#{to_a.map { |e| "[#{e[:x]},#{e[:y]}]" }.join})"
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
def innerable?
|
|
@@ -78,6 +79,14 @@ module Contrek
|
|
|
78
79
|
map(&:end_point)
|
|
79
80
|
end
|
|
80
81
|
|
|
82
|
+
def orient!
|
|
83
|
+
@versus = if size <= 1
|
|
84
|
+
0
|
|
85
|
+
else
|
|
86
|
+
(tail.payload[:y] - head.payload[:y]).positive? ? 1 : -1
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
81
90
|
def self.remove_adjacent_pairs(array = nil)
|
|
82
91
|
n = array.size
|
|
83
92
|
(0...(n - 1)).each do |i|
|
|
@@ -14,6 +14,8 @@ module Contrek
|
|
|
14
14
|
last.next = last.circular_next = new_part if last
|
|
15
15
|
new_part.circular_next = @parts.first
|
|
16
16
|
new_part.prev = last
|
|
17
|
+
|
|
18
|
+
new_part.orient! if new_part.is?(Part::SEAM)
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def insert_after(part, new_part)
|
|
@@ -25,10 +27,10 @@ module Contrek
|
|
|
25
27
|
part.next = part.circular_next = new_part
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
def find_first_part_by_position(position)
|
|
30
|
+
def find_first_part_by_position(position, versus)
|
|
29
31
|
@parts.find do |part|
|
|
30
32
|
part.is?(Part::SEAM) &&
|
|
31
|
-
part.
|
|
33
|
+
part.versus == -versus &&
|
|
32
34
|
position.end_point.queues.include?(part)
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -144,10 +146,10 @@ module Contrek
|
|
|
144
146
|
|
|
145
147
|
count = 0
|
|
146
148
|
inside.each do |position|
|
|
147
|
-
|
|
148
|
-
count += 1
|
|
149
|
+
inclusion = position.end_point.queues.include?(inside_compare)
|
|
150
|
+
count += 1 if inclusion
|
|
149
151
|
end
|
|
150
|
-
if count == inside.size
|
|
152
|
+
if count == inside.size && count < inside_compare.size
|
|
151
153
|
inside.type = Part::EXCLUSIVE
|
|
152
154
|
inside.trasmuted = true
|
|
153
155
|
break
|
data/lib/contrek/finder/node.rb
CHANGED
|
@@ -3,7 +3,8 @@ module Contrek
|
|
|
3
3
|
class Node
|
|
4
4
|
include Listable
|
|
5
5
|
|
|
6
|
-
attr_reader :min_x, :max_x, :y, :name, :
|
|
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
8
|
attr_accessor :track, :abs_x_index, :outer_index, :inner_index
|
|
8
9
|
|
|
9
10
|
T_UP = -1
|
|
@@ -24,12 +25,11 @@ module Contrek
|
|
|
24
25
|
OUTER = 0
|
|
25
26
|
INNER = 1
|
|
26
27
|
|
|
27
|
-
def initialize(cluster, min_x, max_x, y, name)
|
|
28
|
+
def initialize(cluster, min_x, max_x, y, name, connectivity_offset = 0)
|
|
28
29
|
@name = name
|
|
29
30
|
@min_x = min_x
|
|
30
31
|
@max_x = max_x
|
|
31
32
|
@y = y
|
|
32
|
-
@tangs = {T_UP => [], T_DOWN => []}
|
|
33
33
|
@tangs_sequence = nil
|
|
34
34
|
@tangs_count = 0
|
|
35
35
|
@track = 0
|
|
@@ -39,11 +39,12 @@ module Contrek
|
|
|
39
39
|
@down_indexer = 0
|
|
40
40
|
@outer_index = -1
|
|
41
41
|
@inner_index = -1
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
@upper_start = Float::INFINITY
|
|
43
|
+
@upper_end = -1
|
|
44
|
+
@lower_start = Float::INFINITY
|
|
45
|
+
@lower_end = -1
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
@min_x <= node.max_x && node.min_x <= @max_x
|
|
47
|
+
cluster.add_node(self, connectivity_offset)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def my_next(last, versus, mode)
|
|
@@ -62,13 +63,11 @@ module Contrek
|
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
when :inner
|
|
65
|
-
|
|
66
66
|
if versus == :o
|
|
67
67
|
(last_node_index == 0) ? last_node_index = tangs_sequence.size - 1 : last_node_index -= 1
|
|
68
68
|
else
|
|
69
69
|
(last_node_index == tangs_sequence.size - 1) ? last_node_index = 0 : last_node_index += 1
|
|
70
70
|
end
|
|
71
|
-
|
|
72
71
|
end
|
|
73
72
|
tangs_sequence.at(last_node_index)
|
|
74
73
|
end
|
|
@@ -85,10 +84,6 @@ module Contrek
|
|
|
85
84
|
coords_source[:point]
|
|
86
85
|
end
|
|
87
86
|
|
|
88
|
-
def tangs_with_x?(x)
|
|
89
|
-
x.between?(@min_x, @max_x)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
87
|
def track_uncomplete
|
|
93
88
|
(@track & OCOMPLETE) != OCOMPLETE
|
|
94
89
|
end
|
|
@@ -97,28 +92,45 @@ module Contrek
|
|
|
97
92
|
(@track & OCOMPLETE) == OCOMPLETE
|
|
98
93
|
end
|
|
99
94
|
|
|
100
|
-
def add_intersection(other_node)
|
|
95
|
+
def add_intersection(other_node, other_node_index)
|
|
101
96
|
if other_node.y < y
|
|
102
|
-
@
|
|
97
|
+
@upper_start = other_node_index if other_node_index < @upper_start
|
|
98
|
+
@upper_end = other_node_index if other_node_index > @upper_end
|
|
103
99
|
else
|
|
104
|
-
@
|
|
100
|
+
@lower_start = other_node_index if other_node_index < @lower_start
|
|
101
|
+
@lower_end = other_node_index if other_node_index > @lower_end
|
|
105
102
|
end
|
|
106
103
|
end
|
|
107
104
|
|
|
108
|
-
def precalc_tangs_sequences
|
|
109
|
-
@tangs_sequence =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
def precalc_tangs_sequences(cluster:)
|
|
106
|
+
@tangs_sequence = []
|
|
107
|
+
@up_indexer = -cluster.vert_nodes[@y + T_UP][@upper_start].abs_x_index if @upper_end >= 0
|
|
108
|
+
if @upper_end >= 0
|
|
109
|
+
(@upper_start..@upper_end).each do |upper_pos|
|
|
110
|
+
t_node = cluster.vert_nodes[@y + T_UP][upper_pos]
|
|
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
|
+
)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if @lower_end >= 0
|
|
120
|
+
lower_size = (@lower_end >= 0) ? (@lower_end - @lower_start + 1) : 0
|
|
121
|
+
upper_size = (@upper_end >= 0) ? (@upper_end - @upper_start + 1) : 0
|
|
122
|
+
@down_indexer = (cluster.vert_nodes[@y + T_DOWN][@lower_start].abs_x_index + lower_size + upper_size - 1)
|
|
116
123
|
end
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
|
|
125
|
+
if @lower_end >= 0
|
|
126
|
+
@lower_end.downto(@lower_start).each do |lower_pos|
|
|
127
|
+
t_node = cluster.vert_nodes[@y + T_DOWN][lower_pos]
|
|
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
|
+
)
|
|
133
|
+
end
|
|
122
134
|
end
|
|
123
135
|
@tangs_count = tangs_sequence.size
|
|
124
136
|
end
|