contrek 1.0.6 → 1.0.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +102 -6
  5. data/ext/cpp_polygon_finder/PolygonFinder/Makefile +4 -4
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +5 -4
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp +3 -5
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -1
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +2 -2
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.h +1 -1
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +28 -8
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -0
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +12 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.cpp +5 -5
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Hub.h +11 -7
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +2 -0
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +108 -66
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +5 -3
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +33 -28
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +5 -1
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +2 -3
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +15 -20
  23. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +128 -131
  24. data/lib/contrek/bitmaps/rgb_color.rb +0 -15
  25. data/lib/contrek/bitmaps/rgb_cpp_color.rb +10 -0
  26. data/lib/contrek/finder/concurrent/cluster.rb +2 -2
  27. data/lib/contrek/finder/concurrent/cursor.rb +32 -15
  28. data/lib/contrek/finder/concurrent/finder.rb +23 -9
  29. data/lib/contrek/finder/concurrent/hub.rb +2 -2
  30. data/lib/contrek/finder/concurrent/part.rb +6 -1
  31. data/lib/contrek/finder/concurrent/partitionable.rb +61 -33
  32. data/lib/contrek/finder/concurrent/polyline.rb +44 -2
  33. data/lib/contrek/finder/concurrent/queueable.rb +12 -20
  34. data/lib/contrek/finder/polygon_finder.rb +8 -6
  35. data/lib/contrek/finder/result.rb +13 -0
  36. data/lib/contrek/results/cpp_result.rb +21 -0
  37. data/lib/contrek/version.rb +1 -1
  38. data/lib/contrek.rb +6 -3
  39. metadata +5 -2
@@ -8,11 +8,11 @@
8
8
 
9
9
  #include <iostream>
10
10
  #include <list>
11
- #include <rice/rice.hpp>
12
- #include <rice/stl.hpp>
13
11
  #include <vector>
14
12
  #include <map>
15
13
  #include <string>
14
+ #include <rice/rice.hpp>
15
+ #include <rice/stl.hpp>
16
16
 
17
17
  #include "PolygonFinder/src/polygon/finder/PolygonFinder.h"
18
18
  #include "PolygonFinder/src/polygon/finder/PolygonFinder.cpp"
@@ -86,142 +86,133 @@ extern "C" {
86
86
  #include "PolygonFinder/src/polygon/bitmaps/spng.h"
87
87
  }
88
88
 
