contrek 1.3.3 → 1.3.5
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 +3 -0
- data/Gemfile.lock +3 -1
- data/README.md +28 -28
- data/contrek.gemspec +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +0 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +11 -5
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +8 -10
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +4 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +114 -42
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +66 -116
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -1
- 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 +2 -87
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +1 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/PartPool.cpp +0 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +1 -57
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +1 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +31 -17
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +0 -16
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +0 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.cpp +177 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.h +52 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/RasterReducer.cpp +109 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/RasterReducer.h +32 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.h +6 -1
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +4 -0
- data/lib/contrek/bitmaps/painting.rb +27 -10
- data/lib/contrek/bitmaps/png_bitmap.rb +3 -0
- data/lib/contrek/bitmaps/rendering.rb +75 -0
- data/lib/contrek/finder/concurrent/cursor.rb +62 -84
- data/lib/contrek/finder/concurrent/finder.rb +2 -1
- data/lib/contrek/finder/concurrent/part.rb +4 -77
- data/lib/contrek/finder/concurrent/partitionable.rb +1 -58
- data/lib/contrek/finder/concurrent/queueable.rb +23 -5
- data/lib/contrek/finder/concurrent/sequence.rb +0 -15
- data/lib/contrek/finder/concurrent/tile.rb +1 -1
- data/lib/contrek/finder/node.rb +26 -8
- data/lib/contrek/finder/node_cluster.rb +91 -37
- data/lib/contrek/finder/polygon_finder.rb +1 -1
- data/lib/contrek/reducers/douglas_peucker_reducer.rb +191 -0
- data/lib/contrek/reducers/raster_reducer.rb +72 -0
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +3 -0
- metadata +23 -2
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* DouglasPeuckerReducer.cpp
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "DouglasPeuckerReducer.h"
|
|
11
|
+
#include <cstdint>
|
|
12
|
+
#include <limits>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
DouglasPeuckerReducer::DouglasPeuckerReducer(std::vector<Point>& list_of_points)
|
|
16
|
+
: Reducer(list_of_points) {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
void DouglasPeuckerReducer::reduce() {
|
|
20
|
+
const std::size_t count = points.size();
|
|
21
|
+
if (count < 4) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const std::size_t first_index = extremeIndexByX();
|
|
25
|
+
const std::size_t second_index = farthestIndexFrom(first_index);
|
|
26
|
+
if (second_index == first_index) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
std::vector<bool> keep(count, false);
|
|
30
|
+
keep[first_index] = true;
|
|
31
|
+
keep[second_index] = true;
|
|
32
|
+
simplifyArc(first_index, second_index, keep);
|
|
33
|
+
simplifyArc(second_index, first_index, keep);
|
|
34
|
+
compactRing(keep);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
std::size_t DouglasPeuckerReducer::extremeIndexByX() const {
|
|
38
|
+
std::size_t best = 0;
|
|
39
|
+
int best_x = points[0].x;
|
|
40
|
+
for (std::size_t index = 1; index < points.size(); ++index) {
|
|
41
|
+
const int x = points[index].x;
|
|
42
|
+
if (x < best_x) {
|
|
43
|
+
best_x = x;
|
|
44
|
+
best = index;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return best;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
std::size_t DouglasPeuckerReducer::farthestIndexFrom(std::size_t origin_index) const {
|
|
51
|
+
const Point& origin = points[origin_index];
|
|
52
|
+
std::size_t best_index = origin_index;
|
|
53
|
+
std::int64_t best_distance = -1;
|
|
54
|
+
for (std::size_t index = 0; index < points.size(); ++index) {
|
|
55
|
+
const std::int64_t dx = static_cast<std::int64_t>(points[index].x) - origin.x;
|
|
56
|
+
const std::int64_t dy = static_cast<std::int64_t>(points[index].y) - origin.y;
|
|
57
|
+
const std::int64_t distance = dx * dx + dy * dy;
|
|
58
|
+
if (distance > best_distance) {
|
|
59
|
+
best_distance = distance;
|
|
60
|
+
best_index = index;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return best_index;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
void DouglasPeuckerReducer::simplifyArc(std::size_t first_index, std::size_t last_index, std::vector<bool>& keep) const {
|
|
67
|
+
const std::size_t count = points.size();
|
|
68
|
+
std::vector<Arc> stack;
|
|
69
|
+
stack.push_back({first_index, last_index});
|
|
70
|
+
while (!stack.empty()) {
|
|
71
|
+
const Arc arc = stack.back();
|
|
72
|
+
stack.pop_back();
|
|
73
|
+
std::size_t split_index = 0;
|
|
74
|
+
if (!farthestOnArc(
|
|
75
|
+
arc.first,
|
|
76
|
+
arc.last,
|
|
77
|
+
count,
|
|
78
|
+
split_index)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
keep[split_index] = true;
|
|
82
|
+
if (nextIndex(arc.first, count) != split_index) {
|
|
83
|
+
stack.push_back({arc.first, split_index});
|
|
84
|
+
}
|
|
85
|
+
if (nextIndex(split_index, count) != arc.last) {
|
|
86
|
+
stack.push_back({split_index, arc.last});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
bool DouglasPeuckerReducer::farthestOnArc(std::size_t first_index, std::size_t last_index,
|
|
92
|
+
std::size_t count, std::size_t& split_index) const {
|
|
93
|
+
const Point& first = points[first_index];
|
|
94
|
+
const Point& last = points[last_index];
|
|
95
|
+
const double ax = static_cast<double>(first.x);
|
|
96
|
+
const double ay = static_cast<double>(first.y);
|
|
97
|
+
const double bx = static_cast<double>(last.x);
|
|
98
|
+
const double by = static_cast<double>(last.y);
|
|
99
|
+
const double abx = bx - ax;
|
|
100
|
+
const double aby = by - ay;
|
|
101
|
+
const double length_squared = abx * abx + aby * aby;
|
|
102
|
+
double max_distance = TOLERANCE_SQUARED;
|
|
103
|
+
bool found = false;
|
|
104
|
+
std::size_t index = nextIndex(first_index, count);
|
|
105
|
+
|
|
106
|
+
while (index != last_index) {
|
|
107
|
+
const Point& point = points[index];
|
|
108
|
+
double distance;
|
|
109
|
+
if (length_squared == 0.0) {
|
|
110
|
+
const double dx = static_cast<double>(point.x) - ax;
|
|
111
|
+
const double dy = static_cast<double>(point.y) - ay;
|
|
112
|
+
distance = dx * dx + dy * dy;
|
|
113
|
+
} else {
|
|
114
|
+
distance = pointSegmentDistanceSquared(
|
|
115
|
+
static_cast<double>(point.x),
|
|
116
|
+
static_cast<double>(point.y),
|
|
117
|
+
ax,
|
|
118
|
+
ay,
|
|
119
|
+
bx,
|
|
120
|
+
by,
|
|
121
|
+
abx,
|
|
122
|
+
aby,
|
|
123
|
+
length_squared);
|
|
124
|
+
}
|
|
125
|
+
if (distance > max_distance) {
|
|
126
|
+
max_distance = distance;
|
|
127
|
+
split_index = index;
|
|
128
|
+
found = true;
|
|
129
|
+
}
|
|
130
|
+
index = nextIndex(index, count);
|
|
131
|
+
}
|
|
132
|
+
return found;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
double DouglasPeuckerReducer::pointSegmentDistanceSquared(
|
|
136
|
+
double px,
|
|
137
|
+
double py,
|
|
138
|
+
double ax,
|
|
139
|
+
double ay,
|
|
140
|
+
double bx,
|
|
141
|
+
double by,
|
|
142
|
+
double abx,
|
|
143
|
+
double aby,
|
|
144
|
+
double length_squared) {
|
|
145
|
+
const double apx = px - ax;
|
|
146
|
+
const double apy = py - ay;
|
|
147
|
+
const double dot = apx * abx + apy * aby;
|
|
148
|
+
if (dot <= 0.0) {
|
|
149
|
+
return apx * apx + apy * apy;
|
|
150
|
+
}
|
|
151
|
+
if (dot >= length_squared) {
|
|
152
|
+
const double bpx = px - bx;
|
|
153
|
+
const double bpy = py - by;
|
|
154
|
+
return bpx * bpx + bpy * bpy;
|
|
155
|
+
}
|
|
156
|
+
const double cross = apx * aby - apy * abx;
|
|
157
|
+
return (cross * cross) / length_squared;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
std::size_t DouglasPeuckerReducer::nextIndex(std::size_t index, std::size_t count) {
|
|
161
|
+
return index + 1 == count ? 0 : index + 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
void DouglasPeuckerReducer::compactRing(const std::vector<bool>& keep) {
|
|
165
|
+
std::size_t write_index = 0;
|
|
166
|
+
for (
|
|
167
|
+
std::size_t read_index = 0;
|
|
168
|
+
read_index < points.size();
|
|
169
|
+
++read_index
|
|
170
|
+
) {
|
|
171
|
+
if (keep[read_index]) {
|
|
172
|
+
points[write_index] = points[read_index];
|
|
173
|
+
++write_index;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
points.resize(write_index);
|
|
177
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* DouglasPeuckerReducer.h
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#pragma once
|
|
11
|
+
#include <cstddef>
|
|
12
|
+
#include <vector>
|
|
13
|
+
#include "Reducer.h"
|
|
14
|
+
|
|
15
|
+
struct Point;
|
|
16
|
+
|
|
17
|
+
class DouglasPeuckerReducer : public Reducer {
|
|
18
|
+
public:
|
|
19
|
+
explicit DouglasPeuckerReducer(std::vector<Point>& list_of_points);
|
|
20
|
+
void reduce() override;
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
static constexpr double TOLERANCE_SQUARED = 1.0;
|
|
24
|
+
struct Arc {
|
|
25
|
+
std::size_t first;
|
|
26
|
+
std::size_t last;
|
|
27
|
+
};
|
|
28
|
+
std::size_t extremeIndexByX() const;
|
|
29
|
+
std::size_t farthestIndexFrom(
|
|
30
|
+
std::size_t origin_index) const;
|
|
31
|
+
void simplifyArc(
|
|
32
|
+
std::size_t first_index,
|
|
33
|
+
std::size_t last_index,
|
|
34
|
+
std::vector<bool>& keep) const;
|
|
35
|
+
bool farthestOnArc(
|
|
36
|
+
std::size_t first_index,
|
|
37
|
+
std::size_t last_index,
|
|
38
|
+
std::size_t count,
|
|
39
|
+
std::size_t& split_index) const;
|
|
40
|
+
static double pointSegmentDistanceSquared(
|
|
41
|
+
double px,
|
|
42
|
+
double py,
|
|
43
|
+
double ax,
|
|
44
|
+
double ay,
|
|
45
|
+
double bx,
|
|
46
|
+
double by,
|
|
47
|
+
double abx,
|
|
48
|
+
double aby,
|
|
49
|
+
double length_squared);
|
|
50
|
+
static std::size_t nextIndex(std::size_t index, std::size_t count);
|
|
51
|
+
void compactRing(const std::vector<bool>& keep);
|
|
52
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* RasterReducer.cpp
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "RasterReducer.h"
|
|
11
|
+
#include <stdexcept>
|
|
12
|
+
#include <string>
|
|
13
|
+
|
|
14
|
+
RasterReducer::RasterReducer(std::vector<Point>& points, int versus)
|
|
15
|
+
: Reducer(points),
|
|
16
|
+
versus_(versus) {
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
void RasterReducer::reduce() {
|
|
20
|
+
toRasterPolygon(points, versus_);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
void RasterReducer::toRasterPolygon(std::vector<Point>& points, int versus) {
|
|
24
|
+
const std::size_t n = points.size();
|
|
25
|
+
|
|
26
|
+
if (n == 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const bool outer = versus == Node::O;
|
|
31
|
+
const int firstX = points.front().x;
|
|
32
|
+
const int firstY = points.front().y;
|
|
33
|
+
int previousX = points.back().x;
|
|
34
|
+
int previousY = points.back().y;
|
|
35
|
+
|
|
36
|
+
for (std::size_t i = 0; i < n; ++i) {
|
|
37
|
+
const int currentX = points[i].x;
|
|
38
|
+
const int currentY = points[i].y;
|
|
39
|
+
int nextX;
|
|
40
|
+
int nextY;
|
|
41
|
+
if (i + 1 < n) {
|
|
42
|
+
nextX = points[i + 1].x;
|
|
43
|
+
nextY = points[i + 1].y;
|
|
44
|
+
} else {
|
|
45
|
+
nextX = firstX;
|
|
46
|
+
nextY = firstY;
|
|
47
|
+
}
|
|
48
|
+
const Pole incomingSide = segmentPole(
|
|
49
|
+
currentX - previousX,
|
|
50
|
+
currentY - previousY,
|
|
51
|
+
outer);
|
|
52
|
+
const Pole outgoingSide = segmentPole(
|
|
53
|
+
nextX - currentX,
|
|
54
|
+
nextY - currentY,
|
|
55
|
+
outer);
|
|
56
|
+
int x = currentX;
|
|
57
|
+
int y = currentY;
|
|
58
|
+
const auto applySide = [&x, &y](Pole side) {
|
|
59
|
+
switch (side) {
|
|
60
|
+
case Pole::South:
|
|
61
|
+
--y;
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case Pole::East:
|
|
65
|
+
--x;
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case Pole::North:
|
|
69
|
+
case Pole::West:
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
applySide(incomingSide);
|
|
74
|
+
if (outgoingSide != incomingSide) {
|
|
75
|
+
applySide(outgoingSide);
|
|
76
|
+
}
|
|
77
|
+
points[i].x = x;
|
|
78
|
+
points[i].y = y;
|
|
79
|
+
previousX = currentX;
|
|
80
|
+
previousY = currentY;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
RasterReducer::Pole RasterReducer::segmentPole(int dx, int dy, bool clockwise) {
|
|
85
|
+
const int sx = sign(dx);
|
|
86
|
+
const int sy = sign(dy);
|
|
87
|
+
if (sx == 0 && sy == 1) {
|
|
88
|
+
return clockwise ? Pole::East : Pole::West;
|
|
89
|
+
}
|
|
90
|
+
if (sx == 1 && sy == 0) {
|
|
91
|
+
return clockwise ? Pole::North : Pole::South;
|
|
92
|
+
}
|
|
93
|
+
if (sx == 0 && sy == -1) {
|
|
94
|
+
return clockwise ? Pole::West : Pole::East;
|
|
95
|
+
}
|
|
96
|
+
if (sx == -1 && sy == 0) {
|
|
97
|
+
return clockwise ? Pole::South : Pole::North;
|
|
98
|
+
}
|
|
99
|
+
throw std::invalid_argument(
|
|
100
|
+
"Non-axial segment dx=" +
|
|
101
|
+
std::to_string(dx) +
|
|
102
|
+
" dy=" +
|
|
103
|
+
std::to_string(dy) +
|
|
104
|
+
"!");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
int RasterReducer::sign(int value) {
|
|
108
|
+
return (value > 0) - (value < 0);
|
|
109
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* RasterReducer.h
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#pragma once
|
|
11
|
+
#include <vector>
|
|
12
|
+
#include "Reducer.h"
|
|
13
|
+
|
|
14
|
+
struct Point;
|
|
15
|
+
|
|
16
|
+
class RasterReducer : public Reducer {
|
|
17
|
+
public:
|
|
18
|
+
RasterReducer(std::vector<Point>& points, int versus);
|
|
19
|
+
void reduce() override;
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
enum class Pole {
|
|
23
|
+
North,
|
|
24
|
+
South,
|
|
25
|
+
East,
|
|
26
|
+
West
|
|
27
|
+
};
|
|
28
|
+
int versus_;
|
|
29
|
+
void toRasterPolygon(std::vector<Point>& points, int versus);
|
|
30
|
+
static Pole segmentPole(int dx, int dy, bool clockwise);
|
|
31
|
+
static int sign(int value);
|
|
32
|
+
};
|
|
@@ -25,7 +25,12 @@ class VisvalingamReducer : public Reducer {
|
|
|
25
25
|
class Triangle {
|
|
26
26
|
public:
|
|
27
27
|
static int area(const Point& a, const Point& b, const Point& c) {
|
|
28
|
-
|
|
28
|
+
const int cross = (c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y);
|
|
29
|
+
int half = cross / 2;
|
|
30
|
+
if (cross < 0 && cross % 2 != 0) {
|
|
31
|
+
--half;
|
|
32
|
+
}
|
|
33
|
+
return std::abs(half);
|
|
29
34
|
}
|
|
30
35
|
};
|
|
31
36
|
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
#include "PolygonFinder/src/polygon/reducers/LinearReducer.h"
|
|
53
53
|
#include "PolygonFinder/src/polygon/reducers/VisvalingamReducer.cpp"
|
|
54
54
|
#include "PolygonFinder/src/polygon/reducers/VisvalingamReducer.h"
|
|
55
|
+
#include "PolygonFinder/src/polygon/reducers/RasterReducer.cpp"
|
|
56
|
+
#include "PolygonFinder/src/polygon/reducers/RasterReducer.h"
|
|
57
|
+
#include "PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.cpp"
|
|
58
|
+
#include "PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.h"
|
|
55
59
|
#include "PolygonFinder/src/polygon/finder/concurrent/Finder.h"
|
|
56
60
|
#include "PolygonFinder/src/polygon/finder/concurrent/Finder.cpp"
|
|
57
61
|
#include "PolygonFinder/src/polygon/finder/concurrent/Poolable.h"
|
|
@@ -21,23 +21,40 @@ module Contrek
|
|
|
21
21
|
colors.sort_by { |color, count| -color }
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def self.direct_draw_polygons(polygons, png_image)
|
|
24
|
+
def self.direct_draw_polygons(polygons, png_image, zoom: 1)
|
|
25
25
|
polygons.compact.each do |poly|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
if (bounds = poly[:bounds])
|
|
27
|
+
b_factor = 3.to_f / zoom
|
|
28
|
+
bounds_polygon = [
|
|
29
|
+
{x: bounds[:min_x] - b_factor, y: bounds[:min_y] - b_factor},
|
|
30
|
+
{x: bounds[:max_x] + b_factor, y: bounds[:min_y] - b_factor},
|
|
31
|
+
{x: (bounds[:max_x] + b_factor), y: bounds[:max_y] + b_factor},
|
|
32
|
+
{x: (bounds[:min_x] - b_factor), y: bounds[:max_y] + b_factor}
|
|
33
|
+
]
|
|
34
|
+
draw_polygon(bounds_polygon, png_image, ChunkyPNG::Color("blue @ 1.0"), zoom)
|
|
29
35
|
end
|
|
30
|
-
|
|
36
|
+
color = ChunkyPNG::Color("red @ 1.0")
|
|
37
|
+
draw_polygon(poly[:outer], png_image, color, zoom)
|
|
31
38
|
color = ChunkyPNG::Color("green @ 1.0")
|
|
32
39
|
poly[:inner].each do |sequence|
|
|
33
|
-
|
|
34
|
-
sequence.each_cons(2) do |coords|
|
|
35
|
-
png_image.draw_line(coords[0][:x], coords[0][:y], coords[1][:x], coords[1][:y], color)
|
|
36
|
-
end
|
|
37
|
-
png_image.draw_line(sequence[0][:x], sequence[0][:y], sequence[-1][:x], sequence[-1][:y], color)
|
|
40
|
+
draw_polygon(sequence, png_image, color, zoom)
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
end
|
|
44
|
+
|
|
45
|
+
def self.draw_polygon(sequence, png_image, color, zoom, margin = 0)
|
|
46
|
+
return if sequence.empty?
|
|
47
|
+
sequence.each_cons(2) do |coords|
|
|
48
|
+
png_image.draw_line(coords[0][:x] * zoom,
|
|
49
|
+
coords[0][:y] * zoom,
|
|
50
|
+
coords[1][:x] * zoom,
|
|
51
|
+
coords[1][:y] * zoom, color)
|
|
52
|
+
end
|
|
53
|
+
png_image.draw_line(sequence[0][:x] * zoom,
|
|
54
|
+
sequence[0][:y] * zoom,
|
|
55
|
+
sequence[-1][:x] * zoom,
|
|
56
|
+
sequence[-1][:y] * zoom, color)
|
|
57
|
+
end
|
|
41
58
|
end
|
|
42
59
|
end
|
|
43
60
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Contrek
|
|
4
|
+
module Bitmaps
|
|
5
|
+
module Rendering
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def to_window(result, zoom: 100, coords: true, window_w: 2000, window_h: 1200, font_h: 20)
|
|
12
|
+
require "ruby2d"
|
|
13
|
+
ubuntu_purple = ChunkyPNG::Color.from_hex("#77216F")
|
|
14
|
+
bm = RawBitmap.new(w: window_w, h: window_h, color: ubuntu_purple)
|
|
15
|
+
# grid
|
|
16
|
+
light_purple = ChunkyPNG::Color.from_hex("#95338B")
|
|
17
|
+
(window_w / zoom).times { |x| bm.draw_line(x * zoom, 0, x * zoom, window_h, light_purple) }
|
|
18
|
+
(window_h / zoom).times { |y| bm.draw_line(0, y * zoom, window_w, y * zoom, light_purple) }
|
|
19
|
+
# polygons
|
|
20
|
+
Painting.direct_draw_polygons(result.points, bm, zoom: zoom)
|
|
21
|
+
# windowing
|
|
22
|
+
Tempfile.create do |temp_file|
|
|
23
|
+
bm.image.save(temp_file.path, color_mode: ChunkyPNG::COLOR_TRUECOLOR_ALPHA)
|
|
24
|
+
Window.clear
|
|
25
|
+
Window.set(
|
|
26
|
+
title: "Rendering #{result[:metadata][:groups]} polygons",
|
|
27
|
+
width: window_w,
|
|
28
|
+
height: window_h,
|
|
29
|
+
resizable: true
|
|
30
|
+
)
|
|
31
|
+
Image.new(
|
|
32
|
+
temp_file.path,
|
|
33
|
+
x: 0, y: 0,
|
|
34
|
+
width: window_w,
|
|
35
|
+
height: window_h
|
|
36
|
+
)
|
|
37
|
+
draw_labels(result.points, zoom: zoom, draw_coords: coords, font_h:)
|
|
38
|
+
Window.on :key_down do |event|
|
|
39
|
+
if event.key == "escape"
|
|
40
|
+
Window.close
|
|
41
|
+
end
|
|
42
|
+
if event.key == "s"
|
|
43
|
+
Window.screenshot("window_content.png")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
Window.show
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def draw_labels(polygons, font_h:, zoom: 1.0, draw_coords: true)
|
|
53
|
+
positions = []
|
|
54
|
+
polygons.compact.each do |poly|
|
|
55
|
+
([poly[:outer]] + poly[:inner]).each do |seq|
|
|
56
|
+
seq.each_with_index do |coords, n|
|
|
57
|
+
x = coords[:x] * zoom
|
|
58
|
+
y = coords[:y] * zoom
|
|
59
|
+
y += font_h + 2 if positions.index([x, y])
|
|
60
|
+
t = (n + 1).to_s
|
|
61
|
+
t += " - [#{coords[:x]},#{coords[:y]}]" if draw_coords
|
|
62
|
+
draw_text(x, y, t, font_h) unless draw_coords.nil?
|
|
63
|
+
positions << [x, y]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def draw_text(x, y, text, font_h)
|
|
70
|
+
Text.new(text, x:, y:, size: font_h, color: "white", z: 10)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|