contrek 1.0.6 → 1.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a9bb4f4ff327f04f6877b024c533c8d6d7b6f0ecfc7ad0ae51e6ad05a84438c
4
- data.tar.gz: 9bf8394de257d8a3832400936992d5f848e015d10e389ce986347668d4f53b23
3
+ metadata.gz: d0d1a364ff39a11f632f5f57d99970448173727e8f5a42e84e1b334521c1e1d1
4
+ data.tar.gz: 24a52785c8735e9fce60931a1ffc1bd974aa5a50a6fb755229cd25dd5263cab3
5
5
  SHA512:
6
- metadata.gz: c9640a27737904a3ab5eb4b4f1357262ba05dac579c743ba653ef9be0ed73a90648adc5af0d3c5ff9629f04aa12ca27b5f7e703855805488690d604dd7a82572
7
- data.tar.gz: 656e58e008e3a6b912f5b99ed1b90dbe9b673dab08815177d14ec5d3de3a9380359b70d259517bdd2278b5f327db86bb448293d72555534205751e04a49378b1
6
+ metadata.gz: e321b716a5c16d6c75f24bc6bcdebc302a6ea9785009bad93905321ebc7a97ef29048fb1c6e4e0557611ec9d096dee874bd1ff37f66ad2763c4dacd7176b0ce4
7
+ data.tar.gz: 38608a89ed8ee0c43a985bf8057d07b860acd524d2e8c0022651e5ec3fdcc8f13e3d515e309d436ad261203e00a58b901dd6cf141bfbeeb93520f7937ccdb4b8
data/CHANGELOG.md CHANGED
@@ -18,4 +18,8 @@ All notable changes to this project will be documented in this file.
18
18
  ### Added
19
19
  - Added C++ multithreading supports
20
20
  - Optimized old just present C++ code
21
- - Removed Png++ dependency in place of libspn
21
+ - Removed Png++ dependency in place of libspn
22
+
23
+ ## [1.0.7] - 2026-01-10
24
+ ### Added
25
+ - Optimized C++/Ruby data transfer: Implemented NumPy-compatible binary serialization for coordinate outputs. This reduces serialization overhead and significantly increases data throughput between the C++ core and the Ruby interface.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contrek (1.0.5)
4
+ contrek (1.0.7)
5
5
  chunky_png (~> 1.4)
6
6
  concurrent-ruby (~> 1.3.5)
7
7
  rice (= 4.5.0)
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Contrek
2
- Contrek is a Ruby library (C++ powered) to trace png bitmap areas polygonal contours. Manages png images usign libspng (version 0.7.4) library. May work multithreading.
2
+ Contrek is a Ruby gem with a C++ core for fast contour tracing and edge detection in PNG images. It extracts polygonal contours from bitmap shapes, enabling image processing, shape analysis, and raster-to-vector workflows such as PNG to SVG conversion. Manages png images usign libspng (version 0.7.4) library. May work multithreading.
3
3
 
4
4
  ## About Contrek library
5
5
  Contrek (**con**tour **trek**king) simply scans your png bitmap and returns shape contour as close polygonal lines, both for the external and internal sides. It can compute the nesting level of the polygons found with a tree structure. It supports various levels and modes of compression and approximation of the found coordinates. It is capable of multithreaded processing, splitting the image into vertical strips and recombining the coordinates in pairs.
@@ -34,9 +34,10 @@ result = Contrek.contour!(
34
34
  }
35
35
  )
36
36
  ```
37
- The result reports information about the execution times (microseconds), the polygons found, their coordinates and the nesting tree.
37
+ The resulting metadata information contains the execution times (microseconds), the count of polygons found and the nesting tree map. You can access polygons coordinates too (see later).
38
+
38
39
  ```ruby
39
- {:benchmarks=>{"build_tangs_sequence"=>0.129, "compress"=>0.037, "plot"=>0.198, "scan"=>0.114, "total"=>0.478}, :groups=>2, :named_sequence=>"", :polygons=>[...], :treemap=>[]}
40
+ {:benchmarks=>{"build_tangs_sequence"=>0.129, "compress"=>0.037, "plot"=>0.198, "scan"=>0.114, "total"=>0.478}, :groups=>2, :named_sequence=>"", :treemap=>[]}
40
41
 