89
- using namespace Rice;
90
-
91
- namespace Rice::detail
92
- { template<>
93
- class To_Ruby<std::vector<Point*>>
94
- { public:
95
- VALUE convert(const std::vector<Point*>& x)
96
- { Rice::Array arr;
97
- for (Point* p : x)
98
- arr.push(p);
99
- return arr;
100
- }
101
- };
102
-
103
- template<>
104
- class To_Ruby<std::list<std::vector<Point*>>>
105
- { public:
106
- VALUE convert(const std::list<std::vector<Point*>>& x)
107
- { Rice::Array arr;
108
- for (const std::vector<Point*>& vec : x)
109
- { arr.push(vec);
110
- }
111
- return arr;
112
- }
113
- };
89
+ class RubyResult {
90
+ public:
91
+ Rice::Array polygons;
92
+ Rice::Hash metadata;
93
+ };
114
94
 
115
- template<>
116
- class To_Ruby<Point*>
117
- { public:
118
- VALUE convert(Point* const & x)
119
- { Rice::Hash h = Rice::Hash();
120
- h[Symbol("x")] = x->x;
121
- h[Symbol("y")] = x->y;
122
- return(h);
123
- }
124
- };
95
+ using namespace Rice;
125
96
 
126
- template<>
127
- class To_Ruby<std::map<std::string, double>*>
128
- { public:
129
- VALUE convert(std::map<std::string, double>* const & x)
130
- { Rice::Hash return_me = Rice::Hash();
131
- std::map<std::string, double>::iterator it;
132
- for ( it = x->begin(); it != x->end(); it++ )
133
- { return_me[String(it->first)] = it->second;
134
- }
135
- return return_me;
136
- }
137
- };
97
+ namespace Rice::detail {
138
98
 
139
- template<>
140
- class From_Ruby<std::vector<std::string>*>
141
- { public:
142
- Convertible is_convertible(VALUE value)
143
- { switch (rb_type(value))
144
- { case RUBY_T_HASH:
145
- return Convertible::Cast;
99
+ template<>
100
+ class From_Ruby<std::vector<std::string>*>
101
+ { public:
102
+ Convertible is_convertible(VALUE value)
103
+ { switch (rb_type(value))
104
+ { case RUBY_T_HASH:
105
+ return Convertible::Cast;
106
+ break;
107
+ default:
108
+ return Convertible::None;
109
+ }
110
+ }
111
+ std::vector<std::string>* convert(VALUE value)
112
+ { std::vector<std::string> *arguments = new std::vector<std::string>();
113
+ if (rb_type(value) == RUBY_T_NIL) return(arguments);
114
+ Rice::Hash hash = (Rice::Hash) value;
115
+ for (Rice::Hash::iterator it = hash.begin(); it != hash.end(); ++it) {
116
+ Rice::String keyString = it->key.to_s();
117
+ Rice::Object value = it->value;
118
+ switch (value.rb_type()) {
119
+ case T_STRING:
120
+ arguments->push_back("--" + keyString.str()+"="+((Rice::String) value).str());
146
121
  break;
147
- default:
148
- return Convertible::None;
149
- }
122
+ case T_SYMBOL:
123
+ arguments->push_back("--" + keyString.str()+"="+((Rice::Symbol) value).str());
124
+ break;
125
+ case T_FLOAT:
126
+ arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2DBL(value.value())));
127
+ break;
128
+ case T_FIXNUM:
129
+ arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2INT(value.value())));
130
+ break;
131
+ case T_TRUE:
132
+ arguments->push_back("--" + keyString.str()+"=true");
133
+ break;
134
+ case T_FALSE:
135
+ arguments->push_back("--" + keyString.str()+"=false");
136
+ break;
137
+ case T_HASH:
138
+ std::vector<std::string>* iv = From_Ruby<std::vector<std::string>*>::convert(value);
139
+ for (std::vector<std::string>::iterator it_iv = iv->begin() ; it_iv != iv->end(); ++it_iv)
140
+ { (*it_iv).replace(0, 2, "_");
141
+ arguments->push_back("--" + keyString.str() + *it_iv);
142
+ }
143
+ break;
144
+ }
150
145
  }
151
- std::vector<std::string>* convert(VALUE value)
152
- { std::vector<std::string> *arguments = new std::vector<std::string>();
153
- if (rb_type(value) == RUBY_T_NIL) return(arguments);
154
- Rice::Hash hash = (Rice::Hash) value;
155
- for (Rice::Hash::iterator it = hash.begin(); it != hash.end(); ++it) {
156
- Rice::String keyString = it->key.to_s();
157
- Rice::Object value = it->value;
158
- switch (value.rb_type()) {
159
- case T_STRING:
160
- arguments->push_back("--" + keyString.str()+"="+((Rice::String) value).str());
161
- break;
162
- case T_SYMBOL:
163
- arguments->push_back("--" + keyString.str()+"="+((Rice::Symbol) value).str());
164
- break;
165
- case T_FLOAT:
166
- arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2DBL(value.value())));
167
- break;
168
- case T_FIXNUM:
169
- arguments->push_back("--" + keyString.str()+"="+std::to_string(NUM2INT(value.value())));
170
- break;
171
- case T_TRUE:
172
- arguments->push_back("--" + keyString.str()+"=true");
173
- break;
174
- case T_FALSE:
175
- arguments->push_back("--" + keyString.str()+"=false");
176
- break;
177
- case T_HASH:
178
- std::vector<std::string>* iv = From_Ruby<std::vector<std::string>*>::convert(value);
179
- for (std::vector<std::string>::iterator it_iv = iv->begin() ; it_iv != iv->end(); ++it_iv)
180
- { (*it_iv).replace(0, 2, "_");
181
- arguments->push_back("--" + keyString.str() + *it_iv);
182
- }
183
- break;
184
- }
185
- }
186
- return arguments;
146
+ return arguments;
147
+ }
148
+ };
149
+
150
+ template<>
151
+ class To_Ruby<ProcessResult*>
152
+ {public:
153
+ VALUE convert(ProcessResult* const & pr)
154
+ { if (!pr) {
155
+ return Qnil;
187
156
  }
188
- };
157
+ RubyResult* rr = new RubyResult();
158
+ Rice::Data_Object<RubyResult> rb_result(rr);
189
159
 
190
- template<>
191
- struct Type<ProcessResult>
192
- { static bool verify()
193
- { return true;
160
+ Rice::Hash return_me = Rice::Hash();
161
+ Rice::Hash benchmarks_rb;
162
+ for (auto const& [name, value] : pr->benchmarks) {
163
+ benchmarks_rb[Rice::String(name)] = value;
194
164
  }
195
- };
165
+ return_me[Symbol("benchmarks")] = benchmarks_rb;
166
+ return_me[Symbol("groups")] = pr->groups;
167
+ return_me[Symbol("named_sequence")] = pr->named_sequence;
196
168
 
197
- template<>
198
- class To_Ruby<ProcessResult*>
199
- { public:
200
- VALUE convert(ProcessResult* const & pr)
201
- { Rice::Hash return_me = Rice::Hash();
202
- return_me[Symbol("benchmarks")] = &pr->benchmarks;
203
- return_me[Symbol("groups")] = pr->groups;
204
- return_me[Symbol("named_sequence")] = pr->named_sequence;
205
- Rice::Array out;
206
- for (Polygon& x : pr->polygons)
207
- { Rice::Hash h = Rice::Hash();
208
- h[Symbol("outer")] = x.outer;
209
- h[Symbol("inner")] = x.inner;
210
- out.push(h);
169
+ Rice::Array out;
170
+ for (Polygon& x : pr->polygons)
171
+ { Rice::Hash poly_hash = Rice::Hash();
172
+ // OUTER: std::vector<Point*>
173
+ Rice::Array outer_flat;
174
+ for (Point* p : x.outer) {
175
+ outer_flat.push(p->x);
176
+ outer_flat.push(p->y);
211
177
  }
212
- return_me[Symbol("polygons")] = out;
213
- Rice::Array tmapout;
214
- for (const auto& tm : pr->treemap) {
215
- Rice::Array tmentry;
216
- tmentry.push(tm.first); // outer
217
- tmentry.push(tm.second); // inner
218
- tmapout.push(tmentry);
178
+ poly_hash[Symbol("outer")] = outer_flat;
179
+ // INNER: std::list<std::vector<Point*>>
180
+ Rice::Array inner_collection;
181
+ for (const std::vector<Point*>& sequence : x.inner) {
182
+ Rice::Array sequence_flat;
183
+ for (Point* p : sequence) {
184
+ sequence_flat.push(p->x);
185
+ sequence_flat.push(p->y);
186
+ }
187
+ inner_collection.push(sequence_flat);
219
188
  }
220
- return_me[Symbol("treemap")] = tmapout;
221
- delete pr;
222
- return(return_me);
189
+ poly_hash[Symbol("inner")] = inner_collection;
190
+ out.push(poly_hash);
191
+ }
192
+ rr->polygons = out;
193
+
194
+ // Treemap
195
+ Rice::Array tmapout;
196
+ for (const auto& tm : pr->treemap) {
197
+ Rice::Array tmentry;
198
+ tmentry.push(tm.first);
199
+ tmentry.push(tm.second);
200
+ tmapout.push(tmentry);
223
201
  }
224
- };
202
+ return_me[Symbol("treemap")] = tmapout;
203
+ rr->metadata = return_me;
204
+
205
+ // Protects objects 'out' e 'return_me' linking them to the ruby instance preventing GC
206
+ // garbage collector to free them before the instance itself.
207
+ Rice::Object ruby_obj = rb_result;
208
+ ruby_obj.iv_set("@polygons_storage", out);
209
+ ruby_obj.iv_set("@metadata_storage", return_me);
210
+
211
+ delete pr;
212
+ return rb_result.value();
213
+ }
214
+ };
215
+
225
216
  } // namespace Rice::detail
226
217
 
227
218
  extern "C"
@@ -276,17 +267,23 @@ void Init_cpp_polygon_finder() {
276
267
 
277
268
  Data_Type<RGBNotMatcher> rb_cRGBNotMatcher =
278
269
  define_class<RGBNotMatcher, Matcher>("CPPRGBNotMatcher")
279
- .define_constructor(Constructor<RGBNotMatcher, int>())
270
+ .define_constructor(Constructor<RGBNotMatcher, unsigned int>())
280
271
  .define_method("match", &RGBNotMatcher::match);
281
272
 
282
273
  Data_Type<PolygonFinder> rb_cPolygonFinder =
283
274
  define_class<PolygonFinder>("CPPPolygonFinder")
284
- .define_constructor(Constructor<PolygonFinder, Bitmap*, Matcher*, Bitmap*, std::vector<std::string>*>(), Arg("bitmap"), Arg("matcher"), Arg("test_bitmap") = nullptr, Arg("options") = nullptr)
275
+ .define_constructor(Constructor<PolygonFinder, Bitmap*, Matcher*, Bitmap*, std::vector<std::string>*>(), Arg("bitmap"), Arg("matcher"), Arg("test_bitmap") = nullptr, Arg("options") = nullptr, Rice::Arg("yield_gvl") = true)
285
276
  .define_method("get_shapelines", &PolygonFinder::get_shapelines)
286
- .define_method("process_info", &PolygonFinder::process_info);
277
+ .define_method("process_info", &PolygonFinder::process_info, Rice::Arg("yield_gvl") = true);
287
278
 
288
279
  Data_Type<Finder> rb_cFinder =
289
280
  define_class<Finder>("CPPFinder")
290
- .define_constructor(Constructor<Finder, int, Bitmap*, Matcher*, std::vector<std::string>*>(), Arg("number_of_threads"), Arg("bitmap"), Arg("matcher"), Arg("options") = nullptr)
291
- .define_method("process_info", &Finder::process_info);
281
+ .define_constructor(Constructor<Finder, int, Bitmap*, Matcher*, std::vector<std::string>*>(), Arg("number_of_threads"), Arg("bitmap"), Arg("matcher"), Arg("options") = nullptr, Rice::Arg("yield_gvl") = true)
282
+ .define_method("process_info", &Finder::process_info, Rice::Arg("yield_gvl") = true);
283
+
284
+ Data_Type<RubyResult> rb_cResult =
285
+ define_class_under<RubyResult>(rb_cFinder, "Result")
286
+ .define_constructor(Constructor<RubyResult>())
287
+ .define_method("polygons", [](RubyResult& rr) { return rr.polygons; })
288
+ .define_method("metadata", [](RubyResult& rr) { return rr.metadata; });
292
289
  }
@@ -3,23 +3,8 @@ module Contrek
3
3
  class RgbColor
4
4
  attr_reader :raw
5
5
  def initialize(r:, g:, b:, a: 255)
6
- @r = r
7
- @g = g
8
- @b = b
9
- @a = a
10
6
  @raw = (r << 24) + (g << 16) + (b << 8) + a
11
7
  end
12
-
13
- def to_rgb_raw
14
- @raw >> 8
15
- end
16
-
17
- def self.reverse_raw(raw)
18
- [:a, :b, :g, :r].each_with_object({}) do |c, h|
19
- h[c] = raw & 0xFF
20
- raw >>= 8
21
- end
22
- end
23
8
  end
24
9
  end
25
10
  end
@@ -0,0 +1,10 @@
1
+ module Contrek
2
+ module Bitmaps
3
+ class RgbCppColor
4
+ attr_reader :raw
5
+ def initialize(r:, g:, b:, a: 255)
6
+ @raw = (a << 24) | (b << 16) | (g << 8) | r
7
+ end
8
+ end
9
+ end
10
+ end
@@ -3,10 +3,10 @@ module Contrek
3
3
  class Cluster
4
4
  attr_reader :tiles, :hub
5
5
 
6
- def initialize(finder:, height:, width:)
6
+ def initialize(finder:, height:, start_x:, end_x:)
7
7
  @finder = finder
8
8
  @tiles = []
9
- @hub = Hub.new(height:, width:)
9
+ @hub = Hub.new(start_x:, end_x:)
10
10
  end
11
11
 
12
12
  def add(tile)
@@ -54,34 +54,51 @@ module Contrek
54
54
  missing_shapes << shape
55
55
  end
56
56
  end
57
+ to_delay = connect_missings(missing_shapes)
58
+ while to_delay.any?
59
+ to_delay = connect_missings(to_delay)
60
+ end
61
+
62
+ retme = collect_inner_sequences(outer_seq)
63
+
64
+ @polylines_sequence.each do |polyline|
65
+ polyline.turn_on(Polyline::TRACKED_INNER)
66
+ end
67
+ retme
68
+ end
69
+
70
+ private
71
+
72
+ def connect_missings(missing_shapes)
73
+ delay_shapes = []
74
+
57
75
  @polylines_sequence.each do |polyline|
58
76
  missing_shapes.each do |missing_shape|
59
- outer_polyline = missing_shape.outer_polyline
60
- next unless polyline.vert_intersect?(outer_polyline)
61
- if (intersection = polyline.intersection(outer_polyline)).any?
62
- inject_sequences_left, inject_sequences_right = polyline.sew!(intersection, outer_polyline)
77
+ missing_outer_polyline = missing_shape.outer_polyline
78
+ next if missing_outer_polyline.on?(Polyline::TRACKED_OUTER) ||
79
+ !polyline.vert_intersect?(missing_outer_polyline)
80
+
81
+ if (intersection = polyline.intersection(missing_outer_polyline)).any?
82
+ inject_sequences_left, inject_sequences_right = polyline.sew!(intersection, missing_outer_polyline)
83
+ if inject_sequences_left.nil?
84
+ delay_shapes << missing_shape
85
+ next
86
+ end
63
87
  combine!(inject_sequences_right, inject_sequences_left).each do |sewn_sequence|
64
88
  sewn_sequence.uniq!
65
89
  @orphan_inners << sewn_sequence if sewn_sequence.size > 1 && sewn_sequence.map { |c| c[:x] }.uniq.size > 1 # segmenti non sono ammessi, solo aree
66
90
  end
67
- outer_polyline.clear!
68
- outer_polyline.turn_on(Polyline::TRACKED_OUTER)
69
- outer_polyline.turn_on(Polyline::TRACKED_INNER)
91
+ missing_outer_polyline.clear!
92
+ missing_outer_polyline.turn_on(Polyline::TRACKED_OUTER)
93
+ missing_outer_polyline.turn_on(Polyline::TRACKED_INNER)
70
94
  @orphan_inners += missing_shape.inner_polylines
71
95
  end
72
96
  end
73
97
  end
74
98
 
75
- retme = collect_inner_sequences(outer_seq)
76
-
77
- @polylines_sequence.each do |polyline|
78
- polyline.turn_on(Polyline::TRACKED_INNER)
79
- end
80
- retme
99
+ delay_shapes
81
100
  end
82
101
 
83
- private
84
-
85
102
  # rubocop:disable Lint/NonLocalExitFromIterator
86
103
  def traverse_outer(act_part, all_parts, polylines, shapes, outer_joined_polyline)
87
104
  all_parts << act_part if all_parts.last != act_part
@@ -74,14 +74,17 @@ module Contrek
74
74
  end
75
75
  end.real
76
76
 
77
- {polygons: raw_polygons,
78
- benchmarks: {
79
- total: ((@initialize_time + compress_time) * 1000).round(3),
80
- init: (@initialize_time * 1000).round(3),
81
- outer: (@whole_tile.benchmarks[:outer] * 1000).round(3),
82
- inner: (@whole_tile.benchmarks[:inner] * 1000).round(3),
83
- compress: ((compress_time * 1000).round(3) if @options.has_key?(:compress))
84
- }.compact}
77
+ metadata = {
78
+ groups: raw_polygons.size,
79
+ benchmarks: {
80
+ total: ((@initialize_time + compress_time) * 1000).round(3),
81
+ init: (@initialize_time * 1000).round(3),
82
+ outer: (@whole_tile.benchmarks[:outer] * 1000).round(3),
83
+ inner: (@whole_tile.benchmarks[:inner] * 1000).round(3),
84
+ compress: ((compress_time * 1000).round(3) if @options.has_key?(:compress))
85
+ }.compact
86
+ }
87
+ Contrek::Finder::Result.new(raw_polygons, metadata)
85
88
  end
86
89
 
87
90
  private
@@ -95,7 +98,18 @@ module Contrek
95
98
  return
96
99
  end
97
100
  if (twin_tile = arriving_tiles.find { |b| (b.start_x == (tile.end_x - 1)) || ((b.end_x - 1) == tile.start_x) })
98
- cluster = Cluster.new(finder: self, height: bitmap.h, width: bitmap.w)
101
+
102
+ if twin_tile.start_x == (tile.end_x - 1)
103
+ start_x = tile.start_x
104
+ end_x = twin_tile.end_x
105
+ else
106
+ start_x = twin_tile.start_x
107
+ end_x = tile.end_x
108
+ end
109
+
110
+ # puts "start = #{start_x} end = #{end_x}"
111
+
112
+ cluster = Cluster.new(finder: self, height: bitmap.h, start_x:, end_x:)
99
113
  if twin_tile.start_x == (tile.end_x - 1)
100
114
  cluster.add(tile)
101
115
  cluster.add(twin_tile)
@@ -2,8 +2,8 @@ module Contrek
2
2
  module Concurrent
3
3
  class Hub
4
4
  attr_reader :payloads, :width
5
- def initialize(height:, width:)
6
- @width = width
5
+ def initialize(start_x:, end_x:)
6
+ @width = end_x - start_x
7
7
  # @payloads = Array.new(width * height)
8
8
  @payloads = {}
9
9
  end
@@ -8,7 +8,7 @@ module Contrek
8
8
  ADDED = 2
9
9
 
10
10
  attr_reader :polyline, :index, :touched
11
- attr_accessor :next, :circular_next, :prev, :type, :passes, :inverts, :trasmuted
11
+ attr_accessor :next, :circular_next, :prev, :type, :passes, :inverts, :trasmuted, :delayed
12
12
  def initialize(type, polyline)
13
13
  @type = type
14
14
  @polyline = polyline
@@ -19,12 +19,17 @@ module Contrek
19
19
  @touched = false
20
20
  @inverts = false
21
21
  @trasmuted = false
22
+ @delayed = false
22
23
  end
23
24
 
24
25
  def is?(type)
25
26
  @type == type
26
27
  end
27
28
 
29
+ def set_polyline(polyline)
30
+ @polyline = polyline
31
+ end
32
+
28
33
  def add_position(position)
29
34
  add(Position.new(position: position, hub: polyline.tile.cluster.hub))
30
35
  end
@@ -16,6 +16,15 @@ module Contrek
16
16
  new_part.prev = last
17
17
  end
18
18
 
19
+ def insert_after(part, new_part)
20
+ part_index = @parts.index(part)
21
+ @parts.insert(part_index + 1, new_part)
22
+ new_part.prev = part
23
+ new_part.next = new_part.circular_next = part.next
24
+ part.next.prev = new_part if part.next
25
+ part.next = part.circular_next = new_part
26
+ end
27
+
19
28
  def find_first_part_by_position(position)
20
29
  @parts.find do |part|
21
30
  part.is?(Part::SEAM) &&
@@ -57,48 +66,67 @@ module Contrek
57
66
  end
58
67
 
59
68
  def sew!(intersection, other)
60
- matching_part_indexes = []
61
- parts.each_with_index do |part, index|
62
- next if part.trasmuted
63
- matching_part_indexes << index if part.intersection_with_array?(intersection)
64
- end
65
- other_matching_part_indexes = []
66
- other.parts.each_with_index do |part, index|
67
- next if part.trasmuted
68
- other_matching_part_indexes << index if part.intersection_with_array?(intersection)
69
- end
70
- # other_matching_part_indexes and matching_part_indexes always contain at least one element
69
+ matching_part_indexes, other_matching_part_indexes = intersection.transpose.map(&:sort)
70
+ # other_matching_part_indexes and matching_part_indexes always must contain at least one element
71
71
  before_parts = other.parts[other_matching_part_indexes.last + 1..]
72
72
  after_parts = other_matching_part_indexes.first.zero? ? [] : other.parts[0..other_matching_part_indexes.first - 1]
73
73
  part_start = parts[matching_part_indexes.first]
74
74
  part_end = parts[matching_part_indexes.last]
75
75
 
76
- # They are inverted since they traverse in opposite directions
77
- sequence = Sequence.new
78
- sequence.add part_start.head
79
- before_parts.each { |part| sequence.append(part) }
80
- after_parts.each { |part| sequence.append(part) }
81
- sequence.add part_end.tail if part_end.tail # nil when part_start == part_end
82
-
83
- part_start.replace!(sequence)
84
- part_start.type = Part::EXCLUSIVE
85
- part_end.reset! if part_start != part_end
76
+ # left and right side reduces will be combined and later converted into orphan inners sequences
77
+ returning_data = [[matching_part_indexes, parts], [other_matching_part_indexes, other.parts]].map do |matching_part_indexes, parts|
78
+ lastn = 0
79
+ result = []
80
+ (matching_part_indexes.first + 1).upto(matching_part_indexes.last - 1) do |n|
81
+ if matching_part_indexes.index(n).nil?
82
+ part = parts[n]
83
+ if part.is?(Part::SEAM) && part.size > 0 && !part.delayed # fallback, delays the shape
84
+ part.delayed = true
85
+ return nil
86
+ end
87
+ if (lastn == (n - 1)) && result.any?
88
+ result.last.concat part.to_a
89
+ else
90
+ result << part.to_a
91
+ end
92
+ lastn = n
93
+ end
94
+ end
95
+ result
96
+ end
86
97
 
87
- left = []
88
- (matching_part_indexes.first + 1).upto(matching_part_indexes.last - 1) do |n|
89
- left << parts[n].to_a if matching_part_indexes.index(n).nil?
98
+ if part_start != part_end
99
+ (matching_part_indexes.last - 1).downto(matching_part_indexes.first + 1) do |n|
100
+ delete_part = parts[n]
101
+ delete_part.prev.next = delete_part.next if delete_part.prev
102
+ delete_part.next.prev = delete_part.prev if delete_part.next
103
+ parts.delete_at(n)
104
+ end
90
105
  end
91
- (matching_part_indexes.last - 1).downto(matching_part_indexes.first + 1) do |n|
92
- delete_part = parts[n]
93
- delete_part.prev.next = delete_part.next if delete_part.prev
94
- delete_part.next.prev = delete_part.prev if delete_part.next
95
- parts.delete_at(n)
106
+
107
+ all_parts = before_parts + after_parts
108
+ will_be_last = all_parts.last
109
+ all_parts.reverse_each do |part|
110
+ insert_after(part_start, part)
111
+ other.parts.delete(part)
112
+ part.set_polyline(self)
96
113
  end
97
- right = []
98
- (other_matching_part_indexes.first + 1).upto(other_matching_part_indexes.last - 1) do |n|
99
- right << other.parts[n].to_a if other_matching_part_indexes.index(n).nil?
114
+
115
+ part_start.type = Part::EXCLUSIVE
116
+ new_end_part = Part.new(Part::EXCLUSIVE, self)
117
+ new_end_part.add(part_end.tail)
118
+ part_start.singleton! # reduce part to its head only
119
+
120
+ if part_start != part_end
121
+ part_end.prev.next = part_end.next if part_end.prev
122
+ part_end.next.prev = part_end.prev if part_end.next
123
+ parts.delete(part_end)
100
124
  end
101
- [left, right]
125
+ insert_after(will_be_last, new_end_part)
126
+
127
+ reset_tracked_endpoints!
128
+
129
+ returning_data
102
130
  end
103
131
 
104
132
  private