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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +50 -23
  5. data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +19 -11
  6. data/ext/cpp_polygon_finder/PolygonFinder/clean.sh +28 -0
  7. data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +4 -2
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +1 -1
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +2 -0
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +66 -61
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Primitives.h +14 -0
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ClippedPolygonFinder.h +1 -1
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +86 -23
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +3 -0
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +24 -57
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +7 -13
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +12 -9
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -1
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +55 -0
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +32 -0
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +1 -1
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +38 -23
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +1 -2
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +21 -0
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +4 -0
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +1 -0
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +14 -0
  28. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +14 -0
  29. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.cpp +15 -6
  30. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.h +11 -3
  31. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp +42 -0
  32. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h +34 -0
  33. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +72 -11
  34. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +9 -1
  35. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +4 -0
  36. data/lib/contrek/finder/concurrent/cluster.rb +58 -9
  37. data/lib/contrek/finder/concurrent/cursor.rb +11 -19
  38. data/lib/contrek/finder/concurrent/finder.rb +8 -2
  39. data/lib/contrek/finder/concurrent/inner_polyline.rb +49 -0
  40. data/lib/contrek/finder/concurrent/part.rb +26 -12
  41. data/lib/contrek/finder/concurrent/polyline.rb +46 -0
  42. data/lib/contrek/finder/concurrent/queueable.rb +0 -17
  43. data/lib/contrek/finder/concurrent/sequence.rb +21 -0
  44. data/lib/contrek/finder/concurrent/shape.rb +29 -2
  45. data/lib/contrek/finder/concurrent/tile.rb +36 -7
  46. data/lib/contrek/finder/node.rb +3 -1
  47. data/lib/contrek/finder/node_cluster.rb +56 -48
  48. data/lib/contrek/version.rb +1 -1
  49. data/lib/contrek.rb +1 -0
  50. metadata +9 -2
@@ -0,0 +1,42 @@
1
+ /*
2
+ * ShapePool.cpp
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
+
10
+ #include <vector>
11
+ #include "Shape.h"
12
+ #include "ShapePool.h"
13
+
14
+ Shape* ShapePool::acquire_shape(Polyline* outer_polyline, const std::vector<InnerPolyline*>& inner_polylines) {
15
+ shapes_storage.emplace_back(outer_polyline, inner_polylines);
16
+ Shape* shape = &shapes_storage.back();
17
+ return shape;
18
+ }
19
+
20
+ InnerPolyline* ShapePool::acquire_inner_polyline(std::vector<Point*> coords, Shape* shape, bool rec) {
21
+ inner_polylines_storage.emplace_back(coords, shape, rec);
22
+ InnerPolyline* ip = &inner_polylines_storage.back();
23
+ return ip;
24
+ }
25
+
26
+ InnerPolyline* ShapePool::acquire_inner_polyline(Sequence* seq) {
27
+ inner_polylines_storage.emplace_back(seq);
28
+ InnerPolyline* ip = &inner_polylines_storage.back();
29
+ return ip;
30
+ }
31
+
32
+ Sequence* ShapePool::acquire_sequence() {
33
+ sequences_storage.emplace_back();
34
+ Sequence* s = &sequences_storage.back();
35
+ return s;
36
+ }
37
+
38
+ Polyline* ShapePool::acquire_polyline(Tile* tile, const std::vector<Point*>& polygon, const std::optional<RectBounds>& bounds = std::nullopt) {
39
+ polylines_storage.emplace_back(tile, polygon, bounds);
40
+ Polyline* p = &polylines_storage.back();
41
+ return p;
42
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ * ShapePool.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
+
10
+ #pragma once
11
+ #include <deque>
12
+ #include <vector>
13
+ #include "Shape.h"
14
+ #include "../RectBounds.h"
15
+ #include "Polyline.h"
16
+
17
+ class Shape;
18
+ class Tile;
19
+ class Polyline;
20
+ class InnerPolyline;
21
+ class ShapePool {
22
+ private:
23
+ std::deque<Shape> shapes_storage;
24
+ std::deque<InnerPolyline> inner_polylines_storage;
25
+ std::deque<Sequence> sequences_storage;
26
+ std::deque<Polyline> polylines_storage;
27
+
28
+ public:
29
+ Shape* acquire_shape(Polyline* outer_polyline, const std::vector<InnerPolyline*>& inner_polylines);
30
+ InnerPolyline* acquire_inner_polyline(std::vector<Point*> coords, Shape* s, bool rec);
31
+ InnerPolyline* acquire_inner_polyline(Sequence* seq);
32
+ Sequence* acquire_sequence();
33
+ Polyline* acquire_polyline(Tile* tile, const std::vector<Point*>& polygon, const std::optional<RectBounds>& bounds);
34
+ };
@@ -11,6 +11,8 @@
11
11
  #include <iostream>
12
12
  #include <list>
13
13
  #include <utility>
14
+ #include <vector>
15
+ #include <unordered_map>
14
16
  #include "Tile.h"
15
17
  #include "Shape.h"
16
18
  #include "Polyline.h"
@@ -24,13 +26,15 @@ Tile::Tile(Finder *finder, int start_x, int end_x, std::string name, const Bench
24
26
  end_x_(end_x),
25
27
  name_(name),
26
28
  benchmarks(b) {
29
+ this->shapes_pool = new ShapePool();
30
+ this->shapes_pools.push_back(this->shapes_pool);
27
31
  }
28
32
 
29
33
  Tile::~Tile() {
30
- for (Shape* shape : shapes_) {
31
- delete shape;
32
- }
33
34
  shapes_.clear();
35
+ for (ShapePool* pool : this->shapes_pools) {
36
+ delete pool;
37
+ }
34
38
  }
35
39
 
36
40
  bool Tile::whole() {
@@ -46,18 +50,37 @@ bool Tile::right() {
46
50
  }
47
51
 
48
52
  void Tile::initial_process(ClippedPolygonFinder *finder) {
49
- this->assign_raw_polygons(finder->get_cluster()->polygons);
53
+ this->assign_raw_polygons(finder->get_cluster()->polygons, finder->get_cluster()->treemap);
50
54
  }
51
55
 
52
- void Tile::assign_raw_polygons(const std::list<Polygon>& raw_polylines) {
56
+ void Tile::assign_raw_polygons(const std::list<Polygon>& raw_polylines, const std::vector<std::pair<int, int>>& treemap) {
57
+ std::unordered_map<int, Shape*> shapes_map;
58
+ int polyline_index = 0;
53
59
  for (const auto& raw_polyline : raw_polylines)
54
60
  { if (raw_polyline.bounds.width() > 0)
55
- { Polyline* polyline = new Polyline(this, raw_polyline.outer, raw_polyline.bounds);
56
- { Shape* shape = new Shape(polyline, raw_polyline.inner);
57
- polyline->shape = shape;
58
- this->shapes_.push_back(shape);
61
+ { Polyline* polyline = this->shapes_pool->acquire_polyline(this, raw_polyline.outer, raw_polyline.bounds);
62
+ std::vector<InnerPolyline*> inner_polylines_list;
63
+ for (auto& raw_points : raw_polyline.inner) {
64
+ inner_polylines_list.push_back(this->shapes_pool->acquire_inner_polyline(raw_points, nullptr, false));
65
+ }
66
+ Shape* shape = this->shapes_pool->acquire_shape(polyline, inner_polylines_list);
67
+ polyline->shape = shape;
68
+ this->shapes_.push_back(shape);
69
+
70
+ if (treemap.size() > 0)
71
+ { auto treemap_entry = treemap[polyline_index];
72
+ shapes_map[polyline_index] = shape;
73
+ if (treemap_entry.first != -1 && treemap_entry.second != -1) {
74
+ auto it = shapes_map.find(treemap_entry.first);
75
+ if (it != shapes_map.end()) {
76
+ Shape* parent = it->second;
77
+ shape->set_parent_shape(parent);
78
+ shape->parent_inner_polyline = parent->inner_polylines[treemap_entry.second];
79
+ }
80
+ }
59
81
  }
60
82
  }
83
+ polyline_index++;
61
84
  }
62
85
  }
63
86
 
@@ -73,7 +96,12 @@ bool Tile::tg_border(const Point& coord) {
73
96
  }
74
97
 
75
98
  void Tile::info() {
76
- std::cout << "TILE name=" << name_ << " start_x=" << start_x_ << " end_x=" << end_x_ << std::endl;
99
+ std::cout << this->toString() << std::endl;
100
+ }
101
+
102
+ std::string Tile::toString() {
103
+ std::string s = "TILE name=" + name_ + " start_x=" + std::to_string(start_x_) + " end_x=" + std::to_string(end_x_);
104
+ return s;
77
105
  }
78
106
 
79
107
  const std::list<Shape*>& Tile::boundary_shapes()
@@ -96,7 +124,7 @@ std::list<Polygon> Tile::to_raw_polygons()
96
124
  poly.outer = s->outer_polyline->raw();
97
125
  if (!s->inner_polylines.empty()) {
98
126
  for (auto inner : s->inner_polylines) {
99
- poly.inner.push_back(inner);
127
+ poly.inner.push_back(inner->raw());
100
128
  }
101
129
  }
102
130
  retme.push_back(std::move(poly));
@@ -104,3 +132,36 @@ std::list<Polygon> Tile::to_raw_polygons()
104
132
  }
105
133
  return(retme);
106
134
  }
135
+
136
+ std::vector<std::pair<int, int>> Tile::compute_treemap()
137
+ { std::unordered_map<Shape*, int> shapes_map;
138
+ int current_index = 0;
139
+
140
+ for (auto* shape : this->shapes_) {
141
+ if (shape->outer_polyline->is_empty()) continue;
142
+ shapes_map[shape] = current_index++;
143
+ }
144
+ std::vector<std::pair<int, int>> treemap;
145
+ treemap.reserve(shapes_map.size());
146
+
147
+ for (auto* shape : this->shapes_) {
148
+ if (shape->outer_polyline->is_empty()) continue;
149
+ if (shape->parent_shape() != nullptr) {
150
+ int p_idx = shapes_map[shape->parent_shape()];
151
+ const auto& inners = shape->parent_shape()->inner_polylines;
152
+ auto it = std::find(inners.begin(), inners.end(), shape->parent_inner_polyline);
153
+ int inner_idx = static_cast<int>(std::distance(inners.begin(), it));
154
+ treemap.push_back({p_idx, inner_idx});
155
+ } else {
156
+ treemap.push_back({-1, -1});
157
+ }
158
+ }
159
+ return treemap;
160
+ }
161
+
162
+ void Tile::adopt(Tile* other) {
163
+ for (ShapePool* pool : other->shapes_pools) {
164
+ this->shapes_pools.push_back(pool);
165
+ }
166
+ other->shapes_pools.clear();
167
+ }
@@ -6,11 +6,14 @@
6
6
  * Licensed under the GNU Affero General Public License v3 (AGPLv3).
7
7
  * See the LICENSE file in this directory for the full license text.
8
8
  */
9
+
9
10
  #pragma once
10
11
  #include <string>
11
12
  #include <list>
13
+ #include <vector>
12
14
  #include "Finder.h"
13
15
  #include "ClippedPolygonFinder.h"
16
+ #include "ShapePool.h"
14
17
 
15
18
  class Finder;
16
19
  class Cluster;
@@ -51,7 +54,12 @@ class Tile {
51
54
  void info();
52
55
  bool tg_border(const Point& coord);
53
56
  void assign_shapes(std::list<Shape*>& shapes);
54
- void assign_raw_polygons(const std::list<Polygon>& raw_polylines);
57
+ void assign_raw_polygons(const std::list<Polygon>& raw_polylines, const std::vector<std::pair<int, int>>& treemap);
55
58
  std::list<Polygon> to_raw_polygons();
59
+ std::vector<std::pair<int, int>> compute_treemap();
60
+ std::string toString();
56
61
  Benchmarks benchmarks;
62
+ std::vector<ShapePool*> shapes_pools;
63
+ ShapePool* shapes_pool;
64
+ void adopt(Tile* other);
57
65
  };
@@ -63,6 +63,8 @@
63
63
  #include "PolygonFinder/src/polygon/finder/concurrent/Tile.cpp"
64
64
  #include "PolygonFinder/src/polygon/finder/concurrent/Polyline.h"
65
65
  #include "PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp"
66
+ #include "PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h"
67
+ #include "PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp"
66
68
  #include "PolygonFinder/src/polygon/finder/concurrent/Partitionable.h"
67
69
  #include "PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp"
68
70
  #include "PolygonFinder/src/polygon/finder/concurrent/Shape.h"
@@ -91,6 +93,8 @@
91
93
  #include "PolygonFinder/src/polygon/finder/concurrent/HorizontalMerger.cpp"
92
94
  #include "PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.h"
93
95
  #include "PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp"
96
+ #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.h"
97
+ #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp"
94
98
  extern "C" {
95
99
  #include "PolygonFinder/src/polygon/bitmaps/spng.h"
96
100
  }
@@ -22,10 +22,12 @@ module Contrek
22
22
  # outer sequences and both the old and new inner ones. Shapes that are not in the contact area are directly
23
23
  # reassigned unchanged to the new shape.
24
24
  def merge_tiles!
25
+ treemap = @finder.options[:treemap]
25
26
  tot_inner = 0
26
27
  tot_outer = 0
27
28
 
28
29
  new_shapes = []
30
+ all_new_inner_polylines = []
29
31
  tot_outer += Benchmark.measure do
30
32
  @tiles.each do |tile|
31
33
  tile.shapes.each do |shape|
@@ -43,30 +45,61 @@ module Contrek
43
45
  next if shape.outer_polyline.on?(Polyline::TRACKED_OUTER) || shape.outer_polyline.width == 0
44
46
 
45
47
  if shape.outer_polyline.boundary? && shape.outer_polyline.next_tile_eligible_shapes.any?
46
- new_outer = nil
47
- new_inners = shape.inner_polylines
48
48
  cursor = Cursor.new(cluster: self, shape: shape)
49
49
 
50
+ new_outer = nil
50
51
  tot_outer += Benchmark.measure do
51
52
  new_outer = cursor.join_outers!
52
53
  end.real
54
+
55
+ new_inners = shape.inner_polylines
56
+ new_inner_polylines = []
53
57
  tot_inner += Benchmark.measure do
54
- new_inner_sequences = cursor.join_inners!(new_outer)
55
- new_inners += new_inner_sequences.map { |s| s.to_a } if new_inner_sequences.any?
58
+ new_inner_polylines = cursor.join_inners!(new_outer)
59
+ new_inners += new_inner_polylines
60
+ if treemap
61
+ new_inner_polylines.each { |p| p.sequence.compute_vertical_bounds! }
62
+ all_new_inner_polylines += new_inner_polylines
63
+ cursor.orphan_inners.each do |orphan_inner|
64
+ all_new_inner_polylines << orphan_inner if orphan_inner.recombined
65
+ end
66
+ end
56
67
  new_inners += cursor.orphan_inners
57
68
  end.real
58
69
 
59
- new_shapes << Shape.new.tap do |shape|
60
- polyline = Polyline.new(tile: tile, polygon: new_outer.to_a)
61
- shape.outer_polyline = polyline
62
- polyline.shape = shape
63
- shape.inner_polylines = new_inners
70
+ polyline = Polyline.new(tile: tile, polygon: new_outer.to_a)
71
+ inserting_new_shape = Shape.init_by(polyline, new_inners)
72
+ new_shapes << inserting_new_shape
73
+ polyline.shape = inserting_new_shape
74
+ inserting_new_shape.set_parent_shape(shape.parent_shape)
75
+
76
+ new_inner_polylines.each { |inner_polyline| inner_polyline.sequence.shape = inserting_new_shape }
77
+
78
+ if treemap
79
+ cursor.shapes_sequence.each do |merged_shape|
80
+ merged_shape.merged_to_shape = inserting_new_shape
81
+ end
82
+ assign_ancestry(inserting_new_shape, all_new_inner_polylines)
64
83
  end
65
84
  else
85
+ if treemap && !shape.reassociation_skip && shape.parent_shape.nil?
86
+ assign_ancestry(shape, all_new_inner_polylines)
87
+ end
66
88
  new_shapes << shape
67
89
  end
68
90
  end
69
91
  end
92
+
93
+ if treemap
94
+ @tiles.each do |tile|
95
+ tile.shapes.each do |shape|
96
+ if (merged_to_shape = shape.parent_shape&.merged_to_shape)
97
+ shape.set_parent_shape(merged_to_shape)
98
+ end
99
+ end
100
+ end
101
+ end
102
+
70
103
  past_tot_outer = @tiles.first.benchmarks[:outer] + @tiles.last.benchmarks[:outer]
71
104
  past_tot_inner = @tiles.first.benchmarks[:inner] + @tiles.last.benchmarks[:inner]
72
105
 
@@ -81,6 +114,22 @@ module Contrek
81
114
  tile.assign_shapes!(new_shapes)
82
115
  tile
83
116
  end
117
+
118
+ private
119
+
120
+ def assign_ancestry(shape, inner_polylines)
121
+ inner_polylines.each do |inner_polyline|
122
+ if shape.outer_polyline.vert_bounds_intersect?(inner_polyline.vertical_bounds)
123
+ if shape.outer_polyline.within?(inner_polyline.raw)
124
+ shape.set_parent_shape(inner_polyline.shape)
125
+ shape.parent_inner_polyline = inner_polyline
126
+ shape.children_shapes.each do |children_shape|
127
+ children_shape.reassociation_skip = true
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
84
133
  end
85
134
  end
86
135
  end
@@ -1,7 +1,8 @@
1
1
  module Contrek
2
2
  module Concurrent
3
3
  class Cursor
4
- attr_reader :orphan_inners
4
+ attr_reader :orphan_inners, :shapes_sequence
5
+
5
6
  def initialize(cluster:, shape:)
6
7
  @shapes_sequence = Set.new([shape])
7
8
  @cluster = cluster
@@ -90,7 +91,9 @@ module Contrek
90
91
  end
91
92
  combine!(inject_sequences_right, inject_sequences_left).each do |sewn_sequence|
92
93
  sewn_sequence.uniq!
93
- @orphan_inners << sewn_sequence if sewn_sequence.size > 1 && sewn_sequence.map { |c| c[:x] }.uniq.size > 1 # segmenti non sono ammessi, solo aree
94
+ if sewn_sequence.size > 1 && sewn_sequence.map { |c| c[:x] }.uniq.size > 1 # only areas
95
+ @orphan_inners << InnerPolyline.new(shape: shape, raw_coordinates: sewn_sequence, recombined: true)
96
+ end
94
97
  end
95
98
  missing_outer_polyline.clear!
96
99
  polyline.mixed_tile_origin = true
@@ -147,7 +150,7 @@ module Contrek
147
150
  end
148
151
 
149
152
  def collect_inner_sequences(outer_seq)
150
- return_sequences = []
153
+ return_inner_polylines = []
151
154
  @shapes_sequence.each do |shape|
152
155
  polyline = shape.outer_polyline
153
156
  polyline.parts.each do |part|
@@ -165,16 +168,17 @@ module Contrek
165
168
  !(polyline.tile.tg_border?(position.payload) && position.end_point.queues.include?(outer_seq))
166
169
  end
167
170
  end
168
- return_sequences << retme_sequence if retme_sequence.is_not_vertical
171
+ if retme_sequence.is_not_vertical
172
+ return_inner_polylines << InnerPolyline.new(sequence: retme_sequence)
173
+ end
169
174
  end
170
175
  end
171
176
  end
172
- return_sequences
177
+ return_inner_polylines
173
178
  end
174
179
 
175
180
  def traverse_inner(act_part, all_parts, bounds)
176
181
  return if act_part == all_parts.first
177
-
178
182
  if act_part.size > 0
179
183
  min_y, max_y = act_part.to_a.minmax_by { |p| p[:y] }
180
184
  bounds[:min] = [bounds[:min], min_y[:y]].min
@@ -191,9 +195,8 @@ module Contrek
191
195
  next if dest_part.trasmuted || dest_part.is?(Part::EXCLUSIVE)
192
196
  dest_part_versus = dest_part.versus
193
197
  next if dest_part_versus != 0 && dest_part_versus == act_part.versus
194
-
195
198
  if dest_part.intersect_part?(act_part)
196
- link_seq = duplicates_intersection(dest_part, act_part)
199
+ link_seq = dest_part.continuum_to?(act_part)
197
200
  if link_seq.any?
198
201
  ins_part = Part.new(Part::ADDED, act_part.polyline)
199
202
  link_seq.each do |pos|
@@ -217,17 +220,6 @@ module Contrek
217
220
  end
218
221
  # rubocop:enable Lint/NonLocalExitFromIterator
219
222
 
220
- # The sequences should contain inverted parts, e.g., 4,5,6,6,5,4. These parts must be
221
- # removed from the sequences, and the remaining elements are compared. Only the
222
- # elements that appear once between the two sequences are kept. This represents
223
- # a connection between parts inserted afterwards.
224
- # TODO evaluate the adoption of remove_adjacent_pairs!
225
- def duplicates_intersection(part_a, part_b)
226
- a1 = part_a.inverts ? Part.remove_adjacent_pairs(part_a.to_endpoints) : part_a.to_endpoints
227
- b1 = part_b.inverts ? Part.remove_adjacent_pairs(part_b.to_endpoints) : part_b.to_endpoints
228
- (a1 - b1) + (b1 - a1)
229
- end
230
-
231
223
  # example
232
224
  # a = [[A],[B,C]]
233
225
  # b = [[D],[E,F]]
@@ -3,7 +3,7 @@ module Contrek
3
3
  class Finder
4
4
  prepend Poolable
5
5
 
6
- attr_reader :maximum_width
6
+ attr_reader :maximum_width, :options
7
7
 
8
8
  # Supported options
9
9
  # - number_of_threads: number of threads that can be used by the process. If set to 0, it works in
@@ -47,7 +47,12 @@ module Contrek
47
47
  finder = ClippedPolygonFinder.new(
48
48
  bitmap: bitmap,
49
49
  matcher: matcher,
50
- options: {versus: current_versus, bounds: true, connectivity: @options[:connectivity]}.compact,
50
+ options: {
51
+ versus: current_versus,
52
+ bounds: true,
53
+ treemap: @options[:treemap],
54
+ connectivity: @options[:connectivity]
55
+ }.compact,
51
56
  start_x: payload[:tile_start_x],
52
57
  end_x: payload[:tile_end_x]
53
58
  )
@@ -88,6 +93,7 @@ module Contrek
88
93
  width: @maximum_width,
89
94
  height: @height
90
95
  }
96
+ metadata[:treemap] = @whole_tile.compute_treemap if options[:treemap]
91
97
  Contrek::Finder::Result.new(raw_polygons, metadata)
92
98
  end
93
99
 
@@ -0,0 +1,49 @@
1
+ module Contrek
2
+ module Concurrent
3
+ class InnerPolyline
4
+ attr_reader :sequence, :recombined
5
+
6
+ def initialize(shape: nil, raw_coordinates: [], sequence: nil, recombined: false)
7
+ @raw = raw_coordinates if raw_coordinates
8
+ @sequence = sequence if sequence
9
+ @recombined = recombined
10
+ @shape = shape
11
+ end
12
+
13
+ def raw
14
+ @sequence ? @sequence.to_a : @raw
15
+ end
16
+
17
+ def vertical_bounds
18
+ if @sequence
19
+ @sequence.vertical_bounds
20
+ else
21
+ raw_vertical_bounds
22
+ end
23
+ end
24
+
25
+ def shape
26
+ if @sequence
27
+ @sequence.shape
28
+ else
29
+ @shape
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def raw_vertical_bounds
36
+ @vertical_bounds ||= begin
37
+ min_y = Float::INFINITY
38
+ max_y = 0
39
+ @raw.each do |pos|
40
+ y = pos[:y]
41
+ min_y = y if y < min_y
42
+ max_y = y if y > max_y
43
+ end
44
+ {min: min_y, max: max_y}
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -81,10 +81,6 @@ module Contrek
81
81
  intersect
82
82
  end
83
83
 
84
- def to_endpoints
85
- map(&:end_point)
86
- end
87
-
88
84
  def orient!
89
85
  @versus = if size <= 1 || (size == 2 && @inverts)
90
86
  0
@@ -93,16 +89,34 @@ module Contrek
93
89
  end
94
90
  end
95
91
 
96
- def self.remove_adjacent_pairs(array = nil)
97
- n = array.size
98
- (0...(n - 1)).each do |i|
99
- if array[i] == array[i + 1]
100
- # Remove the pair and call recursively
101
- new_array = array[0...i] + array[(i + 2)..]
102
- return remove_adjacent_pairs(new_array)
92
+ def continuum_to?(other)
93
+ return [] if size <= 2 && inverts && other.size <= 2 && other.inverts
94
+ return [] if other.head.nil?
95
+
96
+ target = other.head.payload
97
+ cursor = tail
98
+ while cursor
99
+ if cursor.payload == target
100
+ s = cursor
101
+ o = other.head
102
+ match = true
103
+ nodes = []
104
+ while s && o
105
+ if s.payload != o.payload
106
+ match = false
107
+ break
108
+ end
109
+ nodes << s.end_point
110
+ s = s.next
111
+ o = o.next
112
+ end
113
+ if match && s.nil?
114
+ return nodes
115
+ end
103
116
  end
117
+ cursor = cursor.prev
104
118
  end
105
- array
119
+ []
106
120
  end
107
121
  end
108
122
  end
@@ -119,6 +119,33 @@ module Contrek
119
119
  !(@max_y < other.min_y || other.max_y < @min_y)
120
120
  end
121
121
 
122
+ def vert_bounds_intersect?(vertical_bounds)
123
+ !(@max_y < vertical_bounds[:min] || vertical_bounds[:max] < @min_y)
124
+ end
125
+
126
+ # verifies if enclosed in the given sequence (raycasting technique)
127
+ def within?(raw_coords)
128
+ is_within = false
129
+ self_y = raw.first[:y]
130
+ self_x = raw.first[:x]
131
+ last_node = raw_coords.last
132
+ return false if last_node.nil?
133
+ lx = last_node[:x]
134
+ ly = last_node[:y]
135
+ raw_coords.each do |pos|
136
+ cx = pos[:x]
137
+ cy = pos[:y]
138
+ if (cy > self_y) != (ly > self_y)
139
+ intersect_x = lx + (self_y - ly).to_f * (cx - lx) / (cy - ly)
140
+ if self_x < intersect_x
141
+ is_within = !is_within
142
+ end
143
+ end
144
+ lx, ly = cx, cy
145
+ end
146
+ is_within
147
+ end
148
+
122
149
  def get_bounds
123
150
  {min_x: @min_x,
124
151
  max_x: @max_x,
@@ -126,6 +153,25 @@ module Contrek
126
153
  max_y: @max_y}
127
154
  end
128
155
 
156
+ def self.is_within?(test_seq, container_seq)
157
+ target = test_seq.first
158
+ return false unless target
159
+ tx, ty = target[:x], target[:y]
160
+ inside = false
161
+ j = container_seq.length - 1
162
+ container_seq.each_with_index do |p_i, i|
163
+ p_j = container_seq[j]
164
+ if (p_i[:y] > ty) != (p_j[:y] > ty)
165
+ intersect_x = (p_j[:x] - p_i[:x]) * (ty - p_i[:y]).to_f / (p_j[:y] - p_i[:y]) + p_i[:x]
166
+ if tx < intersect_x
167
+ inside = !inside
168
+ end
169
+ end
170
+ j = i
171
+ end
172
+ inside
173
+ end
174
+
129
175
  private
130
176
 
131
177
  def find_boundary
@@ -152,23 +152,6 @@ module Contrek
152
152
  def pop!
153
153
  rem(@tail)
154
154
  end
155
-
156
- def remove_adjacent_pairs!
157
- unless @tail.nil?
158
- pointer = @tail
159
- loop do
160
- break if pointer == @head
161
- if pointer.payload == pointer.prev&.payload
162
- later = pointer.next
163
- rem pointer.prev
164
- rem pointer
165
- pointer = later.nil? ? @tail : later
166
- redo
167
- end
168
- break unless (pointer = pointer.prev)
169
- end
170
- end
171
- end
172
155
  end
173
156
  end
174
157
  end