41
42
  ```
42
43
 
@@ -63,7 +64,7 @@ polygonfinder = CPPPolygonFinder.new(png_bitmap,
63
64
  {versus: :a, compress: {visvalingam: {tolerance: 1.5}}})
64
65
  result = polygonfinder.process_info
65
66
  # draws the polygons found
66
- Contrek::Bitmaps::Painting.direct_draw_polygons(result[:polygons], png_image)
67
+ Contrek::Bitmaps::Painting.direct_draw_polygons(result.points, png_image)
67
68
  png_image.save('result.png') # => inspect the image to feedback the result
68
69
  ```
69
70
 
@@ -98,7 +99,7 @@ Regarding multithreading:
98
99
 
99
100
  - The treemap option is currently ignored (multithreaded treemap support will be introduced in upcoming revisions).
100
101
 
101
- 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 2 thread working on 2 tiles (total compute time about 2 secs).
102
+ 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 2 threads working on 2 tiles (total compute time about 2 secs).
102
103
 
103
104
  ```ruby
104
105
  result = Contrek.contour!(
@@ -110,7 +111,7 @@ result = Contrek.contour!(
110
111
  finder: {number_of_tiles: 2, compress: {uniq: true}}
111
112
  }
112
113
  )
113
- puts result[:benchmarks].inspect
114
+ puts result.metadata[:benchmarks].inspect
114
115
 
115
116
  {"compress"=>14.596786999999999,
116
117
  "init"=>2078.745861,
@@ -118,6 +119,97 @@ puts result[:benchmarks].inspect
118
119
  "outer"=>118.94489599999999,
119
120
  "total"=>2093.342648}
120
121
  ```
122
+
123
+ ## Result
124
+
125
+ The result structure contains polygon coordinates and a set of metadata. Polygon coordinates can be accessed via:
126
+
127
+ ```ruby
128
+ result.polygons
129
+
130
+ [{:outer=>[11, 2, 11, 5, 6, 5, 6, 2],
131
+ :inner=>[[10, 3, 7, 3, 7, 4, 10, 4]]}]
132
+ ```
133
+
134
+ For native classes (C/C++), coordinates are represented as an interleaved NumPy array (`ndarray`) in the form:
135
+
136
+ ```
137
+ [x0, y0, x1, y1, ...]
138
+ ```
139
+
140
+ A ruby side helper method could converts this data structure into point-based representations:
141
+
142
+ ```ruby
143
+ result.points
144
+
145
+ [{:outer=>[{:x=>11, :y=>2}, {:x=>11, :y=>5}, {:x=>6, :y=>5}, {:x=>6, :y=>2}],
146
+ :inner=>[[{:x=>10, :y=>3}, {:x=>7, :y=>3}, {:x=>7, :y=>4}, {:x=>10, :y=>4}]]}]
147
+ ```
148
+
149
+ - `outer` represents a single polygonal sequence
150
+ - `inner` is represented as an array of polygonal sequences (holes)
151
+
152
+ For pure Ruby implementations (`{ native: false }`), coordinates are always expressed as points.
153
+ In this case, both `polygons` and `points` return the same data, represented as hashes with `x` and `y` keys.
154
+
155
+ ## Metadata
156
+
157
+ Metadata associated with the result can be accessed via:
158
+
159
+ ```ruby
160
+ result.metadata
161
+
162
+ {:benchmarks=>{"build_tangs_sequence"=>0.00577,
163
+ "compress"=>0.01239,
164
+ "plot"=>0.01919,
165
+ "scan"=>0.01473,
166
+ "total"=>0.05208},
167
+ :groups=>1,
168
+ :named_sequence=>"AFEDCBA",
169
+ :treemap=>[]}
170
+ ```
171
+
172
+ ## Treemap
173
+
174
+ The treemap is a data structure that represents the containment hierarchy of polygons. For each polygon, it defines the parent polygon in which it is contained and its relative position among siblings. This structure allows reconstruction of the inclusion tree and determination of nesting relationships between geometries. Consider the above image
175
+
176
+ ```ruby
177
+ "AAAAAAAAAAAAAAAAAAAAAA" \
178
+ "A A" \
179
+ "A BBBBBBBBBBBBBBBBBB A" \
180
+ "A BBBBBBBBBBBBBBBBBB A" \
181
+ "A BBB BBBBB BBB A" \
182
+ "A BBB CC BBBBB D BBB A" \
183
+ "A BBB BBBBB BBB A" \
184
+ "A BBBBBBBBBBBBBBBBBB A" \
185
+ "A BBBBBBBBBBBBBBBBBB A" \
186
+ "A A" \
187
+ "AAAAAAAAAAAAAAAAAAAAAA"
188
+ ```
189
+
190
+ ```ruby
191
+ result.metadata[:treemap]
192
+
193
+ [[-1, -1], # A
194
+ [0, 0], # B
195
+ [1, 0], # C
196
+ [1, 1]] # D
197
+ ```
198
+
199
+ There are four polygons (`A`, `B`, `C`, and `D`).
200
+ The order matches the one provided in `result.polygons`.
201
+
202
+ Each entry has the structure:
203
+
204
+ ```
205
+ [parent_index, position]
206
+ ```
207
+
208
+ - Polygon **A** (index `0`) has no parent and is represented as `[-1, -1]`
209
+ - Polygon **B** is contained in **A** and is its first child → `[0, 0]`
210
+ - Polygon **C** is contained in **B** and is its first child → `[1, 0]`
211
+ - Polygon **D** is also contained in **B** and is its second child → `[1, 1]`
212
+
121
213
  ## Multithreaded approach
