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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +80 -8
- data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +39 -0
- data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +43 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +72 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +19 -24
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.h +1 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +3 -5
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +29 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +11 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.cpp +6 -5
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.h +12 -8
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +108 -66
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +5 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +35 -28
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +5 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +13 -10
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +16 -22
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +1 -1
- data/lib/contrek/bitmaps/rgb_color.rb +0 -15
- data/lib/contrek/bitmaps/rgb_cpp_color.rb +10 -0
- data/lib/contrek/finder/concurrent/cluster.rb +2 -2
- data/lib/contrek/finder/concurrent/cursor.rb +34 -16
- data/lib/contrek/finder/concurrent/finder.rb +12 -1
- data/lib/contrek/finder/concurrent/hub.rb +3 -4
- data/lib/contrek/finder/concurrent/part.rb +8 -2
- data/lib/contrek/finder/concurrent/partitionable.rb +61 -33
- data/lib/contrek/finder/concurrent/polyline.rb +45 -2
- data/lib/contrek/finder/concurrent/position.rb +3 -4
- data/lib/contrek/finder/concurrent/queueable.rb +12 -20
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +4 -3
- metadata +6 -4
- data/ext/cpp_polygon_finder/PolygonFinder/Makefile +0 -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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15c162c3876502f8e8710ce261825dfe7fa66e1b600db084763e02ef70876a37
|
|
4
|
+
data.tar.gz: 32203f85040726d71ac519aac811d4078474c85dcd7c7b08227f1cea7e3b485f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
data/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Contrek
|
|
2
|
-
Contrek is a Ruby gem
|
|
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
|
|
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"=>
|
|
117
|
-
"init"=>
|
|
118
|
-
"inner"=>
|
|
119
|
-
"outer"=>
|
|
120
|
-
"total"=>
|
|
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,
|
|
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 << "
|
|
161
|
+
std::cout << "image reading time =" << cpu_timer.stop() << std::endl;
|
|
180
162
|
|
|
181
|
-
int color = png_bitmap.
|
|
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, ¬_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
|
+
}
|
|
@@ -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
|
-
|
|
75
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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 (
|
|
166
|
-
|
|
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
|
|
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
|
-
|
|
209
|
-
|
|
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
|
-
|
|
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
|
|
13
|
-
: width_(
|
|
14
|
-
{ size_t total_pixels = static_cast<size_t>(
|
|
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
|
-
|
|
21
|
+
int Hub::spawn_end_point() {
|
|
21
22
|
endpoint_pool_.emplace_back();
|
|
22
|
-
return
|
|
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
|
|
18
|
-
|
|
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
|
-
|
|
20
|
+
const int start_x() const { return start_x_; }
|
|
21
|
+
inline EndPoint* get(int key) {
|
|
21
22
|
if (!is_set(key)) return nullptr;
|
|
22
|
-
|
|
23
|
+
int index = payloads_[key];
|
|
24
|
+
return &endpoint_pool_[index];
|
|
23
25
|
}
|
|
24
|
-
inline
|
|
25
|
-
payloads_[key] =
|
|
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
|
-
|
|
36
|
-
std::
|
|
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
|
-
|
|
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;
|