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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +11 -8
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +1 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +66 -158
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +0 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/EndPoint.h +4 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +0 -13
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +1 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +0 -128
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +0 -6
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.cpp +0 -50
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Polyline.h +2 -8
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Position.cpp +22 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +1 -57
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +0 -12
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.h +0 -3
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/VerticalMerger.cpp +11 -3
- data/lib/contrek/finder/concurrent/cluster.rb +1 -2
- data/lib/contrek/finder/concurrent/cursor.rb +56 -120
- data/lib/contrek/finder/concurrent/end_point.rb +2 -0
- data/lib/contrek/finder/concurrent/part.rb +2 -20
- data/lib/contrek/finder/concurrent/partitionable.rb +0 -81
- data/lib/contrek/finder/concurrent/polyline.rb +3 -47
- data/lib/contrek/finder/concurrent/position.rb +8 -2
- data/lib/contrek/finder/concurrent/queueable.rb +0 -41
- data/lib/contrek/finder/concurrent/tile.rb +0 -4
- data/lib/contrek/finder/concurrent/vertical_merger.rb +9 -5
- data/lib/contrek/version.rb +1 -1
- metadata +3 -3
- /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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b46d3ae57168cfeb52a0788b6e11af74a164c8e19b4414783f95ac96d1507ed2
|
|
4
|
+
data.tar.gz: e0637093c914f2426a74b4a47ebeac34152ae473de619fda2e164f22c5d03bc8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
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.
|
|
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:
|
|
185
|
-
init:
|
|
186
|
-
inner:
|
|
187
|
-
outer:
|
|
188
|
-
total:
|
|
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
|
-
|
|
486
|
+
Contrek uses a dual-license model:
|
|
487
487
|
|
|
488
|
-
|
|
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->
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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)
|
|
114
|
-
|
|
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 (
|
|
225
|
-
{
|
|
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()->
|
|
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->
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
293
|
-
|
|
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::
|
|
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::
|
|
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
|
};
|