122
214
 
123
215
  The multithreaded contour-tracing implementation operates as follows:
@@ -178,6 +270,10 @@ This project is licensed under the terms of the MIT license.
178
270
 
179
271
  See [LICENSE.md](LICENSE.md).
180
272
 
273
+ ## Changelog
274
+
275
+ See [CHANGELOG.md](CHANGELOG.md) for a complete list of changes.
276
+
181
277
  ## History
182
278
  The algorithm was originally developed by me in 2018 when I was commissioned to create a Rails web application whose main objective was to census buildings from GoogleMAPS; the end user had to be able to select their home building by clicking its roof on the map which had to be identified as a clickable polygon. The solution was to configure GoogleMAPS to render buildings of a well-defined color (red), and at each refresh of the same to transform the div into an image (html2canvas) then process it server side returning the polygons to be superimposed again on the map. This required very fast polygons determination. Searching for a library for tracing the contours I was not able to find anything better except OpenCV which however seemed to me a very heavy dependency. So I decided to write my algorithm directly in the context of the ROR application. Once perfected, it was already usable but a bit slow in the higher image resolutions. So I decided to write the counterpart in C++, which came out much faster and which I then used as an extension on Ruby by means of Rice.
183
279
 
@@ -124,6 +124,7 @@ Finder::~Finder() {
124
124
  ProcessResult* Finder::process_info() {
125
125
  ProcessResult *pr = new ProcessResult();
126
126
  pr->polygons = std::move(this->whole_tile->to_raw_polygons());
127
+ pr->groups = pr->polygons.size();
127
128
  FakeCluster fake_cluster(pr->polygons, this->options);
128
129
  cpu_timer.start();
129
130
  fake_cluster.compress_coords(pr->polygons, this->options);
@@ -8,11 +8,11 @@
8
8
 
9
9
  #include <iostream>
10
10
  #include <list>
11
- #include <rice/rice.hpp>
12
- #include <rice/stl.hpp>
13
11
  #include <vector>
14
12
  #include <map>
15
13
  #include <string>
14
+ #include <rice/rice.hpp>
15
+ #include <rice/stl.hpp>
16
16
 
17
17
  #include "PolygonFinder/src/polygon/finder/PolygonFinder.h"
18
18
  #include "PolygonFinder/src/polygon/finder/PolygonFinder.cpp"
@@ -86,142 +86,133 @@ extern "C" {
86
86
  #include "PolygonFinder/src/polygon/bitmaps/spng.h"
87
87
  }
88
88
 
89
- using namespace Rice;
90
-
91
- namespace Rice::detail
92
- { template<>
93
- class To_Ruby<std::vector<Point*>>
94
- { public:
95
- VALUE convert(const std::vector<Point*>& x)
96
- { Rice::Array arr;
97
- for (Point* p : x)
98
- arr.push(p);
99
- return arr;
100
- }
101
- };
102
-
103
- template<>
104
- class To_Ruby<std::list<std::vector<Point*>>>
105
- { public:
106
- VALUE convert(const std::list<std::vector<Point*>>& x)
107
- { Rice::Array arr;
108
- for (const std::vector<Point*>& vec : x)
109
- { arr.push(vec);
110
- }
111
- return arr;
112
- }
113
- };
89
+ class RubyResult {
90
+ public:
91
+ Rice::Array polygons;
92
+ Rice::Hash metadata;
93
+ };
114
94
 
115
- template<>
116
- class To_Ruby<Point*>
117
- { public:
118
- VALUE convert(Point* const & x)
119
- { Rice::Hash h = Rice::Hash();
120
- h[Symbol("x")] = x->x;
121
- h[Symbol("y")] = x->y;
122
- return(h);
123
- }
124
- };
95
+ using namespace Rice;
125
96
 
126
- template<>
127
- class To_Ruby<std::map<std::string, double>*>
128
- { public:
129
- VALUE convert(std::map<std::string, double>* const & x)
130
- { Rice::Hash return_me = Rice::Hash();
131
- std::map<std::string, double>::iterator it;
132
- for ( it = x->begin(); it != x->end(); it++ )
133
- { return_me[String(it->first)] = it->second;
134
- }
135
- return return_me;
136
- }
137
- };
97
+ namespace Rice::detail {
138
98
 
139
- template<>
140
- class From_Ruby<std::vector<std::string>*>
141
- { public:
142
- Convertible is_convertible(VALUE value)
143
- { switch (rb_type(value))
144
- { case RUBY_T_HASH:
145
- return Convertible::Cast;
99
+ template<>
100
+ class From_Ruby<std::vector<std::string>*>
101
+ { public:
102
+ Convertible is_convertible(VALUE value)
103
+ { switch (rb_type(value))
104
+ { case RUBY_T_HASH:
105
+ return Convertible::Cast;
106
+ break;
107
+ default:
108
+ return Convertible::None;
109
+ }
110
+ }
111
+ std::vector<std::string>* convert(VALUE value)
112
+ { std::vector<std::string> *arguments = new std::vector<std::string>();
113
+ if (rb_type(value) == RUBY_T_NIL) return(arguments);
114
+ Rice::Hash hash = (Rice::Hash) value;
115
+ for (Rice::Hash::iterator it = hash.begin(); it != hash.end(); ++it) {
116
+ Rice::String keyString = it->key.to_s();
117
+ Rice::Object value = it->value;
118
+ switch (value.rb_type()) {
119
+ case T_STRING:
120
+ arguments->push_back("--" + keyString.str()+"="+((Rice::String) value).str());
146
121
  break;
147
- default:
148
- return Convertible::None;
149
- }
122
+ case T_SYMBOL:
123
+ arguments->push_back("--" + keyString.str()+"="+((Rice::Symbol) value).str());
124
+ break;
125
+ case T_FLOAT:
126
+ arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2DBL(value.value())));
127
+ break;
128
+ case T_FIXNUM:
129
+ arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2INT(value.value())));
130
+ break;
131
+ case T_TRUE:
132
+ arguments->push_back("--" + keyString.str()+"=true");
133
+ break;
134
+ case T_FALSE:
135
+ arguments->push_back("--" + keyString.str()+"=false");
136
+ break;
137
+ case T_HASH:
138
+ std::vector<std::string>* iv = From_Ruby<std::vector<std::string>*>::convert(value);
139
+ for (std::vector<std::string>::iterator it_iv = iv->begin() ; it_iv != iv->end(); ++it_iv)
140
+ { (*it_iv).replace(0, 2, "_");
141
+ arguments->push_back("--" + keyString.str() + *it_iv);
142
+ }
143
+ break;
144
+ }
150
145
  }
151
- std::vector<std::string>* convert(VALUE value)
152
- { std::vector<std::string> *arguments = new std::vector<std::string>();
153
- if (rb_type(value) == RUBY_T_NIL) return(arguments);
154
- Rice::Hash hash = (Rice::Hash) value;
155
- for (Rice::Hash::iterator it = hash.begin(); it != hash.end(); ++it) {
156
- Rice::String keyString = it->key.to_s();
157
- Rice::Object value = it->value;
158
- switch (value.rb_type()) {
159
- case T_STRING:
160
- arguments->push_back("--" + keyString.str()+"="+((Rice::String) value).str());
161
- break;
162
- case T_SYMBOL:
163
- arguments->push_back("--" + keyString.str()+"="+((Rice::Symbol) value).str());
164
- break;
165
- case T_FLOAT:
166
- arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2DBL(value.value())));
167
- break;
168
- case T_FIXNUM:
169
- arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2INT(value.value())));
170
- break;
171
- case T_TRUE:
172
- arguments->push_back("--" + keyString.str()+"=true");
173
- break;
174
- case T_FALSE:
175
- arguments->push_back("--" + keyString.str()+"=false");
176
- break;
177
- case T_HASH:
178
- std::vector<std::string>* iv = From_Ruby<std::vector<std::string>*>::convert(value);
179
- for (std::vector<std::string>::iterator it_iv = iv->begin() ; it_iv != iv->end(); ++it_iv)
180
- { (*it_iv).replace(0, 2, "_");
181
- arguments->push_back("--" + keyString.str() + *it_iv);
182
- }
183
- break;
184
- }
185
- }
186
- return arguments;
146
+ return arguments;
147
+ }
148
+ };
149
+
150
+ template<>
151
+ class To_Ruby<ProcessResult*>
152
+ {public:
153
+ VALUE convert(ProcessResult* const & pr)
154
+ { if (!pr) {
155
+ return Qnil;
187
156
  }
188
- };
157
+ RubyResult* rr = new RubyResult();
158
+ Rice::Data_Object<RubyResult> rb_result(rr);
189
159
 
190
- template<>
191
- struct Type<ProcessResult>
192
- { static bool verify()
193
- { return true;
160
+ Rice::Hash return_me = Rice::Hash();
161
+ Rice::Hash benchmarks_rb;
162
+ for (auto const& [name, value] : pr->benchmarks) {
163
+ benchmarks_rb[Rice::String(name)] = value;
194
164
  }
195
- };
165
+ return_me[Symbol("benchmarks")] = benchmarks_rb;
166
+ return_me[Symbol("groups")] = pr->groups;
167
+ return_me[Symbol("named_sequence")] = pr->named_sequence;
196
168
 
197
- template<>
198
- class To_Ruby<ProcessResult*>
199
- { public:
200
- VALUE convert(ProcessResult* const & pr)
201
- { Rice::Hash return_me = Rice::Hash();
202
- return_me[Symbol("benchmarks")] = &pr->benchmarks;
203
- return_me[Symbol("groups")] = pr->groups;
204
- return_me[Symbol("named_sequence")] = pr->named_sequence;
205
- Rice::Array out;
206
- for (Polygon& x : pr->polygons)
207
- { Rice::Hash h = Rice::Hash();
208
- h[Symbol("outer")] = x.outer;
209
- h[Symbol("inner")] = x.inner;
210
- out.push(h);
169
+ Rice::Array out;
170
+ for (Polygon& x : pr->polygons)
171
+ { Rice::Hash poly_hash = Rice::Hash();
172
+ // OUTER: std::vector<Point*>
173
+ Rice::Array outer_flat;
174
+ for (Point* p : x.outer) {
175
+ outer_flat.push(p->x);
176
+ outer_flat.push(p->y);
211
177
  }
212
- return_me[Symbol("polygons")] = out;
213
- Rice::Array tmapout;
214
- for (const auto& tm : pr->treemap) {
215
- Rice::Array tmentry;
216
- tmentry.push(tm.first); // outer
217
- tmentry.push(tm.second); // inner
218
- tmapout.push(tmentry);
178
+ poly_hash[Symbol("outer")] = outer_flat;
179
+ // INNER: std::list<std::vector<Point*>>
180
+ Rice::Array inner_collection;
181
+ for (const std::vector<Point*>& sequence : x.inner) {
182
+ Rice::Array sequence_flat;
183
+ for (Point* p : sequence) {
184
+ sequence_flat.push(p->x);
185
+ sequence_flat.push(p->y);
186
+ }
187
+ inner_collection.push(sequence_flat);
219
188
  }
220
- return_me[Symbol("treemap")] = tmapout;
221
- delete pr;
222
- return(return_me);
189
+ poly_hash[Symbol("inner")] = inner_collection;
190
+ out.push(poly_hash);
191
+ }
192
+ rr->polygons = out;
193
+
194
+ // Treemap
195
+ Rice::Array tmapout;
196
+ for (const auto& tm : pr->treemap) {
197
+ Rice::Array tmentry;
198
+ tmentry.push(tm.first);
199
+ tmentry.push(tm.second);
200
+ tmapout.push(tmentry);
223
201
  }
224
- };
202
+ return_me[Symbol("treemap")] = tmapout;
203
+ rr->metadata = return_me;
204
+
205
+ // Protects objects 'out' e 'return_me' linking them to the ruby instance preventing GC
206
+ // garbage collector to free them before the instance itself.
207
+ Rice::Object ruby_obj = rb_result;
208
+ ruby_obj.iv_set("@polygons_storage", out);
209
+ ruby_obj.iv_set("@metadata_storage", return_me);
210
+
211
+ delete pr;
212
+ return rb_result.value();
213
+ }
214
+ };
215
+
225
216
  } // namespace Rice::detail
226
217
 
227
218
  extern "C"
@@ -281,12 +272,18 @@ void Init_cpp_polygon_finder() {
281
272
 
282
273
  Data_Type<PolygonFinder> rb_cPolygonFinder =
283
274
  define_class<PolygonFinder>("CPPPolygonFinder")
284
- .define_constructor(Constructor<PolygonFinder, Bitmap*, Matcher*, Bitmap*, std::vector<std::string>*>(), Arg("bitmap"), Arg("matcher"), Arg("test_bitmap") = nullptr, Arg("options") = nullptr)
275
+ .define_constructor(Constructor<PolygonFinder, Bitmap*, Matcher*, Bitmap*, std::vector<std::string>*>(), Arg("bitmap"), Arg("matcher"), Arg("test_bitmap") = nullptr, Arg("options") = nullptr, Rice::Arg("yield_gvl") = true)
285
276
  .define_method("get_shapelines", &PolygonFinder::get_shapelines)
286
- .define_method("process_info", &PolygonFinder::process_info);
277
+ .define_method("process_info", &PolygonFinder::process_info, Rice::Arg("yield_gvl") = true);
287
278
 
288
279
  Data_Type<Finder> rb_cFinder =
289
280
  define_class<Finder>("CPPFinder")
290
- .define_constructor(Constructor<Finder, int, Bitmap*, Matcher*, std::vector<std::string>*>(), Arg("number_of_threads"), Arg("bitmap"), Arg("matcher"), Arg("options") = nullptr)
291
- .define_method("process_info", &Finder::process_info);
281
+ .define_constructor(Constructor<Finder, int, Bitmap*, Matcher*, std::vector<std::string>*>(), Arg("number_of_threads"), Arg("bitmap"), Arg("matcher"), Arg("options") = nullptr, Rice::Arg("yield_gvl") = true)
282
+ .define_method("process_info", &Finder::process_info, Rice::Arg("yield_gvl") = true);
283
+
284
+ Data_Type<RubyResult> rb_cResult =
285
+ define_class_under<RubyResult>(rb_cFinder, "Result")
286
+ .define_constructor(Constructor<RubyResult>())
287
+ .define_method("polygons", [](RubyResult& rr) { return rr.polygons; })
288
+ .define_method("metadata", [](RubyResult& rr) { return rr.metadata; });
292
289
  }
@@ -74,14 +74,17 @@ module Contrek
74
74
  end
75
75
  end.real
76
76
 
77
- {polygons: raw_polygons,
78
- benchmarks: {
79
- total: ((@initialize_time + compress_time) * 1000).round(3),
80
- init: (@initialize_time * 1000).round(3),
81
- outer: (@whole_tile.benchmarks[:outer] * 1000).round(3),
82
- inner: (@whole_tile.benchmarks[:inner] * 1000).round(3),
83
- compress: ((compress_time * 1000).round(3) if @options.has_key?(:compress))
84
- }.compact}
77
+ metadata = {
78
+ groups: raw_polygons.size,
79
+ benchmarks: {
80
+ total: ((@initialize_time + compress_time) * 1000).round(3),
81
+ init: (@initialize_time * 1000).round(3),
82
+ outer: (@whole_tile.benchmarks[:outer] * 1000).round(3),
83
+ inner: (@whole_tile.benchmarks[:inner] * 1000).round(3),
84
+ compress: ((compress_time * 1000).round(3) if @options.has_key?(:compress))
85
+ }.compact
86
+ }
87
+ Contrek::Finder::Result.new(raw_polygons, metadata)
85
88
  end
86
89
 
87
90
  private
@@ -43,12 +43,14 @@ module Contrek
43
43
 
44
44
  # infos
45
45
  def process_info
46
- {named_sequence: @node_cluster.sequences.map { |list| list.map(&:name).join }.join("-"),
47
- groups: @node_cluster.sequences.size,
48
- groups_names: @node_cluster.root_nodes.map(&:name).join,
49
- polygons: @node_cluster.polygons,
50
- benchmarks: format_benchmarks,
51
- treemap: (@node_cluster.treemap if @options.has_key?(:treemap))}
46
+ metadata = {
47
+ named_sequence: @node_cluster.sequences.map { |list| list.map(&:name).join }.join("-"),
48
+ groups: @node_cluster.sequences.size,
49
+ groups_names: @node_cluster.root_nodes.map(&:name).join,
50
+ benchmarks: format_benchmarks,
51
+ treemap: (@node_cluster.treemap if @options.has_key?(:treemap))
52
+ }
53
+ Result.new(@node_cluster.polygons, metadata)
52
54
  end
53
55
 
54
56
  def get_shapelines
@@ -0,0 +1,13 @@
1
+ module Contrek
2
+ module Finder
3
+ Result = Struct.new(:polygons, :metadata_hash) do
4
+ def points
5
+ polygons
6
+ end
7
+
8
+ def metadata
9
+ metadata_hash
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module Contrek
2
+ module Results
3
+ class CPPFinder::Result
4
+ def points
5
+ raw_list = polygons.to_a
6
+ @to_points ||= raw_list.map do |polygon|
7
+ {outer: self.class.to_points(polygon[:outer]),
8
+ inner: polygon[:inner].map { |s| self.class.to_points(s) }}
9
+ end
10
+ end
11
+
12
+ def total_time
13
+ metadata[:benchmarks].values.sum
14
+ end
15
+
16
+ def self.to_points(flat_polygon)
17
+ flat_polygon.each_slice(2).map { |x, y| {x: x, y: y} }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Contrek
2
- VERSION = "1.0.6"
2
+ VERSION = "1.0.7"
3
3
  end
data/lib/contrek.rb CHANGED
@@ -15,6 +15,7 @@ require "contrek/finder/node"
15
15
  require "contrek/finder/node_cluster"
16
16
  require "contrek/finder/concurrent/fake_cluster"
17
17
  require "contrek/finder/polygon_finder"
18
+ require "contrek/finder/result"
18
19
  require "contrek/finder/concurrent/clipped_polygon_finder"
19
20
  require "contrek/finder/concurrent/hub"
20
21
  require "contrek/finder/concurrent/end_point"
@@ -41,6 +42,7 @@ require "contrek/reducers/uniq_reducer"
41
42
  require "contrek/reducers/visvalingam_reducer"
42
43
  require "cpp_polygon_finder"
43
44
  require "contrek/cpp/cpp_concurrent_finder"
45
+ require "contrek/results/cpp_result"
44
46
 
45
47
  module Contrek
46
48
  class << self
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.0.6
4
+ version: 1.0.7
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-01-06 00:00:00.000000000 Z
11
+ date: 2026-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -256,6 +256,7 @@ files:
256
256
  - lib/contrek/finder/node.rb
257
257
  - lib/contrek/finder/node_cluster.rb
258
258
  - lib/contrek/finder/polygon_finder.rb
259
+ - lib/contrek/finder/result.rb
259
260
  - lib/contrek/map/mercator_projection.rb
260
261
  - lib/contrek/matchers/matcher.rb
261
262
  - lib/contrek/matchers/matcher_hsb.rb
@@ -264,6 +265,7 @@ files:
264
265
  - lib/contrek/reducers/reducer.rb
265
266
  - lib/contrek/reducers/uniq_reducer.rb
266
267
  - lib/contrek/reducers/visvalingam_reducer.rb
268
+ - lib/contrek/results/cpp_result.rb
267
269
  - lib/contrek/version.rb
268
270
  homepage: https://github.com/runout77/contrek
269
271
  licenses: