contrek 1.1.7 → 1.1.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 +13 -1
- data/Gemfile.lock +1 -1
- data/README.md +50 -23
- data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +19 -11
- data/ext/cpp_polygon_finder/PolygonFinder/clean.sh +28 -0
- data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +4 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +2 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +66 -61
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Primitives.h +14 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ClippedPolygonFinder.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +86 -23
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +3 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +24 -57
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +7 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +12 -9
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +55 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +32 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +38 -23
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +1 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +21 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +4 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +14 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +14 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.cpp +15 -6
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.h +11 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp +42 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h +34 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +72 -11
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +9 -1
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +4 -0
- data/lib/contrek/finder/concurrent/cluster.rb +58 -9
- data/lib/contrek/finder/concurrent/cursor.rb +11 -19
- data/lib/contrek/finder/concurrent/finder.rb +8 -2
- data/lib/contrek/finder/concurrent/inner_polyline.rb +49 -0
- data/lib/contrek/finder/concurrent/part.rb +26 -12
- data/lib/contrek/finder/concurrent/polyline.rb +46 -0
- data/lib/contrek/finder/concurrent/queueable.rb +0 -17
- data/lib/contrek/finder/concurrent/sequence.rb +21 -0
- data/lib/contrek/finder/concurrent/shape.rb +29 -2
- data/lib/contrek/finder/concurrent/tile.rb +36 -7
- data/lib/contrek/finder/node.rb +3 -1
- data/lib/contrek/finder/node_cluster.rb +56 -48
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +1 -0
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87e8665985a5c9b3818f5f405609c8bf0f9d81dfaa197054e0801acdc60dc56c
|
|
4
|
+
data.tar.gz: 450c56eda5bd65ab936fcd52b37e3f8edbc2ec0ac852aa0cc4c052ee6d81ab7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4cc2ea0d61819436e922eacdcad551658deceac1b5a907cea03d306de65fa2f71b96018acf6fcc6f88163775cabf0ba9b8d6ded3e9067dfcf3cc3e70d3e3e6d
|
|
7
|
+
data.tar.gz: ca933647465baca8a294176a1fbbc336bf1956c8eebab2cf48dd45ac4e55ba55ea84b00d38cbdd9ee0cf012b332684b8902c5e98a8cbaaafbfc846b374a12801
|
data/CHANGELOG.md
CHANGED
|
@@ -18,7 +18,7 @@ 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
|
|
21
|
+
- Removed Png++ dependency in place of libspng
|
|
22
22
|
|
|
23
23
|
## [1.0.7] - 2026-01-10
|
|
24
24
|
### Added
|
|
@@ -68,3 +68,15 @@ All notable changes to this project will be documented in this file.
|
|
|
68
68
|
### Changed
|
|
69
69
|
- Added strict_bounds tracing mode: enables more accurate shape tracing by strictly adhering to pixel boundaries.
|
|
70
70
|
- Topological Consistency Fixes: improved the Topologically Consistent Merging algorithm to support progressive polygon tracing during sequential data streaming.
|
|
71
|
+
|
|
72
|
+
## [1.1.7] - 2026-03-23
|
|
73
|
+
### Changed
|
|
74
|
+
- Removed docs directory from gem.
|
|
75
|
+
|
|
76
|
+
## [1.1.8] - 2026-04-19
|
|
77
|
+
### Changed
|
|
78
|
+
- Treemap now available on multiprocessing side too.
|
|
79
|
+
|
|
80
|
+
## [1.1.9] - 2026-04-24
|
|
81
|
+
### Changed
|
|
82
|
+
- Improved the internal parts joining algorithm, which was imprecise in some circumstances.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -29,24 +29,24 @@ The core strength of Contrek is its **Topologically Consistent Merging** algorit
|
|
|
29
29
|
|
|
30
30
|
<table>
|
|
31
31
|
<tr>
|
|
32
|
-
<td
|
|
33
|
-
|
|
34
|
-
<img src="docs/images/stripes/
|
|
32
|
+
<td width="50%" style="padding: 0; background-color: white;">
|
|
33
|
+
<img src="docs/images/stripes/whole_0.png" width="100%"><br>
|
|
34
|
+
<img src="docs/images/stripes/whole_256.png" width="100%"><br>
|
|
35
|
+
<img src="docs/images/stripes/whole_512.png" width="100%"><br>
|
|
36
|
+
<img src="docs/images/stripes/whole_768.png" width="100%">
|
|
37
|
+
</td>
|
|
38
|
+
<td width="50%" align="center" style="vertical-align: middle; background-color: white;">
|
|
39
|
+
<strong>Full Topological Reconstruction</strong><br><br>
|
|
40
|
+
<img src="docs/images/stripes/whole.png" width="90%">
|
|
35
41
|
</td>
|
|
36
42
|
</tr>
|
|
37
43
|
<tr>
|
|
38
|
-
<td
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<tr>
|
|
44
|
-
<td><img src="docs/images/stripes/whole_768.png" alt="Contour tracing stripe 0" width="50%"></td>
|
|
44
|
+
<td colspan="2" align="center" style="background-color: white;">
|
|
45
|
+
<em><b>Left:</b> Image split into 4 independent memory buffers (stripes).</em><br>
|
|
46
|
+
<em><b>Right:</b> Contrek ensures <b>perfect topological continuity</b> during merging.</em><br>
|
|
47
|
+
🔴 <b>Red:</b> Outer contours | 🟢 <b>Green:</b> Inner zones
|
|
48
|
+
</td>
|
|
45
49
|
</tr>
|
|
46
|
-
<tr><td colspan="2"><em><b>Left:</b> The image is split into 4 independent memory buffers (stripes).
|
|
47
|
-
<br><b>Right:</b> Contrek ensures <b>perfect topological continuity</b> during merging.</em>
|
|
48
|
-
<br><span style="color: #d32f2f;">■</span> <b>Red:</b> Outer contours |
|
|
49
|
-
<span style="color: #2e7d32;">■</span> <b>Dark Green:</b> Inner zones</td></tr>
|
|
50
50
|
</table>
|
|
51
51
|
|
|
52
52
|
## Prerequisites
|
|
@@ -167,8 +167,6 @@ Regarding multithreading:
|
|
|
167
167
|
|
|
168
168
|
- The algorithm splits the contour-detection workflow into multiple phases that can be executed in parallel. The initial contour extraction on each band and the subsequent merging of coordinates between adjacent bands—performed pairwise, recursively, and in a non-deterministic order—results in a final output that is not idempotent. Idempotence is guaranteed only when the exact same merging sequence is repeated.
|
|
169
169
|
|
|
170
|
-
- The treemap option is currently ignored (multithreaded treemap support will be introduced in upcoming revisions).
|
|
171
|
-
|
|
172
170
|
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 4 threads working on 4 tiles (total compute time about 1.2 secs with image load).
|
|
173
171
|
|
|
174
172
|
```ruby
|
|
@@ -290,7 +288,7 @@ result.metadata
|
|
|
290
288
|
|
|
291
289
|
## Treemap
|
|
292
290
|
|
|
293
|
-
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
|
|
291
|
+
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 the index of the parent inner sequence in which is placed. This structure allows reconstruction of the inclusion tree and determination of nesting relationships between geometries. The treemap indexes are referred to positions inside the array of polygons returned by result.points. Consider the above image traced clockwise (o).
|
|
294
292
|
|
|
295
293
|
```ruby
|
|
296
294
|
"AAAAAAAAAAAAAAAAAAAAAA" \
|
|
@@ -311,8 +309,8 @@ result.metadata[:treemap]
|
|
|
311
309
|
|
|
312
310
|
[[-1, -1], # A
|
|
313
311
|
[0, 0], # B
|
|
314
|
-
[1,
|
|
315
|
-
[1,
|
|
312
|
+
[1, 1], # C
|
|
313
|
+
[1, 0]] # D
|
|
316
314
|
```
|
|
317
315
|
|
|
318
316
|
There are four polygons (`A`, `B`, `C`, and `D`).
|
|
@@ -321,13 +319,42 @@ The order matches the one provided in `result.polygons`.
|
|
|
321
319
|
Each entry has the structure:
|
|
322
320
|
|
|
323
321
|
```
|
|
324
|
-
[parent_index,
|
|
322
|
+
[parent_index, index_of_parent_inner_sequence]
|
|
325
323
|
```
|
|
326
324
|
|
|
327
325
|
- Polygon **A** (index `0`) has no parent and is represented as `[-1, -1]`
|
|
328
|
-
- Polygon **B** is contained in **A** and
|
|
329
|
-
- Polygon **C** is contained in **B** and
|
|
330
|
-
- Polygon **D** is also contained in **B**
|
|
326
|
+
- Polygon **B** is contained in **A** in the first (0) and unique inner sequence of A → `[0, 0]`
|
|
327
|
+
- Polygon **C** is contained in **B** and inside its second (1) inner sequence → `[1, 1]`
|
|
328
|
+
- Polygon **D** is also contained in **B** but in the first (0) of its inner sequences → `[1, 0]`
|
|
329
|
+
|
|
330
|
+
**C** is inside the **second** B inner sequence and **D** is inside the **first** B inner sequence because when the contour is traced clockwise the inner sequences are listed from right to left (left to right when anti-clockwise).
|
|
331
|
+
|
|
332
|
+
Consider this sequence
|
|
333
|
+
```ruby
|
|
334
|
+
"AAAAAAAAAAAAAAA " \
|
|
335
|
+
"A A " \
|
|
336
|
+
"A BBBBBBBBBBB A " \
|
|
337
|
+
"A B B A " \
|
|
338
|
+
"A B CC DD E B A " \
|
|
339
|
+
"A B B A " \
|
|
340
|
+
"A BBBBBBBBBBB A " \
|
|
341
|
+
"A A " \
|
|
342
|
+
"AAAAAAAAAAAAAAA "
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
will get
|
|
346
|
+
|
|
347
|
+
```ruby
|
|
348
|
+
result.metadata[:treemap]
|
|
349
|
+
|
|
350
|
+
[[-1, -1], # A
|
|
351
|
+
[0, 0], # B
|
|
352
|
+
[1, 0], # C
|
|
353
|
+
[1, 0], # D
|
|
354
|
+
[1, 0]] # E
|
|
355
|
+
```
|
|
356
|
+
C, D and E will get the same pair (**[1, 0]**) because are all placed inside the first (0) inner sequence of B.
|
|
357
|
+
|
|
331
358
|
|
|
332
359
|
## Multithreaded approach
|
|
333
360
|
|
|
@@ -3,20 +3,28 @@ project(ContrekCore C CXX)
|
|
|
3
3
|
|
|
4
4
|
set(CMAKE_CXX_STANDARD 17)
|
|
5
5
|
set(CMAKE_C_STANDARD 11)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
set(
|
|
9
|
-
set(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if(TCMALLOC_LIB)
|
|
15
|
-
message(STATUS "Contrek: tcmalloc found in ${TCMALLOC_LIB}")
|
|
6
|
+
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
7
|
+
message(STATUS "Contrek: debuginning mode (ASan + GDB)")
|
|
8
|
+
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -fsanitize=address -fno-omit-frame-pointer")
|
|
9
|
+
set(CMAKE_C_FLAGS_DEBUG "-g -O0 -fsanitize=address -fno-omit-frame-pointer")
|
|
10
|
+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
|
11
|
+
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
|
|
12
|
+
list(FILTER CMAKE_CXX_FLAGS EXCLUDE REGEX "-DNDEBUG")
|
|
13
|
+
list(FILTER CMAKE_C_FLAGS EXCLUDE REGEX "-DNDEBUG")
|
|
16
14
|
else()
|
|
17
|
-
|
|
15
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pthread -march=native -DNDEBUG -Ofast -flto")
|
|
16
|
+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fPIC -DNDEBUG")
|
|
17
|
+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread -flto=auto -Wl,--no-as-needed")
|
|
18
|
+
find_library(TCMALLOC_LIB tcmalloc)
|
|
19
|
+
if(TCMALLOC_LIB)
|
|
20
|
+
message(STATUS "Contrek: tcmalloc found in ${TCMALLOC_LIB}")
|
|
21
|
+
else()
|
|
22
|
+
message(WARNING "Contrek: tcmalloc not found; standard one will be used.")
|
|
23
|
+
endif()
|
|
18
24
|
endif()
|
|
19
25
|
|
|
26
|
+
find_package(ZLIB REQUIRED)
|
|
27
|
+
|
|
20
28
|
file(GLOB_RECURSE CPP_SOURCES "*.cpp")
|
|
21
29
|
file(GLOB_RECURSE C_SOURCES "*.c")
|
|
22
30
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Get the script's directory (the project root)
|
|
4
|
+
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|
5
|
+
BUILD_DIR="$ROOT_DIR/build"
|
|
6
|
+
|
|
7
|
+
echo "--- Surgical cleanup initiated in: $BUILD_DIR ---"
|
|
8
|
+
|
|
9
|
+
# Check if the build directory exists
|
|
10
|
+
if [ ! -d "$BUILD_DIR" ]; then
|
|
11
|
+
echo "Build directory does not exist. Nothing to clean."
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Enter the build directory to operate safely
|
|
16
|
+
cd "$BUILD_DIR" || exit
|
|
17
|
+
|
|
18
|
+
# Remove critical CMake files and object files without deleting the build folder itself
|
|
19
|
+
echo "Removing cache files and build artifacts..."
|
|
20
|
+
rm -rf CMakeCache.txt CMakeFiles/ cmake_install.cmake Makefile bin/ lib/ examples/
|
|
21
|
+
|
|
22
|
+
# Optional: Recursively remove all .o, .a, and .so files if present
|
|
23
|
+
find . -name "*.o" -delete
|
|
24
|
+
find . -name "*.a" -delete
|
|
25
|
+
find . -name "*.so" -delete
|
|
26
|
+
|
|
27
|
+
echo "--- Cleanup complete. The build directory is now pristine. ---"
|
|
28
|
+
echo "You can now run: cmake -DBUILD_EXAMPLES=ON .."
|
|
@@ -24,7 +24,7 @@ void run_test() {
|
|
|
24
24
|
// test_suite.test_f();
|
|
25
25
|
// test_suite.test_g();
|
|
26
26
|
// test_suite.test_h();
|
|
27
|
-
test_suite.test_i();
|
|
27
|
+
// test_suite.test_i();
|
|
28
28
|
std::cout << "compute time =" << cpu_timer.stop() << std::endl;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -33,11 +33,13 @@ int main() {
|
|
|
33
33
|
cfg.threads = 4;
|
|
34
34
|
cfg.tiles = 4;
|
|
35
35
|
cfg.compress_unique = true;
|
|
36
|
-
cfg.
|
|
36
|
+
// cfg.treemap = true;
|
|
37
|
+
// cfg.connectivity_mode = Contrek::Connectivity::OMNIDIRECTIONAL;
|
|
37
38
|
|
|
38
39
|
CpuTimer cpu_timer;
|
|
39
40
|
cpu_timer.start();
|
|
40
41
|
std::cout << "--- Start Native Benchmark ---" << std::endl;
|
|
42
|
+
// auto result = Contrek::trace("../images/graphs_1024x1024.png", cfg);
|
|
41
43
|
auto result = Contrek::trace("../images/sample_10240x10240.png", cfg);
|
|
42
44
|
result->print_info();
|
|
43
45
|
std::cout << "Found polygons: " << result->groups << std::endl;
|
|
@@ -155,7 +155,7 @@ void Tests::test_d()
|
|
|
155
155
|
std::cout << "color =" << color << std::endl;
|
|
156
156
|
RGBNotMatcher not_matcher(color);
|
|
157
157
|
|
|
158
|
-
std::vector<std::string> arguments = {"--versus=a", "--compress_uniq"};
|
|
158
|
+
std::vector<std::string> arguments = {"--versus=a", "--compress_uniq", "--treemap"};
|
|
159
159
|
PolygonFinder pl(&png_bitmap, ¬_matcher, nullptr, &arguments);
|
|
160
160
|
ProcessResult *o = pl.process_info();
|
|
161
161
|
o->print_info();
|
|
@@ -129,86 +129,89 @@ void NodeCluster::plot(int versus) {
|
|
|
129
129
|
if ((this->nodes > 0) && (next_node != nullptr))
|
|
130
130
|
{ plot_node(poly.outer, next_node, root_node, versus, poly.bounds);
|
|
131
131
|
}
|
|
132
|
-
this->sequences.push_back(std::move(this->plot_sequence));
|
|
133
|
-
this->plot_sequence.clear();
|
|
134
132
|
|
|
135
|
-
if (poly.outer.size()
|
|
133
|
+
if (poly.outer.size() >= 2)
|
|
136
134
|
{ this->polygons.push_back(poly);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
135
|
+
this->sequences.push_back(std::move(this->plot_sequence));
|
|
136
|
+
|
|
137
|
+
int index_inner = 0;
|
|
138
|
+
while (inner_plot->size() > 0)
|
|
139
|
+
{ this->plot_sequence.clear();
|
|
140
|
+
std::vector<Point*> inner_sequence;
|
|
141
|
+
std::list<Node*>::iterator first_i;
|
|
142
|
+
Node *first = nullptr;
|
|
143
|
+
|
|
144
|
+
Listable *act = inner_plot->first();
|
|
145
|
+
do
|
|
146
|
+
{ if ((reinterpret_cast<Node*>(act))->tangs_count <= 2)
|
|
147
|
+
{ first = reinterpret_cast<Node*>(act);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}while((act = act->data_pointer[inner_plot->get_id()].next) != nullptr);
|
|
151
|
+
|
|
152
|
+
if (first == nullptr) first = reinterpret_cast<Node*>(inner_plot->first());
|
|
153
|
+
|
|
154
|
+
plot_sequence.push_back(first);
|
|
155
|
+
inner_plot->remove(first);
|
|
156
|
+
root_nodes->remove(first);
|
|
157
|
+
|
|
158
|
+
first->inner_index = index_inner;
|
|
159
|
+
|
|
160
|
+
Node* next_node = nullptr;
|
|
161
|
+
|
|
162
|
+
if (first->get_trackmax()) {
|
|
163
|
+
if (inner_v == Node::A) {
|
|
164
|
+
if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_start];
|
|
165
|
+
} else {
|
|
166
|
+
if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_start];
|
|
167
|
+
}
|
|
167
168
|
} else {
|
|
168
|
-
if (
|
|
169
|
+
if (inner_v == Node::A) {
|
|
170
|
+
if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_end];
|
|
171
|
+
} else {
|
|
172
|
+
if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_end];
|
|
173
|
+
}
|
|
169
174
|
}
|
|
170
|
-
} else {
|
|
171
|
-
if (inner_v == Node::A) {
|
|
172
|
-
if (first->lower_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_DOWN][first->lower_end];
|
|
173
|
-
} else {
|
|
174
|
-
if (first->upper_end >= 0) next_node = &this->vert_nodes[first->y + Node::T_UP][first->upper_end];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
175
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
if (next_node != nullptr)
|
|
177
|
+
{ inner_sequence.push_back(next_node->coords_entering_to(first, inner_v, Node::INNER));
|
|
178
|
+
plot_inner_node(inner_sequence, next_node, inner_v, first, root_node);
|
|
179
|
+
}
|
|
180
|
+
this->polygons.back().inner.push_back(inner_sequence);
|
|
181
|
+
this->inner_plot->grab(this->inner_new);
|
|
182
|
+
index_inner++;
|
|
181
183
|
}
|
|
182
|
-
|
|
183
|
-
this->
|
|
184
|
-
|
|
184
|
+
} else {
|
|
185
|
+
this->plot_sequence.clear();
|
|
186
|
+
}
|
|
187
|
+
if (this->options->treemap)
|
|
188
|
+
{ this->treemap.push_back(versus == Node::A ? this->test_in_hole_a(root_node) : this->test_in_hole_o(root_node));
|
|
185
189
|
}
|
|
190
|
+
index_order++;
|
|
186
191
|
} else {
|
|
187
|
-
this->sequences.push_back(std::move(this->plot_sequence));
|
|
188
192
|
this->plot_sequence.clear();
|
|
189
193
|
}
|
|
190
|
-
// tree
|
|
191
|
-
if (this->options->treemap)
|
|
192
|
-
{ this->treemap.push_back(versus == Node::A ? this->test_in_hole_a(root_node) : this->test_in_hole_o(root_node));
|
|
193
|
-
}
|
|
194
|
-
index_order++;
|
|
195
194
|
}
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
std::pair<int, int> NodeCluster::test_in_hole_a(Node* node)
|
|
199
198
|
{ if (node->outer_index > 0)
|
|
200
199
|
{ int start_left = node->abs_x_index - 1;
|
|
200
|
+
auto& row = this->vert_nodes[node->y];
|
|
201
|
+
int row_size = static_cast<int>(row.size());
|
|
202
|
+
if (start_left < 0 && row_size > 0) {
|
|
203
|
+
start_left = row_size - 1;
|
|
204
|
+
}
|
|
201
205
|
do {
|
|
202
206
|
Node* prev = &this->vert_nodes[node->y][start_left];
|
|
203
207
|
int cindex = prev->outer_index;
|
|
204
|
-
|
|
205
208
|
if ((cindex < node->outer_index) && (prev->track & Node::IMAX))
|
|
206
209
|
{ unsigned int start_right = node->abs_x_index;
|
|
207
210
|
unsigned int line_size = this->vert_nodes[node->y].size();
|
|
208
211
|
while (++start_right != line_size)
|
|
209
212
|
{ Node* tnext = &this->vert_nodes[node->y][start_right];
|
|
210
213
|
if (tnext->outer_index == cindex) {
|
|
211
|
-
if (tnext->track & Node::IMIN) return {cindex, prev->
|
|
214
|
+
if (tnext->track & Node::IMIN) return {cindex, prev->inner_right_index == -1 ? prev->inner_left_index : prev->inner_right_index};
|
|
212
215
|
else return {-1, -1};
|
|
213
216
|
}
|
|
214
217
|
}
|
|
@@ -222,18 +225,16 @@ std::pair<int, int> NodeCluster::test_in_hole_o(Node* node)
|
|
|
222
225
|
{ auto& line = this->vert_nodes[node->y];
|
|
223
226
|
const unsigned int line_size = line.size();
|
|
224
227
|
if (node->outer_index == 0 || &line.back() == node) return {-1, -1};
|
|
225
|
-
|
|
226
228
|
unsigned int start_left = node->abs_x_index + 1;
|
|
227
229
|
do {
|
|
228
230
|
Node* prev = &line[start_left];
|
|
229
231
|
int cindex = prev->outer_index;
|
|
230
|
-
|
|
231
232
|
if (cindex < node->outer_index && (prev->track & Node::IMIN))
|
|
232
233
|
{ int start_right = node->abs_x_index;
|
|
233
234
|
while (--start_right >= 0) {
|
|
234
235
|
Node* tnext = &line[start_right];
|
|
235
236
|
if (tnext->outer_index == cindex) {
|
|
236
|
-
if (tnext->track & Node::IMAX) return {cindex, prev->
|
|
237
|
+
if (tnext->track & Node::IMAX) return {cindex, prev->inner_left_index == -1 ? prev->inner_right_index : prev->inner_left_index};
|
|
237
238
|
else return {-1, -1};
|
|
238
239
|
}
|
|
239
240
|
}
|
|
@@ -247,15 +248,20 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
|
|
|
247
248
|
bool strict_bounds = this->options->strict_bounds;
|
|
248
249
|
while (current_node != nullptr) {
|
|
249
250
|
current_node->outer_index = start_node->outer_index;
|
|
250
|
-
current_node->inner_index = stop_at->inner_index;
|
|
251
251
|
root_nodes->remove(current_node);
|
|
252
252
|
inner_plot->remove(current_node);
|
|
253
253
|
|
|
254
254
|
Node *last_node = plot_sequence.back();
|
|
255
255
|
Node *next_node = current_node->my_next_inner(last_node, versus);
|
|
256
|
-
|
|
257
256
|
plot_sequence.push_back(current_node);
|
|
258
257
|
|
|
258
|
+
bool first_is_max = ((current_node->y > last_node->y) == (versus == Node::A));
|
|
259
|
+
if (first_is_max) {
|
|
260
|
+
if (current_node->inner_right_index == -1) current_node->inner_right_index = stop_at->inner_index;
|
|
261
|
+
} else {
|
|
262
|
+
if (current_node->inner_left_index == -1) current_node->inner_left_index = stop_at->inner_index;
|
|
263
|
+
}
|
|
264
|
+
|
|
259
265
|
bool plot = true;
|
|
260
266
|
if (next_node->y == last_node->y) {
|
|
261
267
|
Node *n;
|
|
@@ -273,7 +279,6 @@ void NodeCluster::plot_inner_node(std::vector<Point*>& sequence_coords, Node *no
|
|
|
273
279
|
}
|
|
274
280
|
}
|
|
275
281
|
} else if (strict_bounds) {
|
|
276
|
-
bool first_is_max = ((current_node->y > last_node->y) == (versus == Node::A));
|
|
277
282
|
sequence_coords.push_back(this->points_pool.acquire((first_is_max ? last_node->max_x : last_node->min_x), current_node->y));
|
|
278
283
|
sequence_coords.push_back(this->points_pool.acquire((first_is_max ? next_node->min_x : next_node->max_x), current_node->y));
|
|
279
284
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Primitives.h
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
struct Bounds {
|
|
12
|
+
int min;
|
|
13
|
+
int max;
|
|
14
|
+
};
|
data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ClippedPolygonFinder.h
CHANGED
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
|
|
15
15
|
class ClippedPolygonFinder : public PolygonFinder {
|
|
16
16
|
public:
|
|
17
|
-
ClippedPolygonFinder(Bitmap *bitmap, Matcher *matcher, int bm_start_x, int bm_end_x, std::vector<std::string
|
|
17
|
+
ClippedPolygonFinder(Bitmap *bitmap, Matcher *matcher, int bm_start_x, int bm_end_x, std::vector<std::string>* options = nullptr);
|
|
18
18
|
};
|
|
@@ -49,17 +49,20 @@ void Cluster::list_to_string(std::vector<Point*> list)
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
Tile* Cluster::merge_tiles() {
|
|
52
|
+
bool treemap = this->finder->options().treemap;
|
|
52
53
|
double tot_inner = 0;
|
|
53
54
|
double tot_outer = 0;
|
|
54
55
|
CpuTimer timer;
|
|
55
|
-
|
|
56
|
+
|
|
57
|
+
std::list<Shape*> new_shapes;
|
|
58
|
+
std::vector<InnerPolyline*> all_new_inner_polylines;
|
|
56
59
|
|
|
57
60
|
timer.start();
|
|
58
61
|
for (Tile* tile : tiles_) {
|
|
59
62
|
for (Shape *shape : tile->shapes()) {
|
|
60
63
|
if (shape->outer_polyline->is_on(Polyline::TRACKED_OUTER) || shape->outer_polyline->width() == 0) continue;
|
|
61
|
-
if (shape->outer_polyline->boundary())
|
|
62
|
-
|
|
64
|
+
if (shape->outer_polyline->boundary()) {
|
|
65
|
+
shape->outer_polyline->partition();
|
|
63
66
|
shape->outer_polyline->precalc();
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -68,51 +71,111 @@ Tile* Cluster::merge_tiles() {
|
|
|
68
71
|
|
|
69
72
|
for (Tile* tile : tiles_) {
|
|
70
73
|
std::list<Shape*>& src = tile->shapes();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (shape->outer_polyline->is_on(Polyline::TRACKED_OUTER) || shape->outer_polyline->width() == 0)
|
|
74
|
-
{ it++;
|
|
74
|
+
|
|
75
|
+
for (Shape* shape : src) {
|
|
76
|
+
if (shape->outer_polyline->is_on(Polyline::TRACKED_OUTER) || shape->outer_polyline->width() == 0) {
|
|
75
77
|
continue;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
if (shape->outer_polyline->boundary() && !shape->outer_polyline->next_tile_eligible_shapes().empty()) {
|
|
79
|
-
Sequence* new_outer = nullptr;
|
|
80
|
-
std::list<std::vector<Point*>> new_inners = shape->inner_polylines;
|
|
81
81
|
Cursor cursor(*this, shape);
|
|
82
|
+
Sequence* new_outer = nullptr;
|
|
83
|
+
|
|
82
84
|
timer.start();
|
|
83
85
|
new_outer = cursor.join_outers();
|
|
84
86
|
tot_outer += timer.stop();
|
|
85
87
|
|
|
86
88
|
timer.start();
|
|
87
|
-
std::vector<
|
|
89
|
+
std::vector<InnerPolyline*> new_inners = shape->inner_polylines;
|
|
90
|
+
std::vector<InnerPolyline*> new_inner_polylines = cursor.join_inners(new_outer);
|
|
88
91
|
tot_inner += timer.stop();
|
|
89
92
|
|
|
90
|
-
for (
|
|
91
|
-
new_inners.push_back(
|
|
92
|
-
|
|
93
|
+
for (InnerPolyline* inner_polyline : new_inner_polylines) {
|
|
94
|
+
new_inners.push_back(inner_polyline);
|
|
95
|
+
if (treemap) {
|
|
96
|
+
inner_polyline->sequence()->compute_vertical_bounds();
|
|
97
|
+
all_new_inner_polylines.push_back(inner_polyline);
|
|
98
|
+
for (const auto orphan_inner : cursor.orphan_inners()) {
|
|
99
|
+
if (orphan_inner->recombined()) {
|
|
100
|
+
all_new_inner_polylines.push_back(orphan_inner);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
93
104
|
}
|
|
105
|
+
|
|
94
106
|
for (auto s : cursor.orphan_inners()) {
|
|
95
107
|
new_inners.push_back(s);
|
|
96
108
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
|
|
110
|
+
Polyline* polyline = tile->shapes_pool->acquire_polyline(tile, new_outer->to_vector(), std::nullopt);
|
|
111
|
+
Shape* inserting_new_shape = tile->shapes_pool->acquire_shape(polyline, new_inners);
|
|
112
|
+
|
|
113
|
+
new_shapes.push_back(inserting_new_shape);
|
|
114
|
+
polyline->shape = inserting_new_shape;
|
|
115
|
+
inserting_new_shape->set_parent_shape(shape->parent_shape());
|
|
116
|
+
|
|
117
|
+
for (InnerPolyline* inner_polyline : new_inner_polylines) {
|
|
118
|
+
inner_polyline->sequence()->shape = inserting_new_shape;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (treemap) {
|
|
122
|
+
for (const auto merged_shape : cursor.shapes_sequence()) {
|
|
123
|
+
merged_shape->merged_to_shape = inserting_new_shape;
|
|
124
|
+
}
|
|
125
|
+
this->assign_ancestry(inserting_new_shape, all_new_inner_polylines);
|
|
126
|
+
}
|
|
127
|
+
|
|
102
128
|
} else {
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
if (treemap && !shape->reassociation_skip && shape->parent_shape() == nullptr) {
|
|
130
|
+
this->assign_ancestry(shape, all_new_inner_polylines);
|
|
131
|
+
}
|
|
132
|
+
new_shapes.push_back(shape);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (treemap) {
|
|
138
|
+
for (Tile* tile : tiles_) {
|
|
139
|
+
for (Shape* shape : tile->shapes()) {
|
|
140
|
+
Shape* parent = shape->parent_shape();
|
|
141
|
+
while (parent && parent->merged_to_shape != nullptr) {
|
|
142
|
+
parent = parent->merged_to_shape;
|
|
143
|
+
}
|
|
144
|
+
if (parent != shape->parent_shape()) {
|
|
145
|
+
shape->set_parent_shape(parent);
|
|
146
|
+
}
|
|
105
147
|
}
|
|
106
148
|
}
|
|
107
149
|
}
|
|
150
|
+
|
|
108
151
|
double past_tot_outer = tiles_.front()->benchmarks.outer + tiles_.back()->benchmarks.outer;
|
|
109
152
|
double past_tot_inner = tiles_.front()->benchmarks.inner + tiles_.back()->benchmarks.inner;
|
|
153
|
+
|
|
110
154
|
Benchmarks b{
|
|
111
155
|
tot_outer + past_tot_outer,
|
|
112
156
|
tot_inner + past_tot_inner
|
|
113
157
|
};
|
|
114
|
-
|
|
115
|
-
tile
|
|
116
|
-
|
|
158
|
+
|
|
159
|
+
Tile* tile = new Tile(
|
|
160
|
+
this->finder, tiles_.front()->start_x(), tiles_.back()->end_x(), tiles_.front()->name() + tiles_.back()->name(), b);
|
|
161
|
+
|
|
162
|
+
tile->assign_shapes(new_shapes);
|
|
163
|
+
for (Tile* old_tile : tiles_) {
|
|
164
|
+
tile->adopt(old_tile);
|
|
165
|
+
}
|
|
166
|
+
return tile;
|
|
117
167
|
}
|
|
118
168
|
|
|
169
|
+
void Cluster::assign_ancestry(Shape *shape, std::vector<InnerPolyline*>& inner_polylines)
|
|
170
|
+
{ for (auto* inner_polyline : inner_polylines) {
|
|
171
|
+
if (shape->outer_polyline->vert_bounds_intersect(inner_polyline->vertical_bounds())) {
|
|
172
|
+
if (shape->outer_polyline->within(inner_polyline->raw())) {
|
|
173
|
+
shape->set_parent_shape(inner_polyline->shape());
|
|
174
|
+
shape->parent_inner_polyline = inner_polyline;
|
|
175
|
+
for (auto* children_shape : shape->children_shapes) {
|
|
176
|
+
children_shape->reassociation_skip = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|