contrek 1.0.6 → 1.0.8

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +102 -6
  5. data/ext/cpp_polygon_finder/PolygonFinder/Makefile +4 -4
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +5 -4
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +3 -5
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -1
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +2 -2
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +1 -1
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +28 -8
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +12 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.cpp +5 -5
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.h +11 -7
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +2 -0
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +108 -66
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +5 -3
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +33 -28
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +5 -1
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -3
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +15 -20
  23. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +128 -131
  24. data/lib/contrek/bitmaps/rgb_color.rb +0 -15
  25. data/lib/contrek/bitmaps/rgb_cpp_color.rb +10 -0
  26. data/lib/contrek/finder/concurrent/cluster.rb +2 -2
  27. data/lib/contrek/finder/concurrent/cursor.rb +32 -15
  28. data/lib/contrek/finder/concurrent/finder.rb +23 -9
  29. data/lib/contrek/finder/concurrent/hub.rb +2 -2
  30. data/lib/contrek/finder/concurrent/part.rb +6 -1
  31. data/lib/contrek/finder/concurrent/partitionable.rb +61 -33
  32. data/lib/contrek/finder/concurrent/polyline.rb +44 -2
  33. data/lib/contrek/finder/concurrent/queueable.rb +12 -20
  34. data/lib/contrek/finder/polygon_finder.rb +8 -6
  35. data/lib/contrek/finder/result.rb +13 -0
  36. data/lib/contrek/results/cpp_result.rb +21 -0
  37. data/lib/contrek/version.rb +1 -1
  38. data/lib/contrek.rb +6 -3
  39. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a9bb4f4ff327f04f6877b024c533c8d6d7b6f0ecfc7ad0ae51e6ad05a84438c
4
- data.tar.gz: 9bf8394de257d8a3832400936992d5f848e015d10e389ce986347668d4f53b23
3
+ metadata.gz: 862b863c602031251fedc2dc18a59c9bd270d91acff25761c24164e34e8952e9
4
+ data.tar.gz: 97f18e804accd2d6ae11f95223c95eebe198eafb07c745261d1110f5edf5eae4
5
5
  SHA512:
6
- metadata.gz: c9640a27737904a3ab5eb4b4f1357262ba05dac579c743ba653ef9be0ed73a90648adc5af0d3c5ff9629f04aa12ca27b5f7e703855805488690d604dd7a82572
7
- data.tar.gz: 656e58e008e3a6b912f5b99ed1b90dbe9b673dab08815177d14ec5d3de3a9380359b70d259517bdd2278b5f327db86bb448293d72555534205751e04a49378b1
6
+ metadata.gz: d3d9ce7c0b5ae1a3c62f91480d63856312d89334f181899ccc2ff304c3fef983578838484da18f865ce1d50ea3cb3c291ce49fef1b01dcfaf397ab688bed9e37
7
+ data.tar.gz: c8f7aade05577e132829e7ffcc6af5e637aa879bb6972f4ba8566c8db7d11b641862e32a170c39db4b8ab57666996a4639ffd3f516ba85712266b7ffd82ac276
data/CHANGELOG.md CHANGED
@@ -18,4 +18,14 @@ 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.
26
+
27
+ ## [1.0.8] - 2026-01-25
28
+ ### Changed
29
+ - Fixed ARGB/RGBA format discrepancies between Ruby and C++.
30
+ - Updated the multithreading-side algorithm for rejoining mono-connected orphan polygons to other orphan polygons. The algorithm can now identify them and defer their processing within the pipeline.
31
+ - Improved, on the multithreading side, the polygon intersection detection mechanism; the geometric approach has been dropped in favor of a purely topological one.
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.8)
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. Unlike standard geometric tracers, it employs a **topological approach** to extract polygonal contours, representing shapes as a connected graph of shared endpoints. This ensures perfect adjacency and structural integrity for shape analysis and raster-to-vector workflows, such as PNG to SVG conversion, managed via libspng (0.7.4) with multithreading support.
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
 
@@ -2,15 +2,15 @@ CXX = g++
2
2
  CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g \
3
3
  -fno-omit-frame-pointer -fsanitize=address,undefined \
4
4
  -O0
5
- LDFLAGS = -lpng -pthread -lz -fsanitize=address,undefined
5
+ LDFLAGS = -pthread -lz -fsanitize=address,undefined
6
6
 
7
7
  # valgrinf or perf setup
8
- #CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g -fno-omit-frame-pointer -march=native -DNDEBUG -O3
9
- #LDFLAGS = -lpng -pthread -lz
8
+ # CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g -fno-omit-frame-pointer -march=native -DNDEBUG -O3
9
+ # LDFLAGS = -lpng -pthread -lz
10
10
 
11
11
  # best performances
12
12
  CXXFLAGS = -Wall -Wextra -std=c++17 -Isrc -MMD -MP -pthread -g -march=native -DNDEBUG -Ofast -flto
13
- LDFLAGS = -lpng -pthread -lz -flto=auto
13
+ LDFLAGS = -pthread -lz -flto=auto
14
14
 
15
15
  SRC = $(shell find src -name '*.cpp')
16
16
  C_SRC = $(shell find src -name 'spng.c')
@@ -107,7 +107,7 @@ void Tests::test_c()
107
107
  Point* p2 = new Point({2, 2});
108
108
  Point* p3 = new Point({3, 3});
109
109
 
110
- Hub* hub = new Hub(4, 4); // coordinate 0 -> 3 sia x che y, matrice di 4 * 4
110
+ Hub* hub = new Hub(4, 0, 3);
111
111
 
112
112
  Position* pos1 = new Position(hub, p1);
113
113
  Position* pos2 = new Position(hub, p2);
@@ -176,15 +176,16 @@ void Tests::test_e()
176
176
  cpu_timer.start();
177
177
  FastPngBitmap png_bitmap("images/sample_10240x10240.png");
178
178
  // FastPngBitmap png_bitmap("images/sample_1024x1024.png");
179
- std::cout << "immagine =" << cpu_timer.stop() << std::endl;
179
+ std::cout << "image reading time =" << cpu_timer.stop() << std::endl;
180
180
 
181
- int color = png_bitmap.value_at(0, 0);
182
- std::cout << "color =" << color << std::endl;
181
+ int color = png_bitmap.rgb_value_at(0, 0);
182
+ std::cout << "color = " << color << std::endl;
183
183
  RGBNotMatcher not_matcher(color);
184
184
 
185
185
  std::vector<std::string> arguments = {"--versus=a", "--compress_uniq", "--number_of_tiles=2"};
186
186
  Finder pl(2, &png_bitmap, &not_matcher, &arguments);
187
187
  ProcessResult *o = pl.process_info();
188
188
  o->print_info();
189
+ std::cout << "polygons =" << o->groups << std::endl;
189
190
  delete o;
190
191
  }
@@ -70,12 +70,10 @@ char FastPngBitmap::value_at(int x, int y) {
70
70
  return(0);
71
71
  }
72
72
 
73
+ // source image format RGBA => returning uint ABGR
73
74
  unsigned int FastPngBitmap::rgb_value_at(int x, int y) {
74
- int32_t index = ((y * width) + x) * 4;
75
- unsigned int color;
76
- unsigned char *red = &image[index];
77
- std::memcpy(&color, red, 3);
78
- return(color);
75
+ uint32_t index = (uint32_t(y) * width + x) * 4;
76
+ return *reinterpret_cast<const uint32_t*>(&image[index]);
79
77
  }
80
78
 
