contrek 1.1.9 → 1.2.0

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +11 -8
  5. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +1 -2
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +66 -158
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +0 -2
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +4 -2
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +0 -13
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +1 -3
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +0 -128
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +0 -6
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +0 -50
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +2 -8
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +22 -3
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +1 -57
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +0 -12
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +0 -3
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp +11 -3
  20. data/lib/contrek/finder/concurrent/cluster.rb +1 -2
  21. data/lib/contrek/finder/concurrent/cursor.rb +56 -120
  22. data/lib/contrek/finder/concurrent/end_point.rb +2 -0
  23. data/lib/contrek/finder/concurrent/part.rb +2 -20
  24. data/lib/contrek/finder/concurrent/partitionable.rb +0 -81
  25. data/lib/contrek/finder/concurrent/polyline.rb +3 -47
  26. data/lib/contrek/finder/concurrent/position.rb +8 -2
  27. data/lib/contrek/finder/concurrent/queueable.rb +0 -41
  28. data/lib/contrek/finder/concurrent/tile.rb +0 -4
  29. data/lib/contrek/finder/concurrent/vertical_merger.rb +9 -5
  30. data/lib/contrek/version.rb +1 -1
  31. metadata +3 -3
  32. /data/{LICENSE-MIT.md → lib/LICENSE-MIT.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87e8665985a5c9b3818f5f405609c8bf0f9d81dfaa197054e0801acdc60dc56c
4
- data.tar.gz: 450c56eda5bd65ab936fcd52b37e3f8edbc2ec0ac852aa0cc4c052ee6d81ab7e
3
+ metadata.gz: b46d3ae57168cfeb52a0788b6e11af74a164c8e19b4414783f95ac96d1507ed2
4
+ data.tar.gz: e0637093c914f2426a74b4a47ebeac34152ae473de619fda2e164f22c5d03bc8
5
5
  SHA512:
6
- metadata.gz: e4cc2ea0d61819436e922eacdcad551658deceac1b5a907cea03d306de65fa2f71b96018acf6fcc6f88163775cabf0ba9b8d6ded3e9067dfcf3cc3e70d3e3e6d
7
- data.tar.gz: ca933647465baca8a294176a1fbbc336bf1956c8eebab2cf48dd45ac4e55ba55ea84b00d38cbdd9ee0cf012b332684b8902c5e98a8cbaaafbfc846b374a12801
6
+ metadata.gz: 472da77db1202e4cf38416e7c5dcfbd483a1f9b9fdb414afb3583a441c3ea180107741d1bc939270fb2aa46e54541712a0efc56f542abe9afd5f39d66e7c847a
7
+ data.tar.gz: 66ee95022392360b2cb9dbd9757d669a7f01cc95512ee4a74a288e7dc5c94657ebfc0ebe1107090b3e24df490462073f9ea3f2fd6cb57f7432214a809e486b0b
data/CHANGELOG.md CHANGED
@@ -80,3 +80,7 @@ All notable changes to this project will be documented in this file.
80
80
  ## [1.1.9] - 2026-04-24
81
81
  ### Changed
82
82
  - Improved the internal parts joining algorithm, which was imprecise in some circumstances.
83
+
84
+ ## [1.2.0] - 2026-05-02
85
+ ### Changed
86
+ - Further improvements have been applied to the internal parts joining algorithm using a new structural approach. This update is faster and resolves edge cases where inner parts were mistakenly classified as outer perimeters, ensuring precise contour hierarchy. The simplified logic has led to a significant reduction in codebase complexity and the removal of substantial redundant code.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contrek (1.1.9)
4
+ contrek (1.2.0)
5
5
  chunky_png (~> 1.4)
6
6
  concurrent-ruby (~> 1.3.5)
7
7
  rice (= 4.5.0)
data/README.md CHANGED
@@ -167,7 +167,7 @@ Regarding multithreading:
167
167
 
168
168
  - The algorithm splits the contour-detection workflow into multiple phases that can be executed in parallel. The initial contour extraction on each band and the subsequent merging of coordinates between adjacent bands—performed pairwise, recursively, and in a non-deterministic order—results in a final output that is not idempotent. Idempotence is guaranteed only when the exact same merging sequence is repeated.
169
169
 
170
- By not declaring native option CPP Multithreading optimized code is used. In the above example a [105 MP image](spec/files/images/sample_10240x10240.png) is examined by 4 threads working on 4 tiles (total compute time about 1.2 secs with image load).
170
+ By not declaring native option CPP Multithreading optimized code is used. In the above example a [105 MP image](spec/files/images/sample_10240x10240.png) is examined by 4 threads working on 4 tiles (total compute time about 1.1 secs with image load).
171
171
 
172
172
  ```ruby
173
173
  result = Contrek.contour!(
@@ -181,11 +181,11 @@ result = Contrek.contour!(
181
181
  )
182
182
  puts result.metadata[:benchmarks].inspect
183
183
 
184
- { compress: 26.37,
185
- init: 354.339,
186
- inner: 17.9427,
187
- outer: 93.6252,
188
- total: 380.709
184
+ { compress: 23.83,
185
+ init: 313.179,
186
+ inner: 6.16,
187
+ outer: 78.791,
188
+ total: 337.013
189
189
  }
190
190
 
191
191
  ```
@@ -483,9 +483,12 @@ int main() {
483
483
 
484
484
  ## License
485
485
 
486
- This project is licensed under the terms of a dual-license.
486
+ Contrek uses a dual-license model:
487
487
 
488
- See [LICENSE.md](LICENSE.md).
488
+ - **Ruby gem and wrappers** — [MIT](lib/LICENSE-MIT.md). Free to use in any project, including commercial ones.
489
+ - **C++17 core engine** (`ext/cpp_polygon_finder/PolygonFinder`) — [AGPLv3](ext/cpp_polygon_finder/PolygonFinder/LICENSE_AGPL.txt). If you use the core in a SaaS or closed-source product, you must either open your source or [contact the author](https://github.com/runout77) for a commercial license.
490
+
491
+ See [LICENSE.md](LICENSE.md) for full details.
489
492
 
490
493
  ## Changelog
491
494
 
@@ -63,7 +63,6 @@ Tile* Cluster::merge_tiles() {
63
63
  if (shape->outer_polyline->is_on(Polyline::TRACKED_OUTER) || shape->outer_polyline->width() == 0) continue;
64
64
  if (shape->outer_polyline->boundary()) {
65
65
  shape->outer_polyline->partition();
66
- shape->outer_polyline->precalc();
67
66
  }
68
67
  }
69
68
  }
@@ -77,7 +76,7 @@ Tile* Cluster::merge_tiles() {
77
76
  continue;
78
77
  }
79
78
 
80
- if (shape->outer_polyline->boundary() && !shape->outer_polyline->next_tile_eligible_shapes().empty()) {
79
+ if (shape->outer_polyline->any_ancients) {
81
80
  Cursor cursor(*this, shape);
82
81
  Sequence* new_outer = nullptr;
83
82
 
@@ -56,7 +56,6 @@ void Cursor::traverse_outer(Part* act_part,
56
56
  if (all_parts.empty() || last_part != act_part) {
57
57
  all_parts.push_back(act_part);
58
58
  }
59
-
60
59
  bool jumped_to_new_part = false;
61
60
  if (act_part->is(Part::EXCLUSIVE)) {
62
61
  if (act_part->size == 0) return;
@@ -82,36 +81,45 @@ void Cursor::traverse_outer(Part* act_part,
82
81
  }
83
82
  this->cluster.positions_pool.emplace_back(this->cluster.hub(), new_position->payload);
84
83
  outer_joined_polyline->add(&this->cluster.positions_pool.back());
85
-
86
- for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
87
- if (Part *part = shape->outer_polyline->find_first_part_by_position(new_position, act_part->versus())) {
88
- const auto n = all_parts.size();
89
- Part *last_last_part = n >= 2 ? all_parts[n - 2] : nullptr;
90
-
91
- if (last_last_part != part) {
92
- if (n >= 2) {
93
- bool all_seam = true;
94
- for (std::size_t i = all_parts.size() - 2; i < all_parts.size(); ++i) {
95
- if (all_parts[i]->type != Part::SEAM) {
96
- all_seam = false;
97
- break;
98
- }
84
+ new_position->end_point()->tracked_outer = true;
85
+ int versus = act_part->versus();
86
+ auto& q_set = new_position->end_point()->queues();
87
+ auto it = std::find_if(q_set.begin(), q_set.end(), [&](Queueable<Point>* q) {
88
+ Part* p = static_cast<Part*>(q);
89
+ return p->versus() == -versus && p->polyline()->tile != act_part->polyline()->tile;
90
+ });
91
+
92
+ Part* part = nullptr;
93
+ if (it != q_set.end()) {
94
+ part = static_cast<Part*>(*it);
95
+ }
96
+ if (part) {
97
+ const auto n = all_parts.size();
98
+ Part *last_last_part = n >= 2 ? all_parts[n - 2] : nullptr;
99
+ if (last_last_part != part) {
100
+ if (n >= 2) {
101
+ bool all_seam = true;
102
+ for (std::size_t i = all_parts.size() - 2; i < all_parts.size(); ++i) {
103
+ if (all_parts[i]->type != Part::SEAM) {
104
+ all_seam = false;
105
+ break;
99
106
  }
100
- if (all_seam) break;
101
- }
102
- if (shapes_sequence_lookup.insert(part->polyline()->shape).second) {
103
- shapes_sequence.push_back(part->polyline()->shape);
104
107
  }
105
- part->next_position(new_position);
106
- part->dead_end = true;
107
- act_part = part;
108
- jumped_to_new_part = true;
109
- break;
108
+ if (all_seam) break;
109
+ }
110
+ if (shapes_sequence_lookup.insert(part->polyline()->shape).second) {
111
+ shapes_sequence.push_back(part->polyline()->shape);
110
112
  }
113
+ part->next_position(new_position);
114
+ part->dead_end = true;
115
+ act_part = part;
116
+ jumped_to_new_part = true;
117
+ break;
111
118
  }
112
119
  }
113
- if (jumped_to_new_part) break;
114
- act_part->next_position(nullptr);
120
+ if (!jumped_to_new_part) {
121
+ act_part->next_position(nullptr);
122
+ }
115
123
  }
116
124
  }
117
125
  if (jumped_to_new_part) continue;
@@ -122,107 +130,11 @@ void Cursor::traverse_outer(Part* act_part,
122
130
  }
123
131
 
124
132
  std::vector<InnerPolyline*> Cursor::join_inners(Sequence* outer_seq) {
125
- std::vector<InnerPolyline*> retme;
126
- std::vector<Shape*> missing_shapes;
127
-
128
- for (Tile *tile : this->cluster.tiles())
129
- { for (Shape *shape : tile->shapes())
130
- { if (shape->outer_polyline->is_on(Polyline::TRACKED_OUTER) ||
131
- shape->outer_polyline->is_on(Polyline::TRACKED_INNER) ||
132
- !shape->outer_polyline->boundary() ||
133
- std::find(shapes_sequence_.begin(), shapes_sequence_.end(), shape) != shapes_sequence_.end()) {
134
- continue;
135
- }
136
- missing_shapes.push_back(shape);
137
- }
138
- }
139
-
140
- if (missing_shapes.size() > 0) {
141
- std::vector<Shape*> to_delay_shapes;
142
- to_delay_shapes = connect_missings(this->shapes_sequence_, missing_shapes);
143
- if (!to_delay_shapes.empty())
144
- { connect_missings(to_delay_shapes, missing_shapes);
145
- while (!to_delay_shapes.empty()) {
146
- to_delay_shapes = connect_missings(this->shapes_sequence_, to_delay_shapes);
147
- }
148
- }
149
- }
150
-
151
- retme = collect_inner_sequences(outer_seq);
152
- for (Shape* shape : shapes_sequence_) {
153
- shape->outer_polyline->turn_on(Polyline::TRACKED_INNER);
154
- }
155
- return(retme);
156
- }
157
-
158
- std::vector<Shape*> Cursor::connect_missings(std::vector<Shape*> shapes_sequence, std::vector<Shape*> missing_shapes) {
159
- std::vector<Shape*> delay_shapes;
160
-
161
- for (Shape* shape : shapes_sequence)
162
- { Polyline* polyline = shape->outer_polyline;
163
- for (Shape* missing_shape : missing_shapes)
164
- { Polyline* outer_polyline = missing_shape->outer_polyline;
165
- if ( (polyline->mixed_tile_origin == false && outer_polyline->tile == polyline->tile) ||
166
- outer_polyline->is_on(Polyline::TRACKED_OUTER) ||
167
- !polyline->vert_intersect(*outer_polyline)) continue;
168
- std::vector<std::pair<int, int>> intersection = polyline->intersection(outer_polyline);
169
- if (intersection.size() > 0)
170
- { auto result = polyline->sew(intersection, outer_polyline);
171
- if (!result) {
172
- delay_shapes.push_back(missing_shape);
173
- continue;
174
- }
175
- auto& inject_sequences_left = result->first;
176
- auto& inject_sequences_right = result->second;
177
- auto combined = combine(inject_sequences_right, inject_sequences_left);
178
- for (auto& sewn_sequence : combined) {
179
- std::vector<Point*> unique;
180
- unique.reserve(sewn_sequence.size());
181
- for (Point* p : sewn_sequence) {
182
- if (!p) continue;
183
- auto it = std::find_if(
184
- unique.begin(), unique.end(),
185
- [&](Point* up) {
186
- return up && *up == *p;
187
- });
188
- if (it == unique.end()) unique.push_back(p);
189
- }
190
- sewn_sequence.swap(unique);
191
-
192
- if (sewn_sequence.size() <= 1) continue;
193
-
194
- int first_x = sewn_sequence.front()->x;
195
- bool different_x = false;
196
- for (Point* p : sewn_sequence) {
197
- if (p->x != first_x) {
198
- different_x = true;
199
- break;
200
- }
201
- }
202
- if (different_x) {
203
- orphan_inners_.push_back(outer_polyline->tile->shapes_pool->acquire_inner_polyline(sewn_sequence, shape, true));
204
- }
205
- }
206
- polyline->mixed_tile_origin = true;
207
- outer_polyline->clear();
208
- outer_polyline->turn_on(Polyline::TRACKED_OUTER);
209
- outer_polyline->turn_on(Polyline::TRACKED_INNER);
210
- orphan_inners_.insert(
211
- orphan_inners_.end(),
212
- missing_shape->inner_polylines.begin(),
213
- missing_shape->inner_polylines.end());
214
- }
215
- }
216
- }
217
-
218
- return(delay_shapes);
219
- }
220
-
221
- std::vector<InnerPolyline*> Cursor::collect_inner_sequences(Sequence* outer_seq) {
222
133
  std::vector<InnerPolyline*> return_inner_polylines;
223
-
224
- for (Shape* shape : shapes_sequence_)
225
- { Polyline* polyline = shape->outer_polyline;
134
+ std::vector<Shape*> processing_queue = shapes_sequence_;
135
+ for (size_t i = 0; i < shapes_sequence_.size(); ++i)
136
+ { Shape* shape = shapes_sequence_[i];
137
+ Polyline* polyline = shape->outer_polyline;
226
138
  for (Part* part : polyline->parts())
227
139
  { if (part->innerable())
228
140
  { std::vector<Part*> all_parts;
@@ -232,7 +144,6 @@ std::vector<InnerPolyline*> Cursor::collect_inner_sequences(Sequence* outer_seq)
232
144
  };
233
145
  traverse_inner(part, all_parts, bounds);
234
146
  Sequence* retme_sequence = shape->outer_polyline->tile->shapes_pool->acquire_sequence();
235
-
236
147
  for (Part* part : all_parts)
237
148
  { part->touch();
238
149
  retme_sequence->move_from(*part, [&](QNode<Point>* pos) -> bool {
@@ -242,7 +153,7 @@ std::vector<InnerPolyline*> Cursor::collect_inner_sequences(Sequence* outer_seq)
242
153
  position->payload->y <= bounds.max)) {
243
154
  return(false);
244
155
  }
245
- return(!(polyline->tile->tg_border(*position->payload) && position->end_point()->queues_include(outer_seq)));
156
+ return(!(polyline->tile->tg_border(*position->payload) && position->end_point()->tracked_outer));
246
157
  });
247
158
  }
248
159
  if (retme_sequence->is_not_vertical()) {
@@ -270,27 +181,39 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
270
181
  if (act_part->innerable()) {
271
182
  all_parts.push_back(act_part);
272
183
  bool jumped = false;
273
- while (act_part = act_part->next) {
184
+ while (act_part = act_part->circular_next) {
274
185
  if (act_part->innerable()) {
275
186
  all_parts.push_back(act_part);
276
187
  } else {
277
- for (Shape *shape : act_part->polyline()->next_tile_eligible_shapes()) {
278
- for (Part* dest_part : shape->outer_polyline->parts()) {
279
- if (dest_part->trasmuted || dest_part->is(Part::EXCLUSIVE)) continue;
188
+ if (act_part->head)
189
+ { for (auto dest_part_p : static_cast<Position*>(act_part->head)->end_point()->queues()) {
190
+ Part* dest_part = static_cast<Part*>(dest_part_p);
191
+ if (dest_part->polyline()->tile == act_part->polyline()->tile) {
192
+ continue;
193
+ }
280
194
  int dest_part_versus = dest_part->versus();
281
195
  if (dest_part_versus != 0 && dest_part_versus == act_part->versus()) continue;
282
- if (dest_part->intersect_part(act_part)) {
283
- std::vector<EndPoint*> link_seq = dest_part->continuum_to(*act_part);
284
- if (!link_seq.empty()) {
285
- Part* ins_part = pool.acquire(Part::ADDED, act_part->polyline());
286
- for (EndPoint* pos : link_seq) {
287
- this->cluster.positions_pool.emplace_back(pos);
288
- ins_part->add(&this->cluster.positions_pool.back());
289
- }
290
- all_parts.push_back(ins_part);
196
+ std::vector<EndPoint*> link_seq = dest_part->continuum_to(*act_part);
197
+ if (!link_seq.empty()) {
198
+ Part* ins_part = pool.acquire(Part::ADDED, act_part->polyline());
199
+ for (EndPoint* pos : link_seq) {
200
+ this->cluster.positions_pool.emplace_back(pos);
201
+ ins_part->add(&this->cluster.positions_pool.back());
291
202
  }
292
- shape->outer_polyline->turn_on(Polyline::TRACKED_OUTER);
293
- shape->outer_polyline->turn_on(Polyline::TRACKED_INNER);
203
+ all_parts.push_back(ins_part);
204
+ }
205
+ Shape* shape = dest_part->polyline()->shape;
206
+ if (!dest_part->polyline()->is_on(Polyline::TRACKED_OUTER))
207
+ { shapes_sequence_.push_back(shape);
208
+ orphan_inners_.insert(
209
+ orphan_inners_.end(),
210
+ shape->inner_polylines.begin(),
211
+ shape->inner_polylines.end());
212
+ }
213
+ dest_part->polyline()->turn_on(Polyline::TRACKED_OUTER);
214
+ if (!dest_part->touched()) {
215
+ dest_part->touch();
216
+
294
217
  act_part = dest_part->circular_next;
295
218
  jumped = true;
296
219
  break;
@@ -298,7 +221,6 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
298
221
  }
299
222
  if (jumped) break;
300
223
  }
301
- if (jumped) break;
302
224
  if (act_part->is(Part::SEAM)) {
303
225
  all_parts.push_back(act_part);
304
226
  }
@@ -313,17 +235,3 @@ void Cursor::traverse_inner(Part* act_part, std::vector<Part*>& all_parts, Bound
313
235
  else break;
314
236
  }
315
237
  }
316
-
317
- std::vector<std::vector<Point*>> Cursor::combine(std::vector<std::vector<Point*>>& seqa, std::vector<std::vector<Point*>>& seqb)
318
- { std::vector<std::vector<Point*>> rets;
319
- size_t n = std::min(seqa.size(), seqb.size());
320
- for (size_t i = 0; i < n; ++i) {
321
- std::vector<Point*> last = std::move(seqa.back());
322
- seqa.pop_back();
323
- std::vector<Point*> first = std::move(seqb.front());
324
- seqb.erase(seqb.begin());
325
- first.insert(first.end(), last.begin(), last.end());
326
- rets.push_back(std::move(first));
327
- }
328
- return rets;
329
- }
@@ -35,8 +35,6 @@ class Cursor {
35
35
  std::vector<Part*>& all_parts,
36
36
  std::vector<Shape*>& shapes_sequence,
37
37
  Sequence* outer_joined_polyline);
38
- std::vector<InnerPolyline*> collect_inner_sequences(Sequence* outer_seq);
39
38
  void traverse_inner(Part* act_part, std::vector<Part*> &all_parts, Bounds& bounds);
40
- std::vector<std::vector<Point*>> combine(std::vector<std::vector<Point*>>& seqa, std::vector<std::vector<Point*>>& seqb);
41
39
  std::vector<Shape*> connect_missings(std::vector<Shape*> shapes_sequence, std::vector<Shape*> missing_shapes);
42
40
  };
@@ -20,9 +20,11 @@ class EndPoint {
20
20
 
21
21
  void set_point(Point* p) { point_ = p; }
22
22
  Point* get_point() const { return point_; }
23
- std::unordered_set<Queueable<Point>*>& queues() { return queues_; }
23
+ std::vector<Queueable<Point>*>& queues() { return queues_; }
24
24
  bool queues_include(Queueable<Point>* q) const;
25
+ bool tracked_outer = false;
26
+
25
27
  private:
26
28
  Point* point_;
27
- std::unordered_set<Queueable<Point>*> queues_;
29
+ std::vector<Queueable<Point>*> queues_;
28
30
  };
@@ -61,19 +61,6 @@ void Part::touch()
61
61
  { this->touched_ = true;
62
62
  }
63
63
 
64
- bool Part::intersect_part(Part* other_part)
65
- { bool intersect = false;
66
- other_part->each([&](QNode<Point>* pos) -> bool {
67
- Position *position = static_cast<Position*>(pos);
68
- if (position->end_point()->queues_include(this))
69
- { intersect = true;
70
- return(false);
71
- }
72
- return(true);
73
- });
74
- return(intersect);
75
- }
76
-
77
64
  void Part::orient()
78
65
  { if (this->size <= 1 || (this->size == 2 && this->inverts)) {
79
66
  this->versus_ = 0;
@@ -28,10 +28,10 @@ class Part : public Queueable<Point> {
28
28
  ADDED = 2
29
29
  };
30
30
  explicit Part(Types type, Polyline* polyline);
31
+ bool listable() const override { return true; }
31
32
  bool is(Types type);
32
33
  bool inverts = false;
33
34
  bool trasmuted = false;
34
- bool delayed = false;
35
35
  bool dead_end = false;
36
36
  Part* next = nullptr;
37
37
  Part* prev = nullptr;
@@ -45,8 +45,6 @@ class Part : public Queueable<Point> {
45
45
  const bool touched() const { return touched_; }
46
46
  const int versus() const { return versus_; }
47
47
  void touch();
48
- bool intersect_part(Part* other_part);
49
- void set_polyline(Polyline* polyline) { this->polyline_ = polyline; }
50
48
  void orient();
51
49
  std::string inspect();
52
50
  std::vector<EndPoint*> continuum_to(const Part& other_part) const;
@@ -35,17 +35,6 @@ void Partitionable::add_part(Part* new_part)
35
35
  if (new_part->is(Part::SEAM)) new_part->orient();
36
36
  }
37
37
 
38
- void Partitionable::insert_after(Part* part, Part* new_part) {
39
- auto it = std::find(parts_.begin(), parts_.end(), part);
40
- if (it != parts_.end()) parts_.insert(it + 1, new_part);
41
- new_part->prev = part;
42
- new_part->next = part->next;
43
- new_part->circular_next = part->next;
44
- if (part->next) part->next->prev = new_part;
45
- part->next = new_part;
46
- part->circular_next = new_part;
47
- }
48
-
49
38
  void Partitionable::partition()
50
39
  { this->parts_.clear();
51
40
  Polyline *polyline = static_cast<Polyline*>(this);
@@ -79,123 +68,6 @@ void Partitionable::partition()
79
68
  this->trasmute_parts();
80
69
  }
81
70
 
82
- Part* Partitionable::find_first_part_by_position(Position* position, int versus) {
83
- for (Part* part : this->parts_)
84
- { if ( part->is(Part::SEAM) &&
85
- part->versus() == -(versus) &&
86
- position->end_point()->queues_include(part)) return(part);
87
- }
88
- return(nullptr);
89
- }
90
-
91
- std::optional<SewReturnData> Partitionable::sew(std::vector<std::pair<int, int>> intersection, Polyline* other)
92
- { std::vector<int> matching_part_indexes;
93
- std::vector<int> other_matching_part_indexes;
94
- matching_part_indexes.reserve(intersection.size());
95
- other_matching_part_indexes.reserve(intersection.size());
96
-
97
- // traspose
98
- for (const auto& pair : intersection) {
99
- matching_part_indexes.push_back(pair.first);
100
- other_matching_part_indexes.push_back(pair.second);
101
- }
102
- std::sort(matching_part_indexes.begin(), matching_part_indexes.end());
103
- std::sort(other_matching_part_indexes.begin(), other_matching_part_indexes.end());
104
-
105
- auto start_it_before = other->parts_.begin() + other_matching_part_indexes.back() + 1;
106
- auto end_it_before = other->parts_.end();
107
- std::vector<Part*> before_parts(start_it_before, end_it_before);
108
- auto start_it_after = other->parts_.begin();
109
- auto end_it_after = other->parts_.begin() + other_matching_part_indexes.front();
110
- std::vector<Part*> after_parts(start_it_after, end_it_after);
111
-
112
- int start_idx = matching_part_indexes.front();
113
- int end_idx = matching_part_indexes.back();
114
- Part* part_start = parts_[start_idx];
115
- Part* part_end = parts_[end_idx];
116
-
117
- auto collect_sequences = [&](const std::vector<int>& indices, std::vector<Part*>& p_list)
118
- -> std::vector<std::vector<Point*>> {
119
- std::vector<std::vector<Point*>> result;
120
- int last_n = -1;
121
- if (indices.size() < 2) return result;
122
- for (int n = indices.front() + 1; n < indices.back(); ++n) {
123
- if (std::find(indices.begin(), indices.end(), n) == indices.end()) {
124
- Part* part = p_list[n];
125
- if (part->is(Part::SEAM) && part->size > 0 && !part->delayed) {
126
- part->delayed = true;
127
- result.clear();
128
- result.push_back({nullptr});
129
- return result;
130
- }
131
- if (last_n == (n - 1) && !result.empty()) {
132
- std::vector<Point*> pts = part->to_vector();
133
- result.back().insert(result.back().end(), pts.begin(), pts.end());
134
- } else {
135
- result.push_back(part->to_vector());
136
- }
137
- last_n = n;
138
- }
139
- }
140
- return result;
141
- };
142
- auto left = collect_sequences(matching_part_indexes, this->parts_);
143
- if (!left.empty() && !left[0].empty() && left[0][0] == nullptr) {
144
- return std::nullopt;
145
- }
146
- auto right = collect_sequences(other_matching_part_indexes, other->parts_);
147
- if (!right.empty() && !right[0].empty() && right[0][0] == nullptr) {
148
- return std::nullopt;
149
- }
150
-
151
- if (part_start != part_end) {
152
- for (int n = end_idx - 1; n > start_idx; --n) {
153
- Part* delete_part = parts_[n];
154
- // Topological detachment of pointers
155
- if (delete_part->prev) delete_part->prev->next = delete_part->next;
156
- if (delete_part->next) delete_part->next->prev = delete_part->prev;
157
- parts_.erase(parts_.begin() + n);
158
- }
159
- }
160
-
161
- std::vector<Part*> all_parts;
162
- Polyline* polyline = static_cast<Polyline*>(this);
163
- all_parts.reserve(before_parts.size() + after_parts.size());
164
- all_parts.insert(all_parts.end(), before_parts.begin(), before_parts.end());
165
- all_parts.insert(all_parts.end(), after_parts.begin(), after_parts.end());
166
- Part* will_be_last = all_parts.empty() ? nullptr : all_parts.back();
167
- for (auto it = all_parts.rbegin(); it != all_parts.rend(); ++it) {
168
- Part* p = *it;
169
- this->insert_after(part_start, p);
170
- auto& op = other->parts_;
171
- op.erase(std::remove(op.begin(), op.end(), p), op.end());
172
- p->set_polyline(polyline);
173
- }
174
-
175
- part_start->type = Part::EXCLUSIVE;
176
- PartPool& pool = polyline->tile->cluster->parts_pool;
177
- Part* new_end_part = pool.acquire(Part::EXCLUSIVE, polyline);
178
- new_end_part->add(part_end->tail);
179
- part_start->singleton();
180
-
181
- // deletes part_end
182
- if (part_start != part_end) {
183
- if (part_end->prev) part_end->prev->next = part_end->next;
184
- if (part_end->next) part_end->next->prev = part_end->prev;
185
- auto it = std::find(parts_.begin(), parts_.end(), part_end);
186
- if (it != parts_.end()) {
187
- parts_.erase(it);
188
- }
189
- }
190
-
191
- Part* reference_part = (will_be_last != nullptr) ? will_be_last : part_start;
192
- this->insert_after(reference_part, new_end_part);
193
-
194
- polyline->reset_tracked_endpoints();
195
-
196
- return std::make_pair(left, right);
197
- }
198
-
199
71
  void Partitionable::trasmute_parts()
200
72
  { std::vector<Part*> insides;
201
73
  for (Part* p : parts_) {
@@ -10,18 +10,13 @@
10
10
  #pragma once
11
11
  #include <vector>
12
12
  #include <utility>
13
- #include <optional>
14
13
  #include "Part.h"
15
14
 
16
- using SewReturnData = std::pair<std::vector<std::vector<Point*>>, std::vector<std::vector<Point*>>>;
17
-
18
15
  class Partitionable {
19
16
  public:
20
17
  explicit Partitionable() {}
21
18
  virtual ~Partitionable() = default;
22
19
  void partition();
23
- Part* find_first_part_by_position(Position* position, int versus);
24
- std::optional<SewReturnData> sew(std::vector<std::pair<int, int>> intersection, Polyline* other);
25
20
  const std::vector<Part*> parts() const { return parts_; }
26
21
 
27
22
  protected:
@@ -29,6 +24,5 @@ class Partitionable {
29
24
 
30
25
  private:
31
26
  void add_part(Part* new_part);
32
- void insert_after(Part* part, Part* new_part);
33
27
  void trasmute_parts();
34
28
  };