contrek 1.2.5 → 1.2.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.
Files changed (23) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +12 -0
  5. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +20 -12
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +4 -4
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.h +1 -1
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/spng.c +17 -1
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +4 -3
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -0
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +17 -29
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +4 -3
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +2 -2
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +7 -0
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +1 -0
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +4 -0
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp +1 -1
  19. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +9 -6
  20. data/lib/contrek/cpp/cpp_result.rb +3 -2
  21. data/lib/contrek/finder/concurrent/tile.rb +3 -2
  22. data/lib/contrek/version.rb +1 -1
  23. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32d1d662c0c6c0c1dc6ffcde36090b188e7d3abd39fc5dd7ba0887aa62526b27
4
- data.tar.gz: df8b75836abf466043587f22995f12e13c3ed908b60a4fcda97216e803458540
3
+ metadata.gz: ae46fa52ddce3383ef534142814ed28ddc5cae45042e677c8f445140bee67e82
4
+ data.tar.gz: 57cbaf53a0d6e8c7d31bdfcef0718c94654ceaf0fd7b2be84325751dc29f6224
5
5
  SHA512:
6
- metadata.gz: 33c9e4906109bcd7cee694401b3946b0fe2e5800c551bc20a4ea428bdb7cbe7eb456a2e7cadd57327ca93bf65491284e4566b2caaa9e0ea28a5c771c8e50510b
7
- data.tar.gz: ede6984f06ebef2b9672e1f1c0569428b25332e9bb14249a8341fdc1e4f11c493a066015a496bd9d19baec57a97bf98d4d99d23262a4a7dac93757c129e8c441
6
+ metadata.gz: ee922b6b787ac0f91b98a2e7d608c351499ea5405e325a0eb6d23f751a29075b390c08d757824132c7fbef7d64cef9f3af7dbb11709506e84777ff652f9f15fc
7
+ data.tar.gz: 848102a6980857b1f4d3914e60de04408d0f7a4cc4c950a20b3fa8ef7231e31db029220eb6e3fb71847b6986a09c0d7b1fedae70f8e30e8ef758a3adeabeddcd
data/CHANGELOG.md CHANGED
@@ -106,3 +106,13 @@ All notable changes to this project will be documented in this file.
106
106
  ## [1.2.5] - 2026-05-27
107
107
  ### Changed
108
108
  - **Refactored `ProcessResult.clone()`:** Switched from fragmented dynamic allocation to a contiguous `std::vector` with explicit `.reserve()`. Eliminates heap fragmentation during high-res streaming.
109
+
110
+ ## [1.2.6] - 2026-05-31
111
+ ### Changed
112
+ - **Refactored `spng.c` function `rgb8_row_to_rgba8`:** Extended a loop counter to `size_t` (previously limited to `uint32_t`), which was causing segmentation faults when reading massive images (e.g., 81920x81920).
113
+ - **Refactored `RawBitmap.define` function:** Updated area and size calculations to use full 64-bit integers.
114
+ - **Refactored `PolygonFinder.to_svg_stream()` function:** Optimized performance to efficiently handle massive SVG streams of 2 GB and beyond.
115
+
116
+ ## [1.2.7] - 2026-06-02
117
+ ### Changed
118
+ - **Refactored `bounds` option:** Starting from this release, precalculated bounds for each polygon can now be requested in concurrent mode as well, in addition to single-threaded mode.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contrek (1.2.4)
4
+ contrek (1.2.7)
5
5
  chunky_png (~> 1.4)
6
6
  concurrent-ruby (~> 1.3.5)
7
7
  rice (= 4.5.0)
data/README.md CHANGED
@@ -286,6 +286,18 @@ result.points
286
286
  For pure Ruby implementations (`{ native: false }`), coordinates are always expressed as points.
287
287
  In this case, both `polygons` and `points` return the same data, represented as hashes with `x` and `y` keys.
288
288
 
289
+ For each returned polygon, it is possible to get its bounding box. You just need to request it as an option (`bounds`), for example:
290
+ ```ruby
291
+ {named_sequences: true, bounds: true, compress: {uniq: true, linear: true}}
292
+ ```
293
+ Each entry will contain the precalculated bounds key:
294
+ ```ruby
295
+ [{:bounds=>{:min_x=>3, :max_x=>11, :min_y=>1, :max_y=>3},
296
+ :outer=>[{:x=>3, :y=>1}, {:x=>3, :y=>3}, {:x=>11, :y=>3}, {:x=>11, :y=>1}],
297
+ :inner=>[]}]
298
+ ```
299
+
300
+
289
301
  ## Metadata
290
302
 
291
303
  Metadata associated with the result can be accessed via:
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  #include "Tests.h"
11
+ #include <sys/resource.h>
11
12
  #include <string.h>
12
13
  #include <iostream>
13
14
  #include <list>
@@ -17,7 +18,6 @@
17
18
  #include <cstring>
18
19
  #include <algorithm>
19
20
  #include <cstdio>
20
- #include <sys/resource.h>
21
21
 
22
22
  #include "polygon/finder/PolygonFinder.h"
23
23
  #include "polygon/finder/concurrent/ClippedPolygonFinder.h"
@@ -279,7 +279,7 @@ double get_peak_rss() {
279
279
  This approach allows for processing large PNG files on systems where memory
280
280
  would otherwise be insufficient.
281
281
  */
282
- void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
282
+ void stream_png_image(const std::string& filepath, uint32_t stripe_height, bool generate_svg = false, bool generate_png = false) {
283
283
  std::vector<ProcessResult*> result_clones;
284
284
  std::vector<std::string> varguments = {};
285
285
  VerticalMerger vmerger(0, &varguments);
@@ -305,6 +305,7 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
305
305
 
306
306
  // allocates stripe buffer
307
307
  RawBitmap stripe_bitmap;
308
+ std::cout << total_width << " " << stripe_height << std::endl;
308
309
  stripe_bitmap.define(total_width, stripe_height, 4, true);
309
310
  RGBNotMatcher not_matcher(-1);
310
311
 
@@ -315,6 +316,7 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
315
316
  }
316
317
 
317
318
  size_t row_size = static_cast<size_t>(total_width) * 4;
319
+ int stripe_count = 0;
318
320
  // main stripes loop
319
321
  for (uint32_t current_y_offset = 0; current_y_offset < total_height; current_y_offset += stripe_height) {
320
322
  int uncovered_height = total_height - current_y_offset;
@@ -344,12 +346,13 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
344
346
  PolygonFinder polygon_finder(&stripe_bitmap, &not_matcher, nullptr, &finder_arguments);
345
347
  ProcessResult *result = polygon_finder.process_info();
346
348
  if (result) {
347
- std::cout << "Founds polygons: " << result->groups << std::endl;
349
+ std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
348
350
  ProcessResult* safe_result = result->clone();
349
351
  result_clones.push_back(safe_result);
350
352
  vmerger.add_tile(*safe_result);
351
353
  delete result;
352
354
  }
355
+ stripe_count++;
353
356
  }
354
357
 
355
358
  std::cout << "Merging polygons ..." << std::endl;
@@ -357,15 +360,20 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
357
360
 
358
361
  if (merged_result) {
359
362
  std::cout << "Founds total polygons: " << merged_result->groups << std::endl;
360
- /*RawBitmap full_bitmap;
361
- full_bitmap.define(total_width, total_height, 4, true);
362
- full_bitmap.fill(255, 255, 255);
363
- merged_result->draw_on_bitmap(full_bitmap);
364
- std::cout << "Saving whole png ..." << std::endl;
365
- if (full_bitmap.save_to_png("whole.png")) {
366
- std::cout << "Png saved!" << std::endl;
367
- }*/
368
- merged_result->save_svg("whole.svg");
363
+ if (generate_png) {
364
+ RawBitmap full_bitmap;
365
+ full_bitmap.define(total_width, total_height, 4, true);
366
+ full_bitmap.fill(255, 255, 255);
367
+ merged_result->draw_on_bitmap(full_bitmap);
368
+ std::cout << "Saving whole png ..." << std::endl;
369
+ if (full_bitmap.save_to_png("whole.png")) {
370
+ std::cout << "Png saved!" << std::endl;
371
+ }
372
+ }
373
+ if (generate_svg) {
374
+ merged_result->save_svg("whole.svg");
375
+ std::cout << "Svg saved!" << std::endl;
376
+ }
369
377
  }
370
378
  delete merged_result;
371
379
  // frees memory
@@ -56,16 +56,16 @@ void RawBitmap::draw_pixel(int x, int y, unsigned char r, unsigned char g, unsig
56
56
  if (bpp > 3) p[3] = a;
57
57
  }
58
58
 
59
- uint32_t RawBitmap::define(uint width, uint height, int bytes_per_pixel, bool clear)
60
- { this->width = width;
59
+ size_t RawBitmap::define(uint width, uint height, int bytes_per_pixel, bool clear)
60
+ { this->width = width;
61
61
  this->height = height;
62
62
  this->bpp = bytes_per_pixel;
63
- uint32_t dimension = (static_cast<uint32_t>(width) * static_cast<uint32_t>(height)) * bytes_per_pixel;
63
+ size_t dimension = static_cast<size_t>(width) * static_cast<size_t>(height) * static_cast<size_t>(bytes_per_pixel);
64
64
  this->image = std::make_unique<unsigned char[]>(dimension);
65
65
  if (clear) {
66
66
  std::fill(image.get(), image.get() + dimension, 0);
67
67
  }
68
- return dimension;
68
+ return static_cast<uint32_t>(dimension);
69
69
  }
70
70
 
71
71
  void RawBitmap::fill(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
@@ -25,7 +25,7 @@ class RawBitmap : public Bitmap {
25
25
  unsigned int rgb_value_at(int x, int y);
26
26
  const unsigned char* get_row_ptr(int y) const;
27
27
  int get_bytes_per_pixel() const;
28
- uint32_t define(uint width, uint height, int bytes_per_pixel, bool clear = false);
28
+ size_t define(uint width, uint height, int bytes_per_pixel, bool clear = false);
29
29
  void draw_pixel(int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255);
30
30
  bool save_to_png(const std::string& filename);
31
31
  void fill(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255);
@@ -534,7 +534,7 @@ static void u16_row_to_bigendian(void *row, size_t size)
534
534
  write_u16(&px[i], px[i]);
535
535
  }
536
536
  }
537
-
537
+ /*
538
538
  static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n)
539
539
  {
540
540
  uint32_t i;
@@ -543,6 +543,22 @@ static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint
543
543
  memcpy(out + i * 4, row + i * 3, 3);
544
544
  out[i*4+3] = 255;
545
545
  }
546
+ }*/
547
+
548
+ /* Fix overflow Gigapixel images */
549
+ static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n)
550
+ {
551
+ size_t i;
552
+ for(i=0; i < n; i++)
553
+ {
554
+ out[0] = row[0];
555
+ out[1] = row[1];
556
+ out[2] = row[2];
557
+ out[3] = 255;
558
+
559
+ out += 4;
560
+ row += 3;
561
+ }
546
562
  }
547
563
 
548
564
  static unsigned num_channels(const struct spng_ihdr *ihdr)
@@ -108,12 +108,13 @@ void FinderUtils::sanitize_options(pf_Options& options, std::vector<std::string>
108
108
  break;
109
109
  }
110
110
  }
111
- /*std::cout << "-----------" << std::endl;
111
+ /* std::cout << "-----------" << std::endl;
112
112
  std::cout << "versus " << options.versus << std::endl;
113
+ std::cout << "bounds " << options.bounds << std::endl;
113
114
  std::cout << "number_of_tiles " << options.number_of_tiles << std::endl;
114
115
  std::cout << "uniq " << options.compress_uniq << std::endl;
115
116
  std::cout << "linear " << options.compress_linear << std::endl;
116
117
  std::cout << "visvalingam " << options.compress_visvalingam << std::endl;
117
- std::cout << options.compress_visvalingam_tolerance << std::endl;
118
- std::cout << "-----------" << std::endl;*/
118
+ std::cout << "visvalingam tolernace " << options.compress_visvalingam_tolerance << std::endl;
119
+ std::cout << "-----------" << std::endl; */
119
120
  }