81
79
  const unsigned char* FastPngBitmap::get_row_ptr(int y) const {
@@ -89,7 +89,7 @@ void PolygonFinder::scan() {
89
89
  if (rgb_m) run_loop(rgb_m, fetcher);
90
90
  else run_loop(this->matcher, fetcher);
91
91
  } else {
92
- auto fetcher = [](const unsigned char* p) { return p[0] | (p[1] << 8) | (p[2] << 16); };
92
+ auto fetcher = [](const unsigned char* p) { return *reinterpret_cast<const uint32_t*>(p); };
93
93
  if (rgb_m) run_loop(rgb_m, fetcher);
94
94
  else run_loop(this->matcher, fetcher);
95
95
  }
@@ -16,10 +16,10 @@
16
16
  #include "Cursor.h"
17
17
  #include "../../CpuTimer.h"
18
18
 
19
- Cluster::Cluster(Finder *finder, int height, int width)
19
+ Cluster::Cluster(Finder *finder, int height, int start_x, int end_x)
20
20
  : finder(finder)
21
21
  { tiles_.reserve(2); // only two (left|right)
22
- this->hub_ = new Hub(height, width);
22
+ this->hub_ = new Hub(height, start_x, end_x);
23
23
  }
24
24
 
25
25
  Cluster::~Cluster() {
@@ -20,7 +20,7 @@ class Cluster {
20
20
  std::vector<Tile*> tiles_;
21
21
  Hub *hub_ = nullptr;
22
22
  public:
23
- Cluster(Finder *finder, int height, int width);
23
+ Cluster(Finder *finder, int height, int start_x, int end_x);
24
24
  virtual ~Cluster();
25
25
  void add(Tile* tile);
26
26
  Tile* merge_tiles();
@@ -159,13 +159,36 @@ std::vector<Sequence*> Cursor::join_inners(Sequence* outer_seq) {
159
159
  }
160
160
  }
161
161
 
162
+ std::vector<Shape*> to_delay;
163
+ to_delay = connect_missings(missing_shapes);
164
+ while (!to_delay.empty()) {
165
+ to_delay = connect_missings(to_delay);
166
+ }
167
+
168
+ retme = collect_inner_sequences(outer_seq);
169
+ for (Polyline* polyline : polylines_sequence) {
170
+ polyline->turn_on(Polyline::TRACKED_INNER);
171
+ }
172
+ return(retme);
173
+ }
174
+
175
+ std::vector<Shape*> Cursor::connect_missings(std::vector<Shape*> missing_shapes) {
176
+ std::vector<Shape*> delay_shapes;
177
+
162
178
  for (Polyline *polyline : polylines_sequence)
163
179
  { for (Shape *missing_shape : missing_shapes)
164
180
  { Polyline* outer_polyline = missing_shape->outer_polyline;
165
- if (!polyline->vert_intersect(*outer_polyline)) continue;
166
- std::vector<Point*> intersection = polyline->intersection(outer_polyline);
181
+ if ( outer_polyline->is_on(Polyline::TRACKED_OUTER) ||
182
+ !polyline->vert_intersect(*outer_polyline)) continue;
183
+ std::vector<std::pair<int, int>> intersection = polyline->intersection(outer_polyline);
167
184
  if (intersection.size() > 0)
168
- { auto [inject_sequences_left, inject_sequences_right] = polyline->sew(intersection, outer_polyline);
185
+ { auto result = polyline->sew(intersection, outer_polyline);
186
+ if (!result) {
187
+ delay_shapes.push_back(missing_shape);
188
+ continue;
189
+ }
190
+ auto& inject_sequences_left = result->first;
191
+ auto& inject_sequences_right = result->second;
169
192
  auto combined = combine(inject_sequences_right, inject_sequences_left);
170
193
  for (auto& sewn_sequence : combined) {
171
194
  std::vector<Point*> unique;
@@ -205,11 +228,8 @@ std::vector<Sequence*> Cursor::join_inners(Sequence* outer_seq) {
205
228
  }
206
229
  }
207
230
  }
208
- retme = collect_inner_sequences(outer_seq);
209
- for (Polyline* polyline : polylines_sequence) {
210
- polyline->turn_on(Polyline::TRACKED_INNER);
211
- }
212
- return(retme);
231
+
232
+ return(delay_shapes);
213
233
  }
214
234
 
215
235
  std::vector<Sequence*> Cursor::collect_inner_sequences(Sequence* outer_seq) {
@@ -43,4 +43,5 @@ class Cursor {
43
43
  void traverse_inner(Part* act_part, std::vector<Part*> &all_parts, Bounds& bounds);
44
44
  std::vector<Point*> duplicates_intersection(const Part& part_a, const Part& part_b);
45
45
  std::vector<std::vector<Point*>> combine(std::vector<std::vector<Point*>>& seqa, std::vector<std::vector<Point*>>& seqb);
46
+ std::vector<Shape*> connect_missings(std::vector<Shape*> missing_shapes);
46
47
  };
@@ -86,7 +86,17 @@ void Finder::process_tiles() {
86
86
 
87
87
  if (it != arriving_tiles.end()) {
88
88
  Tile* twin_tile = *it;
89
- Cluster *cluster = new Cluster(this, this->bitmap->h(), this->bitmap->w());
89
+ int start_x, end_x;
90
+ if (twin_tile->start_x() == (tile->end_x() - 1)) {
91
+ start_x = tile->start_x();
92
+ end_x = twin_tile->end_x();
93
+ } else {
94
+ start_x = twin_tile->start_x();
95
+ end_x = tile->end_x();
96
+ }
97
+
98
+ Cluster *cluster = new Cluster(this, this->bitmap->h(), start_x, end_x);
99
+
90
100
  if (twin_tile->start_x() == (tile->end_x() - 1)) {
91
101
  cluster->add(tile);
92
102
  cluster->add(twin_tile);
@@ -124,6 +134,7 @@ Finder::~Finder() {
124
134
  ProcessResult* Finder::process_info() {
125
135
  ProcessResult *pr = new ProcessResult();
126
136
  pr->polygons = std::move(this->whole_tile->to_raw_polygons());
137
+ pr->groups = pr->polygons.size();
127
138
  FakeCluster fake_cluster(pr->polygons, this->options);
128
139
  cpu_timer.start();
129
140
  fake_cluster.compress_coords(pr->polygons, this->options);
@@ -9,15 +9,15 @@
9
9
  #include "Hub.h"
10
10
  #include <cstring>
11
11
 
12
- Hub::Hub(int height, int width)
13
- : width_(width), height_(height)
14
- { size_t total_pixels = static_cast<size_t>(width) * height;
12
+ Hub::Hub(int height, int start_x, int end_x)
13
+ : width_(end_x - start_x), height_(height), start_x_(start_x)
14
+ { size_t total_pixels = static_cast<size_t>(width_) * height;
15
15
  payloads_.resize(total_pixels);
16
16
  bitset_.resize((total_pixels + 63) / 64, 0);
17
17
  std::memset(bitset_.data(), 0, bitset_.size() * sizeof(uint64_t));
18
18
  }
19
19
 
20
- EndPoint* Hub::spawn_end_point() {
20
+ int Hub::spawn_end_point() {
21
21
  endpoint_pool_.emplace_back();
22
- return &endpoint_pool_.back();
22
+ return static_cast<int>(endpoint_pool_.size() - 1);
23
23
  }
@@ -14,16 +14,19 @@
14
14
  class EndPoint;
15
15
  class Hub {
16
16
  public:
17
- explicit Hub(int height, int width);
18
- EndPoint* spawn_end_point();
17
+ explicit Hub(int height, int start_x, int end_x);
18
+ int spawn_end_point();
19
19
  const int width() const { return width_; }
20
- inline EndPoint* get(int key) const {
20
+ const int start_x() const { return start_x_; }
21
+ inline EndPoint* get(int key) {
21
22
  if (!is_set(key)) return nullptr;
22
- return payloads_[key];
23
+ int index = payloads_[key];
24
+ return &endpoint_pool_[index];
23
25
  }
24
- inline void put(int key, EndPoint* payload) {
25
- payloads_[key] = payload;
26
+ inline EndPoint* put(int key, int index) {
27
+ payloads_[key] = index;
26
28
  bitset_[key >> 6] |= (1ULL << (key & 63));
29
+ return &endpoint_pool_[index];
27
30
  }
28
31
  inline bool is_set(int key) const {
29
32
  return (bitset_[key >> 6] & (1ULL << (key & 63)));
@@ -32,7 +35,8 @@ class Hub {
32
35
  private:
33
36
  int width_;
34
37
  int height_;
35
- std::vector<EndPoint*> payloads_;
38
+ int start_x_;
39
+ std::vector<int> payloads_;
36
40
  std::deque<EndPoint> endpoint_pool_;
37
41
  Hub(const Hub&) = delete;
38
42
  Hub& operator=(const Hub&) = delete;
@@ -27,6 +27,7 @@ class Part : public Queueable<Point> {
27
27
  bool is(Types type);
28
28
  bool inverts = false;
29
29
  bool trasmuted = false;
30
+ bool delayed = false;
30
31
  Part* next = nullptr;
31
32
  Part* prev = nullptr;
32
33
  Part* circular_next = nullptr;
@@ -40,6 +41,7 @@ class Part : public Queueable<Point> {
40
41
  const bool touched() const { return touched_; }
41
42
  void touch();
42
43
  bool intersect_part(Part* other_part);
44
+ void set_polyline(Polyline* polyline) { this->polyline_ = polyline; }
43
45
 
44
46
  private:
45
47
  bool touched_ = false;