contrek 1.3.0 → 1.3.2

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +3 -3
  5. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +24 -10
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +2 -2
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +2 -2
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.cpp +1 -1
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +2 -2
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +4 -2
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +6 -6
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +16 -10
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +2 -2
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +16 -20
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +3 -3
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +1 -9
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h +81 -0
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.cpp +9 -28
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/InnerPolyline.h +10 -11
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +30 -0
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +8 -3
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +41 -41
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +4 -2
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +5 -3
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +7 -5
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +1 -1
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +2 -2
  28. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +0 -14
  29. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +0 -2
  30. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Shape.cpp +5 -5
  31. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp +32 -26
  32. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h +7 -7
  33. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp +12 -31
  34. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h +10 -8
  35. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/SubPool.h +47 -0
  36. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/SvgStreamingMerger.h +43 -0
  37. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +2 -1
  38. data/lib/contrek/finder/concurrent/cluster.rb +6 -5
  39. data/lib/contrek/finder/concurrent/cursor.rb +2 -4
  40. data/lib/contrek/finder/concurrent/finder.rb +1 -12
  41. data/lib/contrek/finder/concurrent/inner_polyline.rb +8 -25
  42. data/lib/contrek/finder/concurrent/part.rb +25 -1
  43. data/lib/contrek/finder/concurrent/partitionable.rb +39 -34
  44. data/lib/contrek/finder/concurrent/sequence.rb +1 -17
  45. data/lib/contrek/version.rb +1 -1
  46. metadata +5 -2
@@ -0,0 +1,47 @@
1
+ /*
2
+ * SubPool.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 <memory>
13
+ #include <utility>
14
+
15
+ class ShapePool;
16
+
17
+ template <typename T>
18
+ class SubPool {
19
+ private:
20
+ std::unique_ptr<std::deque<T>> storage_;
21
+ uint32_t active_count_ = 0;
22
+ ShapePool* macro_pool_ = nullptr;
23
+
24
+ public:
25
+ explicit SubPool(ShapePool* macro_pool) : macro_pool_(macro_pool) {
26
+ storage_ = std::make_unique<std::deque<T>>();
27
+ }
28
+
29
+ template <typename... Args>
30
+ T* acquire(Args&&... args) {
31
+ storage_->emplace_back(std::forward<Args>(args)...);
32
+ T* ptr = &storage_->back();
33
+ active_count_++;
34
+ return ptr;
35
+ }
36
+
37
+ void decrement() {
38
+ active_count_--;
39
+ if (active_count_ == 0 && storage_ != nullptr) {
40
+ storage_.reset();
41
+ }
42
+ }
43
+
44
+ bool is_released() const {
45
+ return storage_ == nullptr;
46
+ }
47
+ };
@@ -0,0 +1,43 @@
1
+ /*
2
+ * SvgStreamingMerger.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 <string>
12
+ #include "StreamingMerger.h"
13
+
14
+ class SvgStreamingMerger : public StreamingMerger {
15
+ private:
16
+ std::string svg_css() {
17
+ return ".out{fill:none;stroke:red;stroke-width:1;}.in{fill:none;stroke:green;stroke-width:1;}.out:hover{stroke:yellow;}";
18
+ }
19
+
20
+ protected:
21
+ void write_header() override {
22
+ *stream << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" << total_width
23
+ << "\" height=\"" << total_height << "\"><style>" << svg_css() << "</style>";
24
+ }
25
+ void write_footer() override {
26
+ *stream << "</svg>";
27
+ }
28
+ void write_outer_polygon_start() override {
29
+ *stream << "<polygon points=\"";
30
+ }
31
+ void write_outer_polygon_end() override {
32
+ *stream << "\" class=\"out\"/>";
33
+ }
34
+ void write_inner_polygon_start() override {
35
+ *stream << "<polygon points=\"";
36
+ }
37
+ void write_inner_polygon_end() override {
38
+ *stream << "\" class=\"in\"/>";
39
+ }
40
+
41
+ public:
42
+ using StreamingMerger::StreamingMerger;
43
+ };
@@ -96,6 +96,7 @@
96
96
  #include "PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp"
97
97
  #include "PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h"
98
98
  #include "PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp"
99
+ #include "PolygonFinder/src/polygon/finder/concurrent/SvgStreamingMerger.h"
99
100
  #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.h"
100
101
  #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp"
101
102
  extern "C" {
@@ -315,7 +316,7 @@ StreamingMerger* create_streaming_merger(Object self,
315
316
  int total_width,
316
317
  int total_height) {
317
318
  OfstreamWrapper* wrapper = Rice::detail::From_Ruby<OfstreamWrapper*>().convert(stream_obj.value());
318
- return new StreamingMerger(number_of_threads, options, &wrapper->get_stream(), total_width, total_height);
319
+ return new SvgStreamingMerger(number_of_threads, options, &wrapper->get_stream(), total_width, total_height);
319
320
  }
320
321
 
321
322
  OfstreamWrapper* create_ofstream(Object self, std::string path) {
@@ -5,7 +5,7 @@ module Contrek
5
5
  class Cluster
6
6
  attr_reader :tiles, :hub, :finder
7
7
 
8
- def initialize(finder:, height:, start_x:, end_x:)
8
+ def initialize(finder:, height:)
9
9
  @finder = finder
10
10
  @tiles = []
11
11
  @hub = Hub.new(height:)
@@ -57,23 +57,24 @@ module Contrek
57
57
  new_inners = shape.inner_polylines
58
58
  new_inner_polylines = []
59
59
  tot_inner += Benchmark.measure do
60
- new_inner_polylines = cursor.join_inners!(new_outer, treemap)
60
+ new_inner_polylines = cursor.join_inners!(treemap)
61
61
  new_inners += new_inner_polylines
62
62
  if treemap
63
63
  new_inner_polylines.each do |inner_polyline|
64
- inner_polyline.sequence.compute_vertical_bounds!
64
+ inner_polyline.compute_vertical_bounds!
65
65
  all_new_inner_polylines += new_inner_polylines
66
66
  end
67
67
  end
68
68
  new_inners += cursor.orphan_inners
69
69
  end.real
70
+ shape.clear_inner!
70
71
 
71
72
  polyline = Polyline.new(tile: tile, polygon: new_outer.to_a)
72
73
  inserting_new_shape = Shape.init_by(polyline, new_inners)
73
74
  new_shapes << inserting_new_shape
74
75
  polyline.shape = inserting_new_shape
75
76
 
76
- new_inner_polylines.each { |inner_polyline| inner_polyline.sequence.shape = inserting_new_shape }
77
+ new_inner_polylines.each { |inner_polyline| inner_polyline.shape = inserting_new_shape }
77
78
 
78
79
  if treemap
79
80
  cursor.shapes_sequence.each do |merged_shape|
@@ -115,7 +116,7 @@ module Contrek
115
116
  private
116
117
 
117
118
  def assign_ancestry(shape, inner_polyline)
118
- shape.set_parent_shape(inner_polyline.sequence.shape)
119
+ shape.set_parent_shape(inner_polyline.shape)
119
120
  shape.parent_inner_polyline = inner_polyline
120
121
  shape.fixed = true
121
122
  end
@@ -40,7 +40,7 @@ module Contrek
40
40
  outer_joined_polyline
41
41
  end
42
42
 
43
- def join_inners!(outer_seq, treemap)
43
+ def join_inners!(treemap)
44
44
  return_inner_polylines = []
45
45
  shape_index = 0
46
46
 
@@ -102,9 +102,6 @@ module Contrek
102
102
  if act_part.is?(Part::EXCLUSIVE)
103
103
  return if act_part.size == 0
104
104
  while (position = act_part.next_position)
105
- return if outer_joined_polyline.size > 1 &&
106
- outer_joined_polyline.head.payload == position.payload &&
107
- act_part == all_parts.first
108
105
  outer_joined_polyline.add(position)
109
106
  end
110
107
  else
@@ -177,6 +174,7 @@ module Contrek
177
174
  if !dest_part.polyline.on?(Polyline::TRACKED_OUTER)
178
175
  @shapes_sequence << shape
179
176
  @orphan_inners += shape.inner_polylines
177
+ shape.clear_inner!
180
178
  end
181
179
  dest_part.polyline.turn_on(Polyline::TRACKED_OUTER)
182
180
  if !dest_part.touched
@@ -114,18 +114,7 @@ module Contrek
114
114
  return
115
115
  end
116
116
  if (twin_tile = arriving_tiles.find { |b| (b.start_x == (tile.end_x - 1)) || ((b.end_x - 1) == tile.start_x) })
117
-
118
- if twin_tile.start_x == (tile.end_x - 1)
119
- start_x = tile.start_x
120
- end_x = twin_tile.end_x
121
- else
122
- start_x = twin_tile.start_x
123
- end_x = tile.end_x
124
- end
125
-
126
- # puts "start = #{start_x} end = #{end_x}"
127
-
128
- cluster = Cluster.new(finder: self, height: height, start_x:, end_x:)
117
+ cluster = Cluster.new(finder: self, height: height)
129
118
  if twin_tile.start_x == (tile.end_x - 1)
130
119
  cluster.add(tile)
131
120
  cluster.add(twin_tile)
@@ -3,37 +3,20 @@
3
3
  module Contrek
4
4
  module Concurrent
5
5
  class InnerPolyline
6
- attr_reader :sequence
6
+ attr_reader :raw, :vertical_bounds
7
+ attr_accessor :shape
7
8
 
8
9
  def initialize(shape: nil, raw_coordinates: [], sequence: nil)
9
- @raw = raw_coordinates if raw_coordinates
10
- @sequence = sequence if sequence
11
- @shape = shape
12
- end
13
-
14
- def raw
15
- @sequence ? @sequence.to_a : @raw
16
- end
17
-
18
- def vertical_bounds
19
- if @sequence
20
- @sequence.vertical_bounds
10
+ if sequence
11
+ @shape = sequence.shape
12
+ @raw = sequence.to_a
21
13
  else
22
- raw_vertical_bounds
14
+ @shape = shape
15
+ @raw = raw_coordinates
23
16
  end
24
17
  end
25
18
 
26
- def shape
27
- if @sequence
28
- @sequence.shape
29
- else
30
- @shape
31
- end
32
- end
33
-
34
- private
35
-
36
- def raw_vertical_bounds
19
+ def compute_vertical_bounds!
37
20
  @vertical_bounds ||= begin
38
21
  min_y = Float::INFINITY
39
22
  max_y = 0
@@ -10,11 +10,13 @@ module Contrek
10
10
  ADDED = 2
11
11
 
12
12
  attr_reader :polyline, :touched
13
- attr_accessor :next, :circular_next, :prev, :type, :dead_end, :inverts, :trasmuted, :versus, :mirror
13
+ attr_accessor :next, :circular_next, :prev, :type, :dead_end, :inverts,
14
+ :trasmuted, :versus, :mirror, :next_seam, :transmutation_skip
14
15
  def initialize(type, polyline)
15
16
  @type = type
16
17
  @polyline = polyline
17
18
  @next = nil
19
+ @next_seam = nil
18
20
  @circular_next = nil
19
21
  @prev = nil
20
22
  @dead_end = false
@@ -23,6 +25,7 @@ module Contrek
23
25
  @trasmuted = false
24
26
  @versus = 0
25
27
  @mirror = false
28
+ @transmutation_skip = false
26
29
  end
27
30
 
28
31
  def is?(type)
@@ -80,6 +83,27 @@ module Contrek
80
83
  end
81
84
  end
82
85
 
86
+ def try_transmutation!
87
+ head_queues = head.end_point.queues
88
+ return if head_queues.size == 1
89
+ other_head_part = head_queues.find { |part| part.polyline.tile == polyline.tile && part != self }
90
+ if other_head_part
91
+ tail_queues = tail.end_point.queues
92
+ if tail_queues.find { |part| part == other_head_part }
93
+ if (other_head_part.tail.payload[:y] == tail.payload[:y] && other_head_part.head.payload[:y] == head.payload[:y]) ||
94
+ (other_head_part.tail.payload[:y] == head.payload[:y] && other_head_part.head.payload[:y] == tail.payload[:y])
95
+ if self.next.nil? && other_head_part.prev.nil?
96
+ self.mirror = true
97
+ end
98
+ else
99
+ self.type = Part::EXCLUSIVE
100
+ self.trasmuted = true
101
+ other_head_part.transmutation_skip = true
102
+ end
103
+ end
104
+ end
105
+ end
106
+
83
107
  def within?(other)
84
108
  self_min, self_max = [head.payload[:y], tail.payload[:y]].minmax
85
109
  other_min, other_max = [other.head.payload[:y], other.tail.payload[:y]].minmax
@@ -8,6 +8,8 @@ module Contrek
8
8
  def initialize(*args, **kwargs, &block)
9
9
  super
10
10
  @parts = []
11
+ @first_seam = nil
12
+ @last_seam = nil
11
13
  end
12
14
 
13
15
  def add_part(new_part)
@@ -16,8 +18,14 @@ module Contrek
16
18
  last.next = last.circular_next = new_part if last
17
19
  new_part.circular_next = @parts.first
18
20
  new_part.prev = last
19
-
20
- new_part.orient! if new_part.is?(Part::SEAM)
21
+ if new_part.is?(Part::SEAM)
22
+ @first_seam ||= new_part
23
+ if !@last_seam.nil?
24
+ @last_seam.next_seam = new_part
25
+ end
26
+ @last_seam = new_part
27
+ new_part.orient!
28
+ end
21
29
  end
22
30
 
23
31
  def inspect_parts
@@ -27,6 +35,8 @@ module Contrek
27
35
  def partition!
28
36
  current_part = nil
29
37
  @parts = []
38
+ @first_seam = nil
39
+ @last_seam = nil
30
40
 
31
41
  @raw.each_with_index do |position, n|
32
42
  if @tile.tg_border?(position)
@@ -49,7 +59,7 @@ module Contrek
49
59
  end
50
60
  add_part(current_part)
51
61
 
52
- trasmute_parts!
62
+ transmute_parts!
53
63
  end
54
64
 
55
65
  private
@@ -57,42 +67,37 @@ module Contrek
57
67
  # If there are SEAM parts and one is canceled out by another within the same polyline,
58
68
  # meaning that all its points are repeated in another, longer sequence,
59
69
  # then the shorter one is converted to EXCLUSIVE and marked as transmuted
60
- def trasmute_parts!
70
+ def transmute_parts!
61
71
  transpose = tile.cluster.finder.transpose?
62
-
63
- @parts.each do |inside|
64
- next unless inside.is?(Part::SEAM)
65
- @parts.each do |inside_compare|
66
- next if inside == inside_compare
67
- next unless inside_compare.is?(Part::SEAM)
68
-
72
+ if (current_seam = @first_seam)
73
+ loop do
69
74
  if transpose
70
- if inside.within?(inside_compare)
71
- if !inside.same_length?(inside_compare)
72
- inside.type = Part::EXCLUSIVE
73
- inside.trasmuted = true
74
- inside.head.end_point.queues.delete(inside)
75
- inside.tail.end_point.queues.delete(inside)
76
- break
77
- end
78
- end
79
- else
80
- count = 0
81
- inside.each do |position|
82
- inclusion = position.end_point.queues.include?(inside_compare)
83
- count += 1 if inclusion
84
- end
85
- if count == inside.size
86
- if count < inside_compare.size
87
- inside.type = Part::EXCLUSIVE
88
- inside.trasmuted = true
89
- break
90
- end
91
- if count == inside_compare.size && inside.next.nil? && inside_compare.prev.nil?
92
- inside.mirror = true
75
+ transmute_transposed_part(current_seam)
76
+ elsif !current_seam.transmutation_skip
77
+ current_seam.try_transmutation!
78
+ end
79
+ current_seam = current_seam.next_seam
80
+ break if current_seam.nil?
81
+ end
82
+ end
83
+ end
84
+
85
+ def transmute_transposed_part(part)
86
+ if (current_seam = @first_seam)
87
+ loop do
88
+ if current_seam != part
89
+ if part.within?(current_seam)
90
+ if !part.same_length?(current_seam)
91
+ part.type = Part::EXCLUSIVE
92
+ part.trasmuted = true
93
+ part.head.end_point.queues.delete(part)
94
+ part.tail.end_point.queues.delete(part)
95
+ return
93
96
  end
94
97
  end
95
98
  end
99
+ current_seam = current_seam.next_seam
100
+ break if current_seam.nil?
96
101
  end
97
102
  end
98
103
  end
@@ -3,13 +3,9 @@
3
3
  module Contrek
4
4
  module Concurrent
5
5
  class Sequence
6
- attr_accessor :vertical_bounds, :shape
6
+ attr_accessor :shape
7
7
  prepend Queueable
8
8
 
9
- def initialize
10
- @vertical_bounds = nil
11
- end
12
-
13
9
  def is_not_vertical
14
10
  return false if size < 2
15
11
  x0 = head.payload[:x]
@@ -21,18 +17,6 @@ module Contrek
21
17
  false
22
18
  end
23
19
 
24
- def compute_vertical_bounds!
25
- return if size == 0
26
- min_y = Float::INFINITY
27
- max_y = 0
28
- get_vector_cache.each do |pos|
29
- y = pos[:y]
30
- min_y = y if y < min_y
31
- max_y = y if y > max_y
32
- end
33
- @vertical_bounds = {min: min_y, max: max_y}
34
- end
35
-
36
20
  def get_vector_cache
37
21
  @vector_cache ||= to_a
38
22
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Contrek
4
- VERSION = "1.3.0"
4
+ VERSION = "1.3.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contrek
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emanuele Cesaroni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-17 00:00:00.000000000 Z
11
+ date: 2026-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -188,6 +188,7 @@ files:
188
188
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/FakeCluster.h
189
189
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp
190
190
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.h
191
+ - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h
191
192
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/HorizontalMerger.cpp
192
193
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/HorizontalMerger.h
193
194
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.cpp
@@ -218,6 +219,8 @@ files:
218
219
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/ShapePool.h
219
220
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp
220
221
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h
222
+ - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/SubPool.h
223
+ - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/SvgStreamingMerger.h
221
224
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp
222
225
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h
223
226
  - ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp