contrek 1.2.2 → 1.2.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/CHANGELOG.md +8 -1
- data/Gemfile.lock +1 -1
- data/contrek.gemspec +4 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +39 -16
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +4 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +74 -3
- data/lib/contrek/cpp/cpp_result.rb +2 -0
- data/lib/contrek/finder/result.rb +2 -0
- data/lib/contrek/shared/result.rb +23 -0
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 07f6097c256b6d44373aa941b969b12dd68630e168c72b29d1ae478bb5f48186
|
|
4
|
+
data.tar.gz: 7ec31b1ef4413e679e31a123f367d0a86ed332160455c8024ea185ca67c27020
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c03213b7499e5741bf3c90044251a5ba6fc606905f3c72ff93462a62ffc116ebae7ce211fb2a201862d874ae865df048e45cf85beed094bc60967d2b4dd6fc34
|
|
7
|
+
data.tar.gz: ae891e55d9d9ef5503f654d79c7187713579c6fa1c699736d446d6b19497ddb9d30723a26e98a77261481f364466b7d891962e9937433a65e6e341f240129faa
|
data/CHANGELOG.md
CHANGED
|
@@ -91,4 +91,11 @@ All notable changes to this project will be documented in this file.
|
|
|
91
91
|
|
|
92
92
|
## [1.2.2] - 2026-05-20
|
|
93
93
|
### Changed
|
|
94
|
-
- The treemap determination algorithm has been heavily optimized. Calls to the geometric routine that checks whether a newly generated inner polyline encloses other already-existing ones have been reduced to the minimum. Polylines adjacent to the shared overlap stripe are now excluded from these checks, as they are already identified during the initial polygon detection phase. The geometric approach remains unavoidable in this context and is still a performance bottleneck. It will certainly be the subject of future optimizations.
|
|
94
|
+
- The treemap determination algorithm has been heavily optimized. Calls to the geometric routine that checks whether a newly generated inner polyline encloses other already-existing ones have been reduced to the minimum. Polylines adjacent to the shared overlap stripe are now excluded from these checks, as they are already identified during the initial polygon detection phase. The geometric approach remains unavoidable in this context and is still a performance bottleneck. It will certainly be the subject of future optimizations.
|
|
95
|
+
|
|
96
|
+
## [1.2.3] - 2026-05-23
|
|
97
|
+
### Changed
|
|
98
|
+
### Changed
|
|
99
|
+
* **SVG Conversion:** Added utility methods to convert point coordinates directly into SVG paths.
|
|
100
|
+
* **Contrek API & RAII Architecture:** Refactored the Contrek API to utilize an RAII (Resource Acquisition Is Initialization) pattern, safely wrapping both the trace engine and the processing results within a unified context lifecycle shell.
|
|
101
|
+
* **ProcessResult Memory Management:** Updated `ProcessResult` to properly manage resource deallocation during cloning operations, ensuring deep-copied or moved internal points are automatically and safely freed when the context scope ends.
|
data/Gemfile.lock
CHANGED
data/contrek.gemspec
CHANGED
|
@@ -11,10 +11,10 @@ Gem::Specification.new do |s|
|
|
|
11
11
|
s.homepage = "https://github.com/runout77/contrek"
|
|
12
12
|
s.licenses = ["MIT", "AGPL-3.0-only"]
|
|
13
13
|
s.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
|
14
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
15
|
-
f.match(%r{^(docs|pkg|spec)/}) ||
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
15
|
+
f.match(%r{^(docs|pkg|spec)/}) ||
|
|
16
|
+
f.include?("PolygonFinder/images/") ||
|
|
17
|
+
f.include?("PolygonFinder/examples/")
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
s.metadata = {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#include <vector>
|
|
14
14
|
#include <memory>
|
|
15
15
|
#include <cstdint>
|
|
16
|
+
#include <string_view>
|
|
16
17
|
|
|
17
18
|
#include "Finder.h"
|
|
18
19
|
#include "FastPngBitmap.h"
|
|
@@ -43,21 +44,44 @@ struct Config {
|
|
|
43
44
|
Connectivity connectivity_mode = Connectivity::ORTHOGONAL;
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
struct TraceContext {
|
|
48
|
+
std::unique_ptr<FastPngBitmap> bitmap;
|
|
49
|
+
std::unique_ptr<Matcher> matcher;
|
|
50
|
+
std::vector<std::string> internal_args;
|
|
51
|
+
std::unique_ptr<Finder> finder;
|
|
52
|
+
std::unique_ptr<ProcessResult> result;
|
|
53
|
+
|
|
54
|
+
// this allows result direct access
|
|
55
|
+
const ProcessResult* operator->() const { return result.get(); }
|
|
56
|
+
ProcessResult* operator->() { return result.get(); }
|
|
57
|
+
const ProcessResult& operator*() const { return *result; }
|
|
58
|
+
ProcessResult& operator*() { return *result; }
|
|
59
|
+
|
|
60
|
+
// context can be moved not copied
|
|
61
|
+
TraceContext() = default;
|
|
62
|
+
TraceContext(const TraceContext&) = delete;
|
|
63
|
+
TraceContext& operator=(const TraceContext&) = delete;
|
|
64
|
+
TraceContext(TraceContext&&) = default;
|
|
65
|
+
TraceContext& operator=(TraceContext&&) = default;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
inline TraceContext trace(const std::string& image_path, const Config& cfg = Config()) {
|
|
69
|
+
TraceContext ctx;
|
|
70
|
+
|
|
71
|
+
ctx.bitmap = std::make_unique<FastPngBitmap>(image_path);
|
|
48
72
|
|
|
49
73
|
int32_t color_to_match = (cfg.target_color == -1)
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
? ctx.bitmap->rgb_value_at(0, 0)
|
|
75
|
+
: cfg.target_color;
|
|
52
76
|
|
|
53
|
-
std::unique_ptr<Matcher> matcher;
|
|
54
77
|
if (cfg.mode == MatchMode::NOT_COLOR) {
|
|
55
|
-
matcher = std::make_unique<RGBNotMatcher>(color_to_match);
|
|
78
|
+
ctx.matcher = std::make_unique<RGBNotMatcher>(color_to_match);
|
|
56
79
|
} else {
|
|
57
|
-
matcher = std::make_unique<RGBMatcher>(color_to_match);
|
|
80
|
+
ctx.matcher = std::make_unique<RGBMatcher>(color_to_match);
|
|
58
81
|
}
|
|
59
82
|
|
|
60
|
-
|
|
83
|
+
ctx.internal_args = {"--versus=a"};
|
|
84
|
+
|
|
61
85
|
struct Mapping { bool flag; std::string_view arg; };
|
|
62
86
|
const Mapping mappings[] = {
|
|
63
87
|
{cfg.compress_unique, "--compress_uniq"},
|
|
@@ -65,18 +89,17 @@ inline std::unique_ptr<ProcessResult> trace(const std::string& image_path, const
|
|
|
65
89
|
{cfg.compress_visvalingam, "--compress_visvalingam"},
|
|
66
90
|
{cfg.treemap, "--treemap"}
|
|
67
91
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (m.flag) internal_args.emplace_back(m.arg);
|
|
92
|
+
for (const auto& m : mappings) {
|
|
93
|
+
if (m.flag) ctx.internal_args.emplace_back(m.arg);
|
|
71
94
|
}
|
|
72
|
-
internal_args.push_back("--number_of_tiles=" + std::to_string(cfg.tiles));
|
|
95
|
+
ctx.internal_args.push_back("--number_of_tiles=" + std::to_string(cfg.tiles));
|
|
73
96
|
if (cfg.connectivity_mode == Connectivity::OMNIDIRECTIONAL) {
|
|
74
|
-
internal_args.push_back("--connectivity=" + std::to_string(8));
|
|
97
|
+
ctx.internal_args.push_back("--connectivity=" + std::to_string(8));
|
|
75
98
|
}
|
|
99
|
+
ctx.finder = std::make_unique<Finder>(cfg.threads, ctx.bitmap.get(), ctx.matcher.get(), &ctx.internal_args);
|
|
100
|
+
ctx.result = std::unique_ptr<ProcessResult>(ctx.finder->process_info());
|
|
76
101
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return std::unique_ptr<ProcessResult>(finder.process_info());
|
|
102
|
+
return ctx;
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
} // namespace Contrek
|
|
@@ -342,17 +342,18 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
|
342
342
|
|
|
343
343
|
std::cout << "Merging polygons ..." << std::endl;
|
|
344
344
|
ProcessResult *merged_result = vmerger.process_info();
|
|
345
|
-
std::cout << "Founds total polygons: " << merged_result->groups << std::endl;
|
|
346
345
|
|
|
347
346
|
if (merged_result) {
|
|
348
|
-
|
|
347
|
+
std::cout << "Founds total polygons: " << merged_result->groups << std::endl;
|
|
348
|
+
/*RawBitmap full_bitmap;
|
|
349
349
|
full_bitmap.define(total_width, total_height, 4, true);
|
|
350
350
|
full_bitmap.fill(255, 255, 255);
|
|
351
351
|
merged_result->draw_on_bitmap(full_bitmap);
|
|
352
352
|
std::cout << "Saving whole png ..." << std::endl;
|
|
353
353
|
if (full_bitmap.save_to_png("whole.png")) {
|
|
354
354
|
std::cout << "Png saved!" << std::endl;
|
|
355
|
-
}
|
|
355
|
+
}*/
|
|
356
|
+
merged_result->save_svg("whole.svg");
|
|
356
357
|
}
|
|
357
358
|
delete merged_result;
|
|
358
359
|
// frees memory
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
#include <string>
|
|
15
15
|
#include <map>
|
|
16
16
|
#include <iostream>
|
|
17
|
+
#include <memory>
|
|
17
18
|
#include <utility>
|
|
19
|
+
#include <fstream>
|
|
20
|
+
#include <sstream>
|
|
21
|
+
#include <stdexcept>
|
|
22
|
+
#include <limits>
|
|
18
23
|
#include "../bitmaps/Bitmap.h"
|
|
19
24
|
#include "NodeCluster.h"
|
|
20
25
|
#include "Node.h"
|
|
@@ -58,6 +63,8 @@ struct ProcessResult {
|
|
|
58
63
|
std::list<Polygon> polygons;
|
|
59
64
|
std::string named_sequence;
|
|
60
65
|
std::vector<std::pair<int, int>> treemap;
|
|
66
|
+
std::vector<std::unique_ptr<Point>> cloned_points_storage;
|
|
67
|
+
|
|
61
68
|
void draw_on_bitmap(RawBitmap& canvas) const;
|
|
62
69
|
|
|
63
70
|
void print_polygons() {
|
|
@@ -103,14 +110,22 @@ struct ProcessResult {
|
|
|
103
110
|
new_res->named_sequence = this->named_sequence;
|
|
104
111
|
new_res->treemap = this->treemap;
|
|
105
112
|
|
|
113
|
+
size_t estimated_points = 0;
|
|
114
|
+
for (const auto& poly : this->polygons) {
|
|
115
|
+
estimated_points += poly.outer.size();
|
|
116
|
+
for (const auto& seq : poly.inner) estimated_points += seq.size();
|
|
117
|
+
}
|
|
118
|
+
new_res->cloned_points_storage.reserve(estimated_points);
|
|
119
|
+
|
|
106
120
|
for (const auto& poly : this->polygons) {
|
|
107
121
|
Polygon new_poly;
|
|
108
|
-
//
|
|
122
|
+
// bounds
|
|
109
123
|
new_poly.bounds = poly.bounds;
|
|
110
124
|
// outer
|
|
111
125
|
for (const Point* p : poly.outer) {
|
|
112
126
|
if (p) {
|
|
113
|
-
|
|
127
|
+
new_res->cloned_points_storage.push_back(std::make_unique<Point>(p->x, p->y));
|
|
128
|
+
new_poly.outer.push_back(new_res->cloned_points_storage.back().get());
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
131
|
// inner
|
|
@@ -118,7 +133,8 @@ struct ProcessResult {
|
|
|
118
133
|
std::vector<Point*> new_seq;
|
|
119
134
|
for (const Point* p : seq) {
|
|
120
135
|
if (p) {
|
|
121
|
-
|
|
136
|
+
new_res->cloned_points_storage.push_back(std::make_unique<Point>(p->x, p->y));
|
|
137
|
+
new_seq.push_back(new_res->cloned_points_storage.back().get());
|
|
122
138
|
}
|
|
123
139
|
}
|
|
124
140
|
new_poly.inner.push_back(new_seq);
|
|
@@ -127,6 +143,61 @@ struct ProcessResult {
|
|
|
127
143
|
}
|
|
128
144
|
return new_res;
|
|
129
145
|
}
|
|
146
|
+
|
|
147
|
+
std::string to_svg() const {
|
|
148
|
+
std::vector<std::string> lines;
|
|
149
|
+
lines.push_back(
|
|
150
|
+
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
|
151
|
+
"width=\"" + std::to_string(width) +
|
|
152
|
+
"\" height=\"" + std::to_string(height) + "\">");
|
|
153
|
+
for (const auto& poly : polygons) {
|
|
154
|
+
{ // outer
|
|
155
|
+
std::ostringstream pts;
|
|
156
|
+
bool first = true;
|
|
157
|
+
for (const Point* p : poly.outer) {
|
|
158
|
+
if (!p) continue;
|
|
159
|
+
if (!first)
|
|
160
|
+
pts << " ";
|
|
161
|
+
first = false;
|
|
162
|
+
pts << p->x << "," << p->y;
|
|
163
|
+
}
|
|
164
|
+
lines.push_back(
|
|
165
|
+
"<polygon points=\"" + pts.str() +
|
|
166
|
+
"\" fill=\"none\" stroke=\"red\" stroke-width=\"1\"/>");
|
|
167
|
+
}
|
|
168
|
+
// inner
|
|
169
|
+
for (const auto& sequence : poly.inner) {
|
|
170
|
+
if (sequence.empty()) continue;
|
|
171
|
+
std::ostringstream pts;
|
|
172
|
+
bool first = true;
|
|
173
|
+
for (const Point* p : sequence) {
|
|
174
|
+
if (!p) continue;
|
|
175
|
+
if (!first) pts << " ";
|
|
176
|
+
first = false;
|
|
177
|
+
pts << p->x << "," << p->y;
|
|
178
|
+
}
|
|
179
|
+
lines.push_back(
|
|
180
|
+
"<polygon points=\"" + pts.str() +
|
|
181
|
+
"\" fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
lines.push_back("</svg>");
|
|
185
|
+
std::ostringstream result;
|
|
186
|
+
for (size_t i = 0; i < lines.size(); ++i) {
|
|
187
|
+
result << lines[i];
|
|
188
|
+
if (i + 1 < lines.size()) result << "\n";
|
|
189
|
+
}
|
|
190
|
+
return result.str();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
void save_svg(const std::string& filename) const {
|
|
194
|
+
std::ofstream file(filename);
|
|
195
|
+
if (!file.is_open()) {
|
|
196
|
+
throw std::runtime_error("Unable to open SVG file: " + filename);
|
|
197
|
+
}
|
|
198
|
+
file << to_svg();
|
|
199
|
+
file.close();
|
|
200
|
+
}
|
|
130
201
|
};
|
|
131
202
|
|
|
132
203
|
class PolygonFinder {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Contrek
|
|
2
|
+
module Shared
|
|
3
|
+
module Result
|
|
4
|
+
def to_svg
|
|
5
|
+
width = metadata[:width]
|
|
6
|
+
height = metadata[:height]
|
|
7
|
+
lines = []
|
|
8
|
+
lines << %(<svg xmlns="http://www.w3.org/2000/svg" width="#{width}" height="#{height}">)
|
|
9
|
+
points.each do |poly|
|
|
10
|
+
pts = poly[:outer].map { |p| "#{p[:x]},#{p[:y]}" }.join(" ")
|
|
11
|
+
lines << %(<polygon points="#{pts}" fill="none" stroke="red" stroke-width="1"/>)
|
|
12
|
+
poly[:inner].each do |sequence|
|
|
13
|
+
next if sequence.empty?
|
|
14
|
+
pts = sequence.map { |p| "#{p[:x]},#{p[:y]}" }.join(" ")
|
|
15
|
+
lines << %(<polygon points="#{pts}" fill="none" stroke="green" stroke-width="1"/>)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
lines << "</svg>"
|
|
19
|
+
lines.join("\n")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/contrek/version.rb
CHANGED
data/lib/contrek.rb
CHANGED
|
@@ -6,8 +6,9 @@ require "contrek/bitmaps/png_bitmap"
|
|
|
6
6
|
require "contrek/bitmaps/raw_bitmap"
|
|
7
7
|
require "contrek/bitmaps/rgb_color"
|
|
8
8
|
require "contrek/bitmaps/rgb_cpp_color"
|
|
9
|
-
require "contrek/finder/bounds"
|
|
10
9
|
require "contrek/bitmaps/sample_generator"
|
|
10
|
+
require "contrek/shared/result"
|
|
11
|
+
require "contrek/finder/bounds"
|
|
11
12
|
require "contrek/finder/list"
|
|
12
13
|
require "contrek/finder/list_entry"
|
|
13
14
|
require "contrek/finder/listable"
|
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.2.
|
|
4
|
+
version: 1.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emanuele Cesaroni
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|
|
@@ -291,6 +291,7 @@ files:
|
|
|
291
291
|
- lib/contrek/reducers/reducer.rb
|
|
292
292
|
- lib/contrek/reducers/uniq_reducer.rb
|
|
293
293
|
- lib/contrek/reducers/visvalingam_reducer.rb
|
|
294
|
+
- lib/contrek/shared/result.rb
|
|
294
295
|
- lib/contrek/version.rb
|
|
295
296
|
homepage: https://github.com/runout77/contrek
|
|
296
297
|
licenses:
|