contrek 1.0.7 → 1.0.9

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +80 -8
  5. data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +39 -0
  6. data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +43 -0
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +72 -0
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +19 -24
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +1 -2
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +3 -5
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -1
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +2 -2
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +1 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +29 -9
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -0
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +11 -1
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.cpp +6 -5
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.h +12 -8
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +2 -1
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +2 -0
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +108 -66
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +5 -3
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +35 -28
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +5 -1
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +13 -10
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.h +1 -1
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +16 -22
  28. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +1 -1
  29. data/lib/contrek/bitmaps/rgb_color.rb +0 -15
  30. data/lib/contrek/bitmaps/rgb_cpp_color.rb +10 -0
  31. data/lib/contrek/finder/concurrent/cluster.rb +2 -2
  32. data/lib/contrek/finder/concurrent/cursor.rb +34 -16
  33. data/lib/contrek/finder/concurrent/finder.rb +12 -1
  34. data/lib/contrek/finder/concurrent/hub.rb +3 -4
  35. data/lib/contrek/finder/concurrent/part.rb +8 -2
  36. data/lib/contrek/finder/concurrent/partitionable.rb +61 -33
  37. data/lib/contrek/finder/concurrent/polyline.rb +45 -2
  38. data/lib/contrek/finder/concurrent/position.rb +3 -4
  39. data/lib/contrek/finder/concurrent/queueable.rb +12 -20
  40. data/lib/contrek/version.rb +1 -1
  41. data/lib/contrek.rb +4 -3
  42. metadata +6 -4
  43. data/ext/cpp_polygon_finder/PolygonFinder/Makefile +0 -44
  44. data/ext/cpp_polygon_finder/PolygonFinder/src/Main.cpp +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0d1a364ff39a11f632f5f57d99970448173727e8f5a42e84e1b334521c1e1d1
4
- data.tar.gz: 24a52785c8735e9fce60931a1ffc1bd974aa5a50a6fb755229cd25dd5263cab3
3
+ metadata.gz: 15c162c3876502f8e8710ce261825dfe7fa66e1b600db084763e02ef70876a37
4
+ data.tar.gz: 32203f85040726d71ac519aac811d4078474c85dcd7c7b08227f1cea7e3b485f
5
5
  SHA512:
6
- metadata.gz: e321b716a5c16d6c75f24bc6bcdebc302a6ea9785009bad93905321ebc7a97ef29048fb1c6e4e0557611ec9d096dee874bd1ff37f66ad2763c4dacd7176b0ce4
7
- data.tar.gz: 38608a89ed8ee0c43a985bf8057d07b860acd524d2e8c0022651e5ec3fdcc8f13e3d515e309d436ad261203e00a58b901dd6cf141bfbeeb93520f7937ccdb4b8
6
+ metadata.gz: c9f8d2a44f087f96cfeacc8df8dec0973bf301f770ae45e82e6bf7d01902546c280c0446ee9c281c850a8e7c8965309e20d1a723ec269e60a99d386c288af0e7
7
+ data.tar.gz: 4d2c5c834fca24731866d0a56cc64d4aeac12437fac11b78389c38f7fbed9e637c785eb2a3357fb5c90a5fc257b2d010fe36cac2f84db3888f2b311276c42895
data/CHANGELOG.md CHANGED
@@ -23,3 +23,14 @@ All notable changes to this project will be documented in this file.
23
23
  ## [1.0.7] - 2026-01-10
24
24
  ### Added
25
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.
32
+
33
+ ## [1.0.9] - 2026-01-31
34
+ ### Added
35
+ - **CMake Integration**: The library is now fully modularized via CMake, enabling seamless integration as a dependency in external C++ projects.
36
+ - Multithreading Algorithm Optimization.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contrek (1.0.7)
4
+ contrek (1.0.9)
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 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.
2
+ Contrek is a Ruby gem powered by a <u>standalone C++17 core library</u> for fast contour tracing and edge detection in PNG images. 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.
@@ -99,7 +99,7 @@ Regarding multithreading:
99
99
 
100
100
  - The treemap option is currently ignored (multithreaded treemap support will be introduced in upcoming revisions).
101
101
 
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
+ 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 1.66 secs with image load).
103
103
 
104
104
  ```ruby
105
105
  result = Contrek.contour!(
@@ -113,11 +113,13 @@ result = Contrek.contour!(
113
113
  )
114
114
  puts result.metadata[:benchmarks].inspect
115
115
 
116
- {"compress"=>14.596786999999999,
117
- "init"=>2078.745861,
118
- "inner"=>1183.143734,
119
- "outer"=>118.94489599999999,
120
- "total"=>2093.342648}
116
+ { "compress"=>13.423765,
117
+ "init"=>612.654121,
118
+ "inner"=>14.8930669,
119
+ "outer"=>33.0693249,
120
+ "total"=>626.0778879
121
+ }
122
+
121
123
  ```
122
124
 
123
125
  ## Result
@@ -262,7 +264,77 @@ This the one for the native C++
262
264
  }
263
265
  ```
264
266
 
265
- About 75x faster. Times are in microseconds; system: AMD Ryzen 7 3700X 8-Core Processor (BogoMIPS: 7199,99) on Ubuntu distro.
267
+ About 75x faster. Times are in microseconds; system: AMD Ryzen 7 3700X 8-Core Processor (BogoMIPS: 7199,99) on Ubuntu distro.
268
+
269
+ ## 🛠 C++ Standalone Library Usage
270
+
271
+ The core of **Contrek** is a high-performance `C++17` library. It is designed to be **standalone**, meaning it has zero dependencies on Ruby and can be integrated into any `C++` project.
272
+
273
+
274
+ ### Prerequisites
275
+ * **CMake** (3.10+)
276
+ * **ZLIB** (Required for PNG processing)
277
+ * **C++17 Compiler**
278
+
279
+ ### 1. Build & Run Examples
280
+ If you want to test the performances or see the library in action:
281
+
282
+ ```bash
283
+ # Navigate to the core folder
284
+ cd ext/cpp_polygon_finder/PolygonFinder
285
+
286
+ # Setup build directory
287
+ mkdir build && cd build
288
+
289
+ # Configure with examples enabled
290
+ cmake -DBUILD_EXAMPLES=ON ..
291
+
292
+ # Build and run
293
+ make
294
+ ./contrek_test
295
+ ```
296
+
297
+ ### 2. How to integrate it into your project
298
+
299
+ To use Contrek's engine in your own C++ application without Ruby:
300
+
301
+ 1. **Copy the core folder**: Take the `ext/cpp_polygon_finder/PolygonFinder` directory and place it inside your project (e.g., in a `libs/` or `third_party/` folder).
302
+ 2. **Update your CMakeLists.txt**: Add these lines to link the library:
303
+
304
+ ```cmake
305
+ # Tell CMake to include Contrek
306
+ add_subdirectory(libs/PolygonFinder)
307
+
308
+ # Link it to your executable
309
+ add_executable(my_app main.cpp)
310
+ target_link_libraries(my_app PRIVATE ContrekLib)
311
+ ```
312
+
313
+ ### 3. C++ API Quick Start
314
+
315
+ Contrek provides a high-level C++ API. Here is how you can use it in your standalone projects:
316
+
317
+ ```cpp
318
+ #include "ContrekApi.h"
319
+ #include <iostream>
320
+
321
+ int main() {
322
+ // 1. Configure the engine
323
+ Contrek::Config cfg;
324
+ cfg.threads = 4;
325
+ cfg.tiles = 2;
326
+
327
+ // 2. Run the tracing process
328
+ // Supports PNG files via internal spng integration
329
+ auto result = Contrek::trace("path/to/image.png", cfg);
330
+
331
+ // 3. Access results
332
+ result->print_info(); // prints generic infos
333
+ std::cout << "Found polygons: " << result->groups << std::endl;
334
+
335
+ return 0;
336
+ }
337
+ ```
266
338
 
267
339
  ## License
268
340
 
@@ -0,0 +1,39 @@
1
+ cmake_minimum_required(VERSION 3.10)
2
+ project(ContrekCore C CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_C_STANDARD 11)
6
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pthread -g -march=native -DNDEBUG -Ofast -flto")
7
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fPIC -DNDEBUG")
8
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread -flto=auto -Wl,--no-as-needed")
9
+
10
+ find_package(ZLIB REQUIRED)
11
+
12
+ file(GLOB_RECURSE CPP_SOURCES "*.cpp")
13
+ file(GLOB_RECURSE C_SOURCES "*.c")
14
+
15
+ list(FILTER CPP_SOURCES EXCLUDE REGEX "examples/.*\\.cpp")
16
+
17
+ add_library(ContrekLib STATIC ${CPP_SOURCES} ${C_SOURCES})
18
+
19
+ file(GLOB_RECURSE ALL_HEADERS "*.h")
20
+ foreach(header_file ${ALL_HEADERS})
21
+ get_filename_component(header_dir ${header_file} DIRECTORY)
22
+ list(APPEND ALL_INCLUDE_DIRS ${header_dir})
23
+ endforeach()
24
+ list(REMOVE_DUPLICATES ALL_INCLUDE_DIRS)
25
+
26
+ target_include_directories(ContrekLib PUBLIC ${ALL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS})
27
+ target_link_libraries(ContrekLib PRIVATE ${ZLIB_LIBRARIES} pthread)
28
+
29
+ option(BUILD_EXAMPLES "Build the example application" OFF)
30
+
31
+ if(BUILD_EXAMPLES)
32
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/example.cpp")
33
+ message(STATUS "Contrek: Compiling example option ON")
34
+ add_executable(contrek_test examples/example.cpp)
35
+ target_link_libraries(contrek_test PRIVATE ContrekLib)
36
+ else()
37
+ message(WARNING "Contrek: examples/example.cpp not found!")
38
+ endif()
39
+ endif()
@@ -0,0 +1,43 @@
1
+ //============================================================================
2
+ // Name : example.cpp
3
+ // Author : Emanuele Cesaroni
4
+ // Version :
5
+ // Copyright : 2025 Emanuele Cesaroni
6
+ // Description :
7
+ //============================================================================
8
+
9
+ #include <iostream>
10
+ #include "ContrekApi.h"
11
+ #include "Tests.h"
12
+
13
+ void run_test() {
14
+ CpuTimer cpu_timer;
15
+ Tests test_suite;
16
+ cpu_timer.start();
17
+
18
+ // test_suite.test_a();
19
+ // test_suite.test_b();
20
+ // test_suite.test_c();
21
+ // test_suite.test_d();
22
+ // test_suite.test_e();
23
+ test_suite.test_f();
24
+ std::cout << "compute time =" << cpu_timer.stop() << std::endl;
25
+ }
26
+
27
+ int main() {
28
+ Contrek::Config cfg;
29
+ cfg.threads = 2;
30
+ cfg.tiles = 2;
31
+ cfg.compress_unique = true;
32
+
33
+ CpuTimer cpu_timer;
34
+ cpu_timer.start();
35
+ std::cout << "--- Start Native Benchmark ---" << std::endl;
36
+ auto result = Contrek::trace("../images/sample_10240x10240.png", cfg);
37
+ result->print_info();
38
+ std::cout << "Found polygons: " << result->groups << std::endl;
39
+ std::cout << "Time: " << cpu_timer.stop() << " ms" << std::endl;
40
+
41
+ // run_test();
42
+ return 0;
43
+ }
@@ -0,0 +1,72 @@
1
+ /*
2
+ * ContrekApi.h
3
+ *
4
+ * Created on: 25 gen 2026
5
+ * Author: ema
6
+ * Copyright 2025 Emanuele Cesaroni
7
+ */
8
+
9
+ #pragma once
10
+
11
+ #include <string>
12
+ #include <vector>
13
+ #include <memory>
14
+ #include <cstdint>
15
+
16
+ #include "Finder.h"
17
+ #include "FastPngBitmap.h"
18
+ #include "RGBNotMatcher.h"
19
+ #include "RGBMatcher.h"
20
+
21
+ namespace Contrek {
22
+
23
+ enum class MatchMode {
24
+ NOT_COLOR, // Tracks border of what is not target color
25
+ EXACT_COLOR // Tracks border of what exactly matchs target color
26
+ };
27
+
28
+ struct Config {
29
+ int threads = 4;
30
+ int tiles = 2;
31
+ bool compress_unique = false;
32
+ bool compress_linear = false;
33
+ bool compress_visvalingam = false;
34
+ bool treemap = false;
35
+ int32_t target_color = -1;
36
+ MatchMode mode = MatchMode::NOT_COLOR;
37
+ };
38
+
39
+ inline std::unique_ptr<ProcessResult> trace(const std::string& image_path, const Config& cfg = Config()) {
40
+ auto bitmap = std::make_unique<FastPngBitmap>(image_path);
41
+
42
+ int32_t color_to_match = (cfg.target_color == -1)
43
+ ? bitmap->rgb_value_at(0, 0)
44
+ : cfg.target_color;
45
+
46
+ std::unique_ptr<Matcher> matcher;
47
+ if (cfg.mode == MatchMode::NOT_COLOR) {
48
+ matcher = std::make_unique<RGBNotMatcher>(color_to_match);
49
+ } else {
50
+ matcher = std::make_unique<RGBMatcher>(color_to_match);
51
+ }
52
+
53
+ std::vector<std::string> internal_args = {"--versus=a"};
54
+ struct Mapping { bool flag; std::string_view arg; };
55
+ const Mapping mappings[] = {
56
+ {cfg.compress_unique, "--compress_uniq"},
57
+ {cfg.compress_linear, "--compress_linear"},
58
+ {cfg.compress_visvalingam, "--compress_visvalingam"},
59
+ {cfg.treemap, "--treemap"}
60
+ };
61
+
62
+ for (auto& m : mappings) {
63
+ if (m.flag) internal_args.emplace_back(m.arg);
64
+ }
65
+ internal_args.push_back("--number_of_tiles=" + std::to_string(cfg.tiles));
66
+
67
+ Finder finder(cfg.threads, bitmap.get(), matcher.get(), &internal_args);
68
+
69
+ return std::unique_ptr<ProcessResult>(finder.process_info());
70
+ }
71
+
72
+ } // namespace Contrek
@@ -28,12 +28,6 @@
28
28
  #include "polygon/finder/concurrent/Position.h"
29
29
  #include "polygon/finder/Polygon.h"
30
30
 
31
- Tests::Tests() {
32
- }
33
-
34
- Tests::~Tests() {
35
- }
36
-
37
31
  void Tests::test_a()
38
32
  { std::string chunk =
39
33
  "0000000000000000"\
@@ -79,22 +73,10 @@ void Tests::test_b()
79
73
  " XX XX "\
80
74
  " XX XX "\
81
75
  " XXXXXXXXXXX ";
82
- // "0123456789012345"
83
- /*chunk = " XXXXXXXXXXX "\
84
- " XX XX XX "\
85
- " XX XX XX "\
86
- " XX XX XX "\
87
- " XXXXXXXXXXX ";*/
88
76
 
89
77
  std::vector<std::string> arguments = {"--versus=o", "--number_of_tiles=2", "--compress_uniq", "--compress_linear", "--treemap"};
90
78
  ValueNotMatcher matcher(' ');
91
79
  Bitmap b(chunk, 16);
92
-
93
- /*ClippedPolygonFinder pl(&b, &matcher, 0, 16, &arguments);
94
- ProcessResult *pr = pl.process_info();
95
- pr->print_polygons();
96
- delete pr;*/
97
-
98
80
  Finder concurrentFinder(2, &b, &matcher, &arguments);
99
81
  ProcessResult *pro = concurrentFinder.process_info();
100
82
  pro->print_polygons();
@@ -107,7 +89,7 @@ void Tests::test_c()
107
89
  Point* p2 = new Point({2, 2});
108
90
  Point* p3 = new Point({3, 3});
109
91
 
110
- Hub* hub = new Hub(4, 4); // coordinate 0 -> 3 sia x che y, matrice di 4 * 4
92
+ Hub* hub = new Hub(4, 0, 3);
111
93
 
112
94
  Position* pos1 = new Position(hub, p1);
113
95
  Position* pos2 = new Position(hub, p2);
@@ -155,7 +137,7 @@ void Tests::test_c()
155
137
  void Tests::test_d()
156
138
  { CpuTimer cpu_timer;
157
139
  cpu_timer.start();
158
- FastPngBitmap png_bitmap("images/sample_10240x10240.png");
140
+ FastPngBitmap png_bitmap("../images/sample_10240x10240.png");
159
141
  // FastPngBitmap png_bitmap("images/labyrinth.png");
160
142
  std::cout << "image_w=" << png_bitmap.w() << " image_h=" << png_bitmap.h() << std::endl;
161
143
  std::cout << "immagine =" << cpu_timer.stop() << std::endl;
@@ -174,17 +156,30 @@ void Tests::test_d()
174
156
  void Tests::test_e()
175
157
  { CpuTimer cpu_timer;
176
158
  cpu_timer.start();
177
- FastPngBitmap png_bitmap("images/sample_10240x10240.png");
159
+ FastPngBitmap png_bitmap("../images/sample_10240x10240.png");
178
160
  // FastPngBitmap png_bitmap("images/sample_1024x1024.png");
179
- std::cout << "immagine =" << cpu_timer.stop() << std::endl;
161
+ std::cout << "image reading time =" << cpu_timer.stop() << std::endl;
180
162
 
181
- int color = png_bitmap.value_at(0, 0);
182
- std::cout << "color =" << color << std::endl;
163
+ int color = png_bitmap.rgb_value_at(0, 0);
164
+ std::cout << "color = " << color << std::endl;
183
165
  RGBNotMatcher not_matcher(color);
184
166
 
185
167
  std::vector<std::string> arguments = {"--versus=a", "--compress_uniq", "--number_of_tiles=2"};
186
168
  Finder pl(2, &png_bitmap, &not_matcher, &arguments);
187
169
  ProcessResult *o = pl.process_info();
188
170
  o->print_info();
171
+ std::cout << "polygons =" << o->groups << std::endl;
189
172
  delete o;
190
173
  }
174
+
175
+ void Tests::test_f()
176
+ { FastPngBitmap png_bitmap("../images/labyrinth.png");
177
+ std::cout << "image_w=" << png_bitmap.w() << " image_h=" << png_bitmap.h() << std::endl;
178
+ std::cout << "load_error=" << png_bitmap.error() << std::endl;
179
+
180
+ std::string data_url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==";
181
+ data_url.erase(0, 22);
182
+ RemoteFastPngBitmap bitmap(&data_url);
183
+ std::cout << "image_w=" << bitmap.w() << " image_h=" << bitmap.h() << std::endl;
184
+ std::cout << "load_error=" << bitmap.error() << std::endl;
185
+ }
@@ -10,11 +10,10 @@
10
10
 
11
11
  class Tests {
12
12
  public:
13
- Tests();
14
- virtual ~Tests();
15
13
  virtual void test_a();
16
14
  virtual void test_b();
17
15
  virtual void test_c();
18
16
  virtual void test_d();
19
17
  virtual void test_e();
18
+ virtual void test_f();
20
19
  };
@@ -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) {
@@ -269,7 +289,7 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
269
289
  } else {
270
290
  for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
271
291
  for (Part* dest_part : shape->outer_polyline->parts()) {
272
- if (dest_part->trasmuted) continue;
292
+ if (dest_part->trasmuted || dest_part->is(Part::EXCLUSIVE)) continue;
273
293
  if (dest_part->intersect_part(act_part)) {
274
294
  std::vector<Point*> link_seq = duplicates_intersection(*dest_part, *act_part);
275
295
  if (!link_seq.empty()) {
@@ -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);
@@ -9,15 +9,16 @@
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>(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
+ endpoint_pool_.resize(total_pixels);
18
19
  }
19
20
 
20
- EndPoint* Hub::spawn_end_point() {
21
+ int Hub::spawn_end_point() {
21
22
  endpoint_pool_.emplace_back();
22
- return &endpoint_pool_.back();
23
+ return static_cast<int>(endpoint_pool_.size() - 1);
23
24
  }
@@ -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,8 +35,9 @@ class Hub {
32
35
  private:
33
36
  int width_;
34
37
  int height_;
35
- std::vector<EndPoint*> payloads_;
36
- std::deque<EndPoint> endpoint_pool_;
38
+ int start_x_;
39
+ std::vector<int> payloads_;
40
+ std::vector<EndPoint> endpoint_pool_;
37
41
  Hub(const Hub&) = delete;
38
42
  Hub& operator=(const Hub&) = delete;
39
43
  std::vector<uint64_t> bitset_;
@@ -44,7 +44,8 @@ Position* Part::next_position(Position* force_position) {
44
44
 
45
45
  void Part::add_position(Point* point) {
46
46
  Cluster* c = this->polyline_->tile->cluster;
47
- c->positions_pool.emplace_back(c->hub(), point);
47
+ Hub* hub = is(EXCLUSIVE) ? nullptr : c->hub();
48
+ c->positions_pool.emplace_back(hub, point);
48
49
  this->add(&c->positions_pool.back());
49
50
  }
50
51
 
@@ -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;