@@ -107,6 +107,7 @@ ProcessResult* PolygonFinder::process_info() {
107
107
  pr->treemap = this->node_cluster->treemap;
108
108
  pr->width = this->source_bitmap->w();
109
109
  pr->height = this->source_bitmap->h();
110
+ pr->has_bounds = this->node_cluster->options->bounds;
110
111
 
111
112
  if (this->node_cluster->options->named_sequences && typeid(*this->source_bitmap) == typeid(Bitmap))
112
113
  { std::string sequence;
@@ -59,6 +59,7 @@ struct pf_Options {
59
59
  struct ProcessResult {
60
60
  int groups;
61
61
  int width, height;
62
+ bool has_bounds = false;
62
63
  std::map<std::string, double> benchmarks;
63
64
  std::list<Polygon> polygons;
64
65
  std::string named_sequence;
@@ -147,50 +148,37 @@ struct ProcessResult {
147
148
  return new_res;
148
149
  }
149
150
 
150
- std::string to_svg() const {
151
- std::vector<std::string> lines;
152
- lines.push_back(
153
- "<svg xmlns=\"http://www.w3.org/2000/svg\" "
154
- "width=\"" + std::to_string(width) +
155
- "\" height=\"" + std::to_string(height) + "\">");
151
+ void to_svg_stream(std::ostream& os) const {
152
+ os << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
153
+ << "width=\"" << width << "\" height=\"" << height << "\">\n";
156
154
  for (const auto& poly : polygons) {
157
- { // outer
158
- std::ostringstream pts;
155
+ // --- OUTER (Red) ---
156
+ if (!poly.outer.empty()) {
157
+ os << "<polygon points=\"";
159
158
  bool first = true;
160
159
  for (const Point* p : poly.outer) {
161
160
  if (!p) continue;
162
- if (!first)
163
- pts << " ";
161
+ if (!first) os << " ";
164
162
  first = false;
165
- pts << p->x << "," << p->y;
163
+ os << p->x << "," << p->y;
166
164
  }
167
- lines.push_back(
168
- "<polygon points=\"" + pts.str() +
169
- "\" fill=\"none\" stroke=\"red\" stroke-width=\"1\"/>");
165
+ os << "\" fill=\"none\" stroke=\"red\" stroke-width=\"1\"/>\n";
170
166
  }
171
- // inner
167
+ // --- INNER (Green) ---
172
168
  for (const auto& sequence : poly.inner) {
173
169
  if (sequence.empty()) continue;
174
- std::ostringstream pts;
170
+ os << "<polygon points=\"";
175
171
  bool first = true;
176
172
  for (const Point* p : sequence) {
177
173
  if (!p) continue;
178
- if (!first) pts << " ";
174
+ if (!first) os << " ";
179
175
  first = false;
180
- pts << p->x << "," << p->y;
176
+ os << p->x << "," << p->y;
181
177
  }
182
- lines.push_back(
183
- "<polygon points=\"" + pts.str() +
184
- "\" fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>");
178
+ os << "\" fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>\n";
185
179
  }
186
180
  }
187
- lines.push_back("</svg>");
188
- std::ostringstream result;
189
- for (size_t i = 0; i < lines.size(); ++i) {
190
- result << lines[i];
191
- if (i + 1 < lines.size()) result << "\n";
192
- }
193
- return result.str();
181
+ os << "</svg>";
194
182
  }
195
183
 
196
184
  void save_svg(const std::string& filename) const {
@@ -198,7 +186,7 @@ struct ProcessResult {
198
186
  if (!file.is_open()) {
199
187
  throw std::runtime_error("Unable to open SVG file: " + filename);
200
188
  }
201
- file << to_svg();
189
+ to_svg_stream(file);
202
190
  file.close();
203
191
  }
204
192
  };
@@ -60,7 +60,7 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, std::vec
60
60
  Tile* tile = new Tile(this, payload.tile_start_x, payload.tile_end_x,
61
61
  std::to_string(payload.tile_index), Benchmarks {0, 0});
62
62
  tile->initial_process(finder);
63
- tiles.queue_push(tile);
63
+ tiles_.queue_push(tile);
64
64
  });
65
65
 
66
66
  x = tile_end_x - 1;
@@ -79,7 +79,7 @@ void Finder::process_tiles() {
79
79
  std::vector<Tile*> arriving_tiles;
80
80
 
81
81
  while (true) {
82
- Tile* tile = tiles.queue_pop();
82
+ Tile* tile = tiles_.queue_pop();
83
83
 
84
84
  if (tile->whole()) {
85
85
  this->whole_tile = tile;
@@ -116,7 +116,7 @@ void Finder::process_tiles() {
116
116
  arriving_tiles.erase(it);
117
117
  enqueue(cluster, [this](Cluster* c) {
118
118
  Tile* merged_tile = c->merge_tiles();
119
- tiles.queue_push(merged_tile);
119
+ tiles_.queue_push(merged_tile);
120
120
  delete c;
121
121
  });
122
122
  } else {
@@ -146,6 +146,7 @@ ProcessResult* Finder::process_info() {
146
146
  pr->groups = pr->polygons.size();
147
147
  pr->width = this->maximum_width_;
148
148
  pr->height = this->height;
149
+ pr->has_bounds = this->options_.bounds;
149
150
  FakeCluster fake_cluster(pr->polygons, this->options_);
150
151
  cpu_timer.start();
151
152
  fake_cluster.compress_coords(pr->polygons, this->options_);
@@ -42,7 +42,7 @@ class Finder : public Poolable {
42
42
  CpuTimer cpu_timer;
43
43
 
44
44
  protected:
45
- Queue<Tile*> tiles;
45
+ Queue<Tile*> tiles_;
46
46
  int maximum_width_;
47
47
  int height = 0;
48
48
  void process_tiles();
@@ -55,4 +55,5 @@ class Finder : public Poolable {
55
55
  int maximum_width() const { return maximum_width_; }
56
56
  virtual ProcessResult* process_info();
57
57
  const pf_Options& options() const { return options_; }
58
+ Queue<Tile*>& tiles() { return tiles_; }
58
59
  };
@@ -21,9 +21,9 @@ void Merger::add_tile(ProcessResult& result)
21
21
  this->height = result.height;
22
22
  }
23
23
  int end_x = this->current_x + result.width;
24
- Tile* tile = new Tile(this, this->current_x, end_x, std::to_string(tiles.size()), Benchmarks {0, 0});
24
+ Tile* tile = new Tile(this, this->current_x, end_x, std::to_string(tiles_.size()), Benchmarks {0, 0});
25
25
  tile->assign_raw_polygons(result.polygons, result.treemap);
26
- tiles.queue_push(tile);
26
+ tiles_.queue_push(tile);
27
27
 
28
28
  this->maximum_width_ = end_x;
29
29
  this->current_x = end_x - 1;
@@ -95,3 +95,10 @@ std::string Polyline::named() {
95
95
  return this->named_;
96
96
  }
97
97
  }
98
+
99
+ void Polyline::fill_bounds(RectBounds& target_bounds) const {
100
+ target_bounds.min_x = this->min_x;
101
+ target_bounds.max_x = this->max_x;
102
+ target_bounds.min_y = this->min_y_;
103
+ target_bounds.max_y = this->max_y_;
104
+ }
@@ -52,6 +52,7 @@ class Polyline : public Partitionable {
52
52
  InnerPolyline* inside_inner_polyline = nullptr;
53
53
  std::string named();
54
54
  void set_named(std::string force_named) { this->named_ = force_named; }
55
+ void fill_bounds(RectBounds& target_bounds) const;
55
56
 
56
57
  private:
57
58
  std::vector<Point*> raw_;
@@ -107,10 +107,14 @@ std::string Tile::toString() {
107
107
 
108
108
  std::list<Polygon> Tile::to_raw_polygons()
109
109
  { std::list<Polygon> retme;
110
+ bool bounds = this->finder->options().bounds;
110
111
  for (Shape* s : shapes_)
111
112
  { if (s->outer_polyline && !s->outer_polyline->is_empty())
112
113
  { Polygon poly;
113
114
  poly.outer = s->outer_polyline->raw();
115
+ if (bounds) {
116
+ s->outer_polyline->fill_bounds(poly.bounds);
117
+ }
114
118
  if (!s->inner_polylines.empty()) {
115
119
  for (auto inner : s->inner_polylines) {
116
120
  poly.inner.push_back(inner->raw());
@@ -21,7 +21,7 @@ VerticalMerger::VerticalMerger(int number_of_threads, std::vector<std::string> *
21
21
 
22
22
  void VerticalMerger::add_tile(ProcessResult& result)
23
23
  { transpose(result);
24
- if (this->tiles.size() > 0) {
24
+ if (this->tiles_.size() > 0) {
25
25
  translate(result, this->current_x);
26
26
  }
27
27
  adjust(result);
@@ -172,6 +172,7 @@ class To_Ruby<ProcessResult*>
172
172
  }
173
173
  RubyResult* rr = new RubyResult();
174
174
  Rice::Data_Object<RubyResult> rb_result(rr);
175
+ bool inject_bounds = pr->has_bounds;
175
176
 
176
177
  Rice::Hash return_me = Rice::Hash();
177
178
  Rice::Hash benchmarks_rb;
@@ -206,12 +207,14 @@ class To_Ruby<ProcessResult*>
206
207
  }
207
208
  poly_hash[Symbol("inner")] = inner_collection;
208
209
  // BOUNDS
209
- Rice::Hash bounds_hash = Rice::Hash();
210
- bounds_hash[Symbol("min_x")] = x.bounds.min_x;
211
- bounds_hash[Symbol("max_x")] = x.bounds.max_x;
212
- bounds_hash[Symbol("min_y")] = x.bounds.min_y;
213
- bounds_hash[Symbol("max_y")] = x.bounds.max_y;
214
- poly_hash[Symbol("bounds")] = bounds_hash;
210
+ if (inject_bounds) {
211
+ Rice::Hash bounds_hash = Rice::Hash();
212
+ bounds_hash[Symbol("min_x")] = x.bounds.min_x;
213
+ bounds_hash[Symbol("max_x")] = x.bounds.max_x;
214
+ bounds_hash[Symbol("min_y")] = x.bounds.min_y;
215
+ bounds_hash[Symbol("max_y")] = x.bounds.max_y;
216
+ poly_hash[Symbol("bounds")] = bounds_hash;
217
+ }
215
218
 
216
219
  out.push(poly_hash);
217
220
  }
@@ -19,8 +19,9 @@ module Contrek
19
19
  def points
20
20
  raw_list = polygons.to_a
21
21
  @to_points ||= raw_list.map do |polygon|
22
- {outer: self.class.to_points(polygon[:outer]),
23
- inner: polygon[:inner].map { |s| self.class.to_points(s) }}
22
+ {bounds: (polygon[:bounds] if polygon.key?(:bounds)),
23
+ outer: self.class.to_points(polygon[:outer]),
24
+ inner: polygon[:inner].map { |s| self.class.to_points(s) }}.compact
24
25
  end
25
26
  end
26
27
 
@@ -54,8 +54,9 @@ module Contrek
54
54
  def to_raw_polygons
55
55
  @shapes.filter_map do |shape|
56
56
  unless shape.outer_polyline.empty?
57
- {outer: shape.outer_polyline.raw,
58
- inner: shape.inner_polylines.map(&:raw)}
57
+ {bounds: (shape.outer_polyline.get_bounds if @finder.options[:bounds]),
58
+ outer: shape.outer_polyline.raw,
59
+ inner: shape.inner_polylines.map(&:raw)}.compact
59
60
  end
60
61
  end
61
62
  end
@@ -1,3 +1,3 @@
1
1
  module Contrek
2
- VERSION = "1.2.5"
2
+ VERSION = "1.2.7"
3
3
  end
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.5
4
+ version: 1.2.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-05-27 00:00:00.000000000 Z
11
+ date: 2026-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec