contrek 1.1.6 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +50 -23
  5. data/contrek.gemspec +1 -1
  6. data/ext/cpp_polygon_finder/PolygonFinder/CMakeLists.txt +19 -11
  7. data/ext/cpp_polygon_finder/PolygonFinder/clean.sh +28 -0
  8. data/ext/cpp_polygon_finder/PolygonFinder/examples/example.cpp +4 -2
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +1 -1
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +2 -0
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +2 -0
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +66 -61
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Primitives.h +14 -0
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ClippedPolygonFinder.h +1 -1
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +86 -23
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +3 -0
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +23 -29
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +7 -12
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +12 -9
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h +2 -1
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +55 -0
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +32 -0
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Merger.cpp +1 -1
  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 +10 -6
  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/polyline.rb +46 -0
  41. data/lib/contrek/finder/concurrent/sequence.rb +21 -0
  42. data/lib/contrek/finder/concurrent/shape.rb +29 -2
  43. data/lib/contrek/finder/concurrent/tile.rb +36 -7
  44. data/lib/contrek/finder/node.rb +3 -1
  45. data/lib/contrek/finder/node_cluster.rb +56 -48
  46. data/lib/contrek/version.rb +1 -1
  47. data/lib/contrek.rb +1 -0
  48. metadata +9 -9
  49. data/docs/images/strict_bounds_off.png +0 -0
  50. data/docs/images/strict_bounds_on.png +0 -0
  51. data/docs/images/stripes/whole.png +0 -0
  52. data/docs/images/stripes/whole_0.png +0 -0
  53. data/docs/images/stripes/whole_256.png +0 -0
  54. data/docs/images/stripes/whole_512.png +0 -0
  55. data/docs/images/stripes/whole_768.png +0 -0
@@ -11,14 +11,22 @@
11
11
  #include <list>
12
12
  #include <iostream>
13
13
  #include <vector>
14
+ #include "InnerPolyline.h"
14
15
 
15
16
  class Point;
16
17
  class Polyline;
17
18
  class Shape {
18
19
  public:
19
- Shape(Polyline* outer_polyline, const std::list<std::vector<Point*>>& inner_polylines);
20
- virtual ~Shape();
20
+ Shape(Polyline* outer_polyline, const std::vector<InnerPolyline*>& inner_polylines);
21
21
  Polyline* outer_polyline = nullptr;
22
- std::list<std::vector<Point*>> inner_polylines;
22
+ std::vector<InnerPolyline*> inner_polylines;
23
+ Shape* merged_to_shape = nullptr;
24
+ InnerPolyline* parent_inner_polyline = nullptr;
25
+ std::vector<Shape*> children_shapes;
23
26
  void clear_inner();
27
+ bool reassociation_skip = false;
28
+ Shape* parent_shape() { return parent_shape_; }
29
+ void set_parent_shape(Shape*);
30
+ private:
31
+ Shape* parent_shape_ = nullptr;
24
32
  };
@@ -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
@@ -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
@@ -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
@@ -1,8 +1,13 @@
1
1
  module Contrek
2
2
  module Concurrent
3
3
  class Sequence
4
+ attr_accessor :vertical_bounds, :shape
4
5
  prepend Queueable
5
6
 
7
+ def initialize
8
+ @vertical_bounds = nil
9
+ end
10
+
6
11
  def is_not_vertical
7
12
  return false if size < 2
8
13
  x0 = head.payload[:x]
@@ -13,6 +18,22 @@ module Contrek
13
18
  end
14
19
  false
15
20
  end
21
+
22
+ def compute_vertical_bounds!
23
+ return if size == 0
24
+ min_y = Float::INFINITY
25
+ max_y = 0
26
+ get_vector_cache.each do |pos|
27
+ y = pos[:y]
28
+ min_y = y if y < min_y
29
+ max_y = y if y > max_y
30
+ end
31
+ @vertical_bounds = {min: min_y, max: max_y}
32
+ end
33
+
34
+ def get_vector_cache
35
+ @vector_cache ||= to_a
36
+ end
16
37
  end
17
38
  end
18
39
  end
@@ -1,10 +1,37 @@
1
1
  module Contrek
2
2
  module Concurrent
3
3
  class Shape
4
- attr_accessor :outer_polyline, :inner_polylines
4
+ attr_accessor :outer_polyline, :inner_polylines, :merged_to_shape, :parent_inner_polyline,
5
+ :reassociation_skip
6
+ attr_reader :parent_shape, :children_shapes
7
+
8
+ def initialize
9
+ @parent_shape = nil
10
+ @merged_to_shape = nil
11
+ @parent_inner_polyline = nil
12
+ @children_shapes = []
13
+ @reassociation_skip = false
14
+ end
15
+
16
+ def self.init_by(set_outer_polyline, set_inner_polylines)
17
+ s = Shape.new
18
+ s.outer_polyline = set_outer_polyline
19
+ s.inner_polylines = set_inner_polylines
20
+ s
21
+ end
5
22
 
6
23
  def clear_inner!
7
- @inner_polylines = []
24
+ @inner_polylines.clear
25
+ end
26
+
27
+ def set_parent_shape(shape)
28
+ @parent_shape&.children_shapes&.delete(self)
29
+ @parent_shape = shape
30
+ shape.children_shapes << self if shape
31
+ end
32
+
33
+ def info
34
+ "Shape (outer_polyline = #{outer_polyline.named}, children count = #{@children_shapes.size}"
8
35
  end
9
36
  end
10
37
  end