1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Sub-path Ordering functions for embroidery stitch LPE (Implementation)
4  *
5  * Copyright (C) 2016 Michael Soegtrop
6  *
7  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
8  */
9 
10 #include "live_effects/lpe-embrodery-stitch-ordering.h"
11 
12 #include <numeric>
13 
14 namespace Inkscape {
15 namespace LivePathEffect {
16 namespace LPEEmbroderyStitchOrdering {
17 
18 using namespace Geom;
19 
20 // ==================== Debug Trace Macros ====================
21 
22 // ATTENTION: both level and area macros must be enabled for tracing
23 
24 // These macros are for enabling certain levels of tracing
25 #define DebugTrace1(list) // g_warning list
26 #define DebugTrace2(list) // g_warning list
27 
28 // These macros are for enabling certain areas of tracing
29 #define DebugTraceGrouping(list) // list
30 #define DebugTraceTSP(list)      // list
31 
32 // Combinations of above
33 #define DebugTrace1TSP(list) DebugTraceTSP( DebugTrace1(list) )
34 #define DebugTrace2TSP(list) DebugTraceTSP( DebugTrace2(list) )
35 
36 // ==================== Template Utilities ====================
37 
38 // Delete all objects pointed to by a vector and clear the vector
39 
delete_and_clear(std::vector<T> & vector)40 template< typename T > void delete_and_clear(std::vector<T> &vector)
41 {
42     for (typename std::vector<T>::iterator it = vector.begin(); it != vector.end(); ++it) {
43         delete *it;
44     }
45     vector.clear();
46 }
47 
48 // Assert that there are no duplicates in a vector
49 
assert_unique(std::vector<T> & vector)50 template< typename T > void assert_unique(std::vector<T> &vector)
51 {
52     typename std::vector<T> copy = vector;
53     std::sort(copy.begin(), copy.end());
54     assert(std::unique(copy.begin(), copy.end()) == copy.end());
55 }
56 
57 // remove element(s) by value
58 
remove_by_value(std::vector<T> * vector,const T & value)59 template< typename T > void remove_by_value(std::vector<T> *vector, const T &value)
60 {
61     vector->erase(std::remove(vector->begin(), vector->end(), value), vector->end());
62 }
63 
64 // fill a vector with increasing elements (similar to C++11 iota)
65 // iota is included in some C++ libraries, not in other (it is always included for C++11)
66 // To avoid issues, use our own name (not iota)
67 
68 template<class OutputIterator, class Counter>
fill_increasing(OutputIterator begin,OutputIterator end,Counter counter)69 void fill_increasing(OutputIterator begin, OutputIterator end, Counter counter)
70 {
71     while (begin != end) {
72         *begin++ = counter++;
73     }
74 }
75 
76 // check if an iteratable sequence contains an element
77 
78 template<class InputIterator, class Element>
contains(InputIterator begin,InputIterator end,const Element & elem)79 bool contains(InputIterator begin, InputIterator end, const Element &elem)
80 {
81     while (begin != end) {
82         if (*begin == elem) {
83             return true;
84         }
85         ++begin;
86     }
87     return false;
88 }
89 
90 // Check if a vector contains an element
91 
92 template<class Element>
contains(std::vector<Element> const & vector,const Element & elem)93 bool contains(std::vector<Element> const &vector, const Element &elem)
94 {
95     return contains(vector.begin(), vector.end(), elem);
96 }
97 
98 // ==================== Multi-dimensional iterator functions ====================
99 
100 // Below are 3 simple template functions to do triangle/pyramid iteration (without diagonal).
101 // Here is a sample of iterating over 5 elements in 3 dimensions:
102 //
103 // 0 1 2
104 // 0 1 3
105 // 0 1 4
106 // 0 2 3
107 // 0 2 4
108 // 1 2 4
109 // 1 3 4
110 // 2 3 4
111 // end end end
112 //
113 // If the number of elements is less then the number of dimensions, the number of dimensions is reduced automatically.
114 //
115 // I thought about creating an iterator class for this, but it doesn't match that well, so I used functions on iterator vectors.
116 
117 // Initialize a vector of iterators
118 
119 template<class Iterator>
triangleit_begin(std::vector<Iterator> & iterators,Iterator const & begin,Iterator const & end,size_t n)120 void triangleit_begin(std::vector<Iterator> &iterators, Iterator const &begin, Iterator const &end, size_t n)
121 {
122     iterators.clear();
123     // limit number of dimensions to number of elements
124     size_t n1 = end - begin;
125     n = std::min(n, n1);
126     if (n) {
127         iterators.push_back(begin);
128         for (int i = 1; i < n; i++) {
129             iterators.push_back(iterators.back() + 1);
130         }
131     }
132 }
133 
134 // Increment a vector of iterators
135 
136 template<class Iterator>
triangleit_incr(std::vector<Iterator> & iterators,Iterator const & end)137 void triangleit_incr(std::vector<Iterator> &iterators, Iterator const &end)
138 {
139     size_t n = iterators.size();
140 
141     for (int i = 0; i < n; i++) {
142         iterators[n - 1 - i]++;
143         // Each dimension ends at end-i, so that there are elements left for the i higher dimensions
144         if (iterators[n - 1 - i] != end - i) {
145             // Assign increasing numbers to the higher dimension
146             for (int j = n - i; j < n; j++) {
147                 iterators[j] = iterators[j - 1] + 1;
148             }
149             return;
150         }
151     }
152 }
153 
154 // Check if a vector of iterators is at the end
155 
156 template<class Iterator>
triangleit_test(std::vector<Iterator> & iterators,Iterator const & end)157 bool triangleit_test(std::vector<Iterator> &iterators, Iterator const &end)
158 {
159     if (iterators.empty()) {
160         return false;
161     } else {
162         return iterators.back() != end;
163     }
164 }
165 
166 // ==================== Trivial Ordering Functions ====================
167 
168 // Sub-path reordering: do nothing - keep original order
169 
OrderingOriginal(std::vector<OrderingInfo> & infos)170 void OrderingOriginal(std::vector<OrderingInfo> &infos)
171 {
172 }
173 
174 // Sub-path reordering: reverse every other sub path
175 
OrderingZigZag(std::vector<OrderingInfo> & infos,bool revfirst)176 void OrderingZigZag(std::vector<OrderingInfo> &infos, bool revfirst)
177 {
178     for (auto & info : infos) {
179         info.reverse = (info.index & 1) == (revfirst ? 0 : 1);
180     }
181 }
182 
183 // Sub-path reordering: continue with the neartest start or end point of yet unused sub paths
184 
OrderingClosest(std::vector<OrderingInfo> & infos,bool revfirst)185 void OrderingClosest(std::vector<OrderingInfo> &infos, bool revfirst)
186 {
187     std::vector<OrderingInfo> result;
188     result.reserve(infos.size());
189 
190     result.push_back(infos[0]);
191     result.back().reverse = revfirst;
192     Point p = result.back().GetEndRev();
193 
194     infos[0].used = true;
195 
196 
197     for (unsigned int iRnd = 1; iRnd < infos.size(); iRnd++) {
198         // find closest point to p
199         unsigned iBest = 0;
200         bool revBest = false;
201         Coord distBest = Geom::infinity();
202 
203         for (std::vector<OrderingInfo>::iterator it = infos.begin(); it != infos.end(); ++it) {
204             it->index = it - infos.begin();
205             it->reverse = (it->index & 1) != 0;
206 
207             if (!it->used) {
208                 Coord dist = distance(p, it->GetBegOrig());
209                 if (dist < distBest) {
210                     distBest = dist;
211                     iBest = it - infos.begin();
212                     revBest = false;
213                 }
214 
215                 dist = distance(p, it->GetEndOrig());
216                 if (dist < distBest) {
217                     distBest = dist;
218                     iBest = it - infos.begin();
219                     revBest = true;
220                 }
221             }
222         }
223 
224         result.push_back(infos[iBest]);
225         result.back().reverse = revBest;
226         p = result.back().GetEndRev();
227         infos[iBest].used = true;
228     }
229 
230     infos = result;
231 }
232 
233 // ==================== Traveling Salesman k-opt Ordering Function and Utilities ====================
234 
235 // A Few notes on this:
236 // - This is a relatively simple Lin-type k-opt algorithm, but the grouping optimizations done make it already quite complex.
237 // - The main Ordering Function is OrderingAdvanced
238 // - Lines which start at the end of another line are connected and treated as one (struct OrderingInfoEx)
239 // - Groups of zig-zag OrderingInfoEx are grouped (struct OrderingGroup) if both ends of the segment mutually agree with a next neighbor.
240 //   These groups are treated as a unit in the TSP algorithm.
241 //   The only option is to reverse the first segment, so that a group has 4 end points, 2 of which are used externally.
242 // - Run a k-opt (k=2..5) Lin like Traveling Salesman Problem algorithm on the groups as a unit and the remaining edges.
243 //   See https://en.wikipedia.org/wiki/Travelling_salesman_problem#Iterative_improvement
244 //   The algorithm uses a greedy nearest neighbor as start configuration and does not use repeated random starts.
245 // - The algorithm searches an open tour (rather than a closed one), so the longest segment in the closed path is ignored.
246 // - TODO: it might be faster to use k=3 with a few random starting patterns instead of k=5
247 // - TODO: it is surely wiser to implement e.g. Lin-Kenrighan TSP, but the simple k-opt works ok.
248 // - TODO(EASY): add a jump distance, above which threads are removed and make the length of this jump distance constant and large,
249 //   so that mostly the number of jumps is optimized
250 
251 // Find 2 nearest points to given point
252 
FindNearest2(const std::vector<OrderingInfoEx * > & infos)253 void OrderingPoint::FindNearest2(const std::vector<OrderingInfoEx *> &infos)
254 {
255     // This implementation is not terribly elegant (unSTLish).
256     // But for the first 2 elements using e.g. partial_sort is not simpler.
257 
258     Coord dist0 = Geom::infinity();
259     Coord dist1 = Geom::infinity();
260     nearest[0] = nullptr;
261     nearest[1] = nullptr;
262 
263     for (auto info : infos) {
264         Coord dist = distance(point, info->beg.point);
265         if (dist < dist1) {
266             if (&info->beg != this && &info->end != this) {
267                 if (dist < dist0) {
268                     nearest[1] = nearest[0];
269                     nearest[0] = &info->beg;
270                     dist1 = dist0;
271                     dist0 = dist;
272                 } else {
273                     nearest[1] = &info->beg;
274                     dist1 = dist;
275                 }
276             }
277         }
278 
279         dist = distance(point, info->end.point);
280         if (dist < dist1) {
281             if (&info->beg != this && &info->end != this) {
282                 if (dist < dist0) {
283                     nearest[1] = nearest[0];
284                     nearest[0] = &info->end;
285                     dist1 = dist0;
286                     dist0 = dist;
287                 } else {
288                     nearest[1] = &info->end;
289                     dist1 = dist;
290                 }
291             }
292         }
293     }
294 }
295 
296 // Check if "this" is among the nearest of its nearest
297 
EnforceMutual()298 void OrderingPoint::EnforceMutual()
299 {
300     if (nearest[0] && !(this == nearest[0]->nearest[0] || this == nearest[0]->nearest[1])) {
301         nearest[0] = nullptr;
302     }
303 
304     if (nearest[1] && !(this == nearest[1]->nearest[0] || this == nearest[1]->nearest[1])) {
305         nearest[1] = nullptr;
306     }
307 
308     if (nearest[1] && !nearest[0]) {
309         nearest[0] = nearest[1];
310         nearest[1] = nullptr;
311     }
312 }
313 
314 // Check if the subpath indices of this and other are the same, otherwise zero both nearest
315 
EnforceSymmetric(const OrderingPoint & other)316 void OrderingPoint::EnforceSymmetric(const OrderingPoint &other)
317 {
318     if (nearest[0] && !(
319                 (other.nearest[0] && nearest[0]->infoex == other.nearest[0]->infoex) ||
320                 (other.nearest[1] && nearest[0]->infoex == other.nearest[1]->infoex)
321             )) {
322         nearest[0] = nullptr;
323     }
324 
325     if (nearest[1] && !(
326                 (other.nearest[0] && nearest[1]->infoex == other.nearest[0]->infoex) ||
327                 (other.nearest[1] && nearest[1]->infoex == other.nearest[1]->infoex)
328             )) {
329         nearest[1] = nullptr;
330     }
331 
332     if (nearest[1] && !nearest[0]) {
333         nearest[0] = nearest[1];
334         nearest[1] = nullptr;
335     }
336 }
337 
Dump()338 void OrderingPoint::Dump()
339 {
340     // COMENTED TO SUPRESS WARNING UNUSED AUTOR TAKE IT UNCOMENTED
341     // Coord dist0 = nearest[0] ? distance(point, nearest[0]->point) : -1.0;
342     // Coord dist1 = nearest[1] ? distance(point, nearest[1]->point) : -1.0;
343     // int idx0 = nearest[0] ? nearest[0]->infoex->idx : -1;
344     // int idx1 = nearest[1] ? nearest[1]->infoex->idx : -1;
345     DebugTrace2(("I=%d X=%.5lf Y=%.5lf d1=%.3lf d2=%.3lf i1=%d i2=%d", infoex->idx, point.x(), 297.0 - point.y(), dist0, dist1, idx0, idx1));
346 }
347 
348 
349 // If this element can be grouped (has neighbours) but is not yet grouped, create a new group
350 
MakeGroup(std::vector<OrderingInfoEx * > & infos,std::vector<OrderingGroup * > * groups)351 void OrderingInfoEx::MakeGroup(std::vector<OrderingInfoEx *> &infos, std::vector<OrderingGroup *> *groups)
352 {
353     if (grouped || !beg.HasNearest() || !end.HasNearest()) {
354         return;
355     }
356 
357     groups->push_back(new OrderingGroup(groups->size()));
358 
359     // Add neighbors recursively
360     AddToGroup(infos, groups->back());
361 }
362 
363 // Add this and all connected elements to the group
364 
AddToGroup(std::vector<OrderingInfoEx * > & infos,OrderingGroup * group)365 void OrderingInfoEx::AddToGroup(std::vector<OrderingInfoEx *> &infos, OrderingGroup *group)
366 {
367     if (grouped || !beg.HasNearest() || !end.HasNearest()) {
368         return;
369     }
370 
371     group->items.push_back(this);
372     grouped = true;
373     // Note: beg and end neighbors have been checked to be symmetric
374     if (beg.nearest[0]) {
375         beg.nearest[0]->infoex->AddToGroup(infos, group);
376     }
377     if (beg.nearest[1]) {
378         beg.nearest[1]->infoex->AddToGroup(infos, group);
379     }
380     if (end.nearest[0]) {
381         end.nearest[0]->infoex->AddToGroup(infos, group);
382     }
383     if (end.nearest[1]) {
384         end.nearest[1]->infoex->AddToGroup(infos, group);
385     }
386 }
387 
388 // Constructor
389 
OrderingGroupNeighbor(OrderingGroupPoint * me,OrderingGroupPoint * other)390 OrderingGroupNeighbor::OrderingGroupNeighbor(OrderingGroupPoint *me, OrderingGroupPoint *other) :
391     point(other),
392     distance(Geom::distance(me->point, other->point))
393 {
394 }
395 
396 // Comparison function for sorting by distance
397 
Compare(const OrderingGroupNeighbor & a,const OrderingGroupNeighbor & b)398 bool OrderingGroupNeighbor::Compare(const OrderingGroupNeighbor &a, const OrderingGroupNeighbor &b)
399 {
400     return a.distance < b.distance;
401 }
402 
403 // Find the nearest unused neighbor point
404 
FindNearestUnused()405 OrderingGroupNeighbor *OrderingGroupPoint::FindNearestUnused()
406 {
407     for (auto & it : nearest) {
408         if (!it.point->used) {
409             DebugTrace1TSP(("Nearest: group %d, size %d, point %d, nghb %d, xFrom %.4lf, yFrom %.4lf, xTo %.4lf, yTo %.4lf, dist %.4lf",
410                             it->point->group->index, it->point->group->items.size(), it->point->indexInGroup, it - nearest.begin(),
411                             point.x(), 297 - point.y(),
412                             it->point->point.x(), 297 - it->point->point.y(),
413                             it->distance));
414             return &it;
415         }
416     }
417 
418     // it shouldn't happen that we can't find any point at all
419     assert(0);
420     return nullptr;
421 }
422 
423 // Return the other end in the group of the point
424 
GetOtherEndGroup()425 OrderingGroupPoint *OrderingGroupPoint::GetOtherEndGroup()
426 {
427     return group->endpoints[ indexInGroup ^ 1 ];
428 }
429 
430 // Return the alternate point (if one exists), 0 otherwise
431 
GetAltPointGroup()432 OrderingGroupPoint *OrderingGroupPoint::GetAltPointGroup()
433 {
434     if (group->nEndPoints < 4) {
435         return nullptr;
436     }
437 
438     OrderingGroupPoint *alt = group->endpoints[ indexInGroup ^ 2 ];
439     return alt->used ? nullptr : alt;
440 }
441 
442 
443 // Sets the rev flags in the group assuming that the group starts with this point
444 
SetRevInGroup()445 void OrderingGroupPoint::SetRevInGroup()
446 {
447     // If this is not a front point, the item list needs to be reversed
448     group->revItemList = !front;
449 
450     // If this is not a begin point, the items need to be reversed
451     group->revItems = !begin;
452 }
453 
454 // Mark an end point as used and also mark the two other alternating points as used
455 // Returns the used point
456 
UsePoint()457 void OrderingGroupPoint::UsePoint()
458 {
459     group->UsePoint(indexInGroup);
460 }
461 
462 // Mark an end point as unused and possibly also mark the two other alternating points as unused
463 // Returns the used point
464 
UnusePoint()465 void OrderingGroupPoint::UnusePoint()
466 {
467     group->UnusePoint(indexInGroup);
468 }
469 
470 // Return the other end in the connection
GetOtherEndConnection()471 OrderingGroupPoint *OrderingGroupPoint::GetOtherEndConnection()
472 {
473     assert(connection);
474     assert(connection->points[ indexInConnection ] == this);
475     assert(connection->points[ indexInConnection ^ 1 ]);
476 
477     return connection->points[ indexInConnection ^ 1 ];
478 }
479 
480 
481 // Set the end points of a group from the items
482 
SetEndpoints()483 void OrderingGroup::SetEndpoints()
484 {
485     assert(items.size() >= 1);
486 
487     if (items.size() == 1) {
488         // A simple line:
489         //
490         // b0-front--e1
491 
492         nEndPoints = 2;
493         endpoints[0] = new OrderingGroupPoint(items.front()->beg.point, this, 0, true,  true);
494         endpoints[1] = new OrderingGroupPoint(items.front()->end.point, this, 1, false, true);
495     } else {
496         // If the number of elements is even, the group is
497         // either from items.front().beg to items.back().beg
498         // or from     items.front().end to items.back().end:
499         // Below: b=beg, e=end, numbers are end point indices
500         //
501         // b0-front--e     b0-front--e2
502         //           |     |
503         // b---------e     b---------e
504         // |                         |
505         // b---------e     b---------e
506         //           |     |
507         // b1-back---e     b1-back---e3
508         //
509         //
510         // if the number of elements is odd, it is crossed:
511         //
512         // b0-front--e     b--front--e2
513         //           |     |
514         // b---------e     b---------e
515         // |                         |
516         // b--back---e1    b3-back---e
517         //
518         // TODO: this is not true with the following kind of pattern
519         //
520         // b--front--e
521         // b---------e
522         //           b--------e
523         //           b--back--e
524         //
525         // Here only one connection is possible, from front.end to back.beg
526         //
527         // TODO: also this is not true if segment direction is alternating
528         //
529         // TOTO: => Just see where you end up from front().begin and front().end
530         //
531         // the numbering is such that either end points 0 and 1 are used or 2 and 3.
532         int cross = items.size() & 1 ? 2 : 0;
533         nEndPoints = 4;
534 
535         endpoints[0      ] = new OrderingGroupPoint(items.front()->beg.point, this, 0,       true,  true);
536         endpoints[1 ^ cross] = new OrderingGroupPoint(items.back() ->beg.point, this, 1 ^ cross, true,  false);
537         endpoints[2      ] = new OrderingGroupPoint(items.front()->end.point, this, 2,       false, true);
538         endpoints[3 ^ cross] = new OrderingGroupPoint(items.back() ->end.point, this, 3 ^ cross, false, false);
539     }
540 }
541 
542 // Add all points from another group as neighbors
543 
AddNeighbors(OrderingGroup * nghb)544 void OrderingGroup::AddNeighbors(OrderingGroup *nghb)
545 {
546     for (int iThis = 0; iThis < nEndPoints; iThis++) {
547         for (int iNghb = 0; iNghb < nghb->nEndPoints; iNghb++) {
548             endpoints[iThis]->nearest.emplace_back(endpoints[iThis], nghb->endpoints[iNghb]);
549         }
550     }
551 }
552 
553 // Mark an end point as used and also mark the two other alternating points as used
554 // Returns the used point
555 
UsePoint(int index)556 OrderingGroupPoint *OrderingGroup::UsePoint(int index)
557 {
558     assert(index < nEndPoints);
559     assert(!endpoints[index]->used);
560     endpoints[index]->used = true;
561     if (nEndPoints == 4) {
562         int offs = index < 2 ? 2 : 0;
563         endpoints[0 + offs]->used = true;
564         endpoints[1 + offs]->used = true;
565     }
566 
567     return endpoints[index];
568 }
569 
570 // Mark an end point as unused and possibly also mark the two other alternating points as unused
571 // Returns the used point
572 
UnusePoint(int index)573 void OrderingGroup::UnusePoint(int index)
574 {
575     assert(index < nEndPoints);
576     assert(endpoints[index]->used);
577     endpoints[index]->used = false;
578 
579     if (nEndPoints == 4 && !endpoints[index ^ 1]->used) {
580         int offs = index < 2 ? 2 : 0;
581         endpoints[0 + offs]->used = false;
582         endpoints[1 + offs]->used = false;
583     }
584 }
585 
586 // Add an end point
AddPoint(OrderingGroupPoint * point)587 void OrderingSegment::AddPoint(OrderingGroupPoint *point)
588 {
589     assert(point);
590     assert(nEndPoints < 4);
591     endpoints[ nEndPoints++ ] = point;
592 
593     // If both ends of a group are added and the group has 4 points, add the other two as well
594     if (nEndPoints == 2 && endpoints[0]->group == endpoints[1]->group) {
595         OrderingGroup *group = endpoints[0]->group;
596         if (group->nEndPoints == 4) {
597             for (int i = 0; i < 4; i++) {
598                 endpoints[i] = group->endpoints[i];
599             }
600             nEndPoints = 4;
601         }
602     }
603 }
604 
605 // Get begin point (taking swap and end bit into account
GetBeginPoint(unsigned int iSwap,unsigned int iEnd)606 OrderingGroupPoint *OrderingSegment::GetBeginPoint(unsigned int iSwap, unsigned int iEnd)
607 {
608     int iPoint = ((iEnd >> endbit) & 1) + (((iSwap >> swapbit) & 1) << 1);
609     assert(iPoint < nEndPoints);
610     return endpoints[iPoint];
611 }
612 
613 // Get end point (taking swap and end bit into account
GetEndPoint(unsigned int iSwap,unsigned int iEnd)614 OrderingGroupPoint *OrderingSegment::GetEndPoint(unsigned int iSwap, unsigned int iEnd)
615 {
616     int iPoint = (((iEnd >> endbit) & 1) ^ 1) + (((iSwap >> swapbit) & 1) << 1);
617     assert(iPoint < nEndPoints);
618     return endpoints[iPoint];
619 }
620 
621 
622 // Find the next unused point in list
FindUnusedAndUse(std::vector<OrderingGroupPoint * > * unusedPoints,std::vector<OrderingGroupPoint * >::iterator const from)623 std::vector<OrderingGroupPoint *>::iterator FindUnusedAndUse(std::vector<OrderingGroupPoint *> *unusedPoints, std::vector<OrderingGroupPoint *>::iterator const from)
624 {
625     for (std::vector<OrderingGroupPoint *>::iterator it = from; it != unusedPoints->end(); ++it) {
626         if (!(*it)->used) {
627             (*it)->UsePoint();
628             return it;
629         }
630     }
631     return unusedPoints->end();
632 }
633 
634 // Find the shortest reconnect between the given points
635 
FindShortestReconnect(std::vector<OrderingSegment> & segments,std::vector<OrderingGroupConnection * > & connections,std::vector<OrderingGroupConnection * > & allconnections,OrderingGroupConnection ** longestConnect,Coord * total,Coord olddist)636 bool FindShortestReconnect(std::vector<OrderingSegment> &segments, std::vector<OrderingGroupConnection *> &connections, std::vector<OrderingGroupConnection *> &allconnections, OrderingGroupConnection **longestConnect, Coord *total, Coord olddist)
637 {
638     // Find the longest connection outside of the active set
639     // The longest segment is then the longest of this longest outside segment and all inside segments
640     OrderingGroupConnection *longestOutside = nullptr;
641 
642     if (contains(connections, *longestConnect)) {
643         // The longest connection is inside the active set, so we need to search for the longest outside
644         Coord length = 0.0;
645         for (auto & allconnection : allconnections) {
646             if (allconnection->Distance() > length) {
647                 if (!contains(connections, allconnection)) {
648                     longestOutside = allconnection;
649                     length = allconnection->Distance();
650                 }
651             }
652         }
653     } else {
654         longestOutside = *longestConnect;
655     }
656 
657     // length of longestConnect outside
658     Coord longestOutsideLength = longestOutside ? longestOutside->Distance() : 0.0;
659 
660     // We measure length without the longest, so subtract the longest length from the old distance
661     olddist -= (*longestConnect)->Distance();
662 
663     // Assign a swap bit and end bit to each active connection
664     int nEndBits = 0;
665     int nSwapBits = 0;
666     for (auto & segment : segments) {
667         segment.endbit = nEndBits++;
668         if (segment.nEndPoints == 4) {
669             segment.swapbit = nSwapBits++;
670         } else {
671             // bit 32 should always be 0
672             segment.swapbit = 31;
673         }
674     }
675 
676     unsigned int swapMask = (1U << nSwapBits) - 1;
677     unsigned int endMask = (1U << nEndBits) - 1;
678 
679     // Create a permutation vector
680     std::vector<int> permutation(segments.size());
681     fill_increasing(permutation.begin(), permutation.end(), 0);
682 
683     // best improvement
684     bool improved = false;
685     Coord distBest = olddist;
686     std::vector<int> permutationBest;
687     unsigned int iSwapBest;
688     unsigned int iEndBest;
689     int nTrials = 0;
690 
691     // Loop over the permutations
692     do {
693         // Loop over the swap bits
694         unsigned int iSwap = 0;
695         do {
696             // Loop over the end bits
697             unsigned int iEnd = 0;
698             do {
699                 // Length of all active connections
700                 Coord lengthTotal = 0;
701                 // Length of longest connection (active or inactive)
702                 Coord lengthLongest = longestOutsideLength;
703 
704                 // Close the loop with the end point of the last segment
705                 OrderingGroupPoint *prevend = segments[permutation.back()].GetEndPoint(iSwap, iEnd);
706                 for (int & it : permutation) {
707                     OrderingGroupPoint *thisbeg = segments[it].GetBeginPoint(iSwap, iEnd);
708                     Coord length = Geom::distance(thisbeg->point, prevend->point);
709                     lengthTotal += length;
710                     if (length > lengthLongest) {
711                         lengthLongest = length;
712                     }
713                     prevend = segments[it].GetEndPoint(iSwap, iEnd);
714                 }
715                 lengthTotal -= lengthLongest;
716 
717                 // If there is an improvement, remember the best selection
718                 if (lengthTotal + 1e-6 < distBest) {
719                     improved = true;
720                     distBest = lengthTotal;
721                     permutationBest = permutation;
722                     iSwapBest = iSwap;
723                     iEndBest = iEnd;
724 
725                     // Just debug printing
726                     OrderingGroupPoint *prevend = segments[permutation.back()].GetEndPoint(iSwap, iEnd);
727                     for (int & it : permutation) {
728                         // COMENTED TO SUPRESS WARNING UNUSED AUTOR TAKE IT UNCOMENTED
729                         //OrderingGroupPoint *thisbeg = segments[it].GetBeginPoint(iSwap, iEnd);
730                         DebugTrace2TSP(("IMP 0F=%d %d %.6lf", thisbeg->group->index, thisbeg->indexInGroup, Geom::distance(thisbeg->point, prevend->point)));
731                         DebugTrace2TSP(("IMP 0T=%d %d %.6lf", prevend->group->index, prevend->indexInGroup, Geom::distance(thisbeg->point, prevend->point)));
732                         prevend = segments[it].GetEndPoint(iSwap, iEnd);
733                     }
734                 }
735 
736                 nTrials++;
737 
738                 // bit 0 is always 0, because the first segment is kept fixed
739                 iEnd += 2;
740             } while (iEnd & endMask);
741             iSwap++;
742         } while (iSwap & swapMask);
743         // first segment is kept fixed
744     } while (std::next_permutation(permutation.begin() + 1, permutation.end()));
745 
746     if (improved) {
747         DebugTrace2TSP(("Improvement %lf->%lf in %d", olddist, distBest, nTrials));
748         // change the connections
749 
750         for (std::vector<OrderingGroupConnection *>::iterator it = connections.begin(); it != connections.end(); ++it) {
751             DebugTrace2TSP(("WAS 0F=%d %d %.6lf", (*it)->points[0]->group->index, (*it)->points[0]->indexInGroup, (*it)->Distance()));
752             DebugTrace2TSP(("WAS 0T=%d %d %.6lf", (*it)->points[1]->group->index, (*it)->points[1]->indexInGroup, (*it)->Distance()));
753         }
754         DebugTrace2TSP(("OLDDIST %.6lf delta %.6lf", olddist, olddist - (*longestConnect)->Distance()));
755         DebugTrace2TSP(("LONG =%d %d %.6lf", (*longestConnect)->points[0]->group->index, (*longestConnect)->points[0]->indexInGroup, (*longestConnect)->Distance()));
756         DebugTrace2TSP(("LONG =%d %d %.6lf", (*longestConnect)->points[1]->group->index, (*longestConnect)->points[1]->indexInGroup, (*longestConnect)->Distance()));
757 
758         int perm = permutationBest.back();
759 
760         for (std::vector<OrderingGroupConnection *>::iterator it = connections.begin(); it != connections.end(); ++it) {
761             (*it)->Connect(1, segments[ perm ].GetEndPoint(iSwapBest, iEndBest));
762             perm = permutationBest[ it - connections.begin() ];
763             (*it)->Connect(0, segments[ perm ].GetBeginPoint(iSwapBest, iEndBest));
764 
765         }
766 
767         for (std::vector<OrderingGroupConnection *>::iterator it = connections.begin(); it != connections.end(); ++it) {
768             DebugTrace2TSP(("IS 0F=%d %d %.6lf", (*it)->points[0]->group->index, (*it)->points[0]->indexInGroup, (*it)->Distance()));
769             DebugTrace2TSP(("IS 0T=%d %d %.6lf", (*it)->points[1]->group->index, (*it)->points[1]->indexInGroup, (*it)->Distance()));
770         }
771 
772         (*longestConnect) = longestOutside;
773         for (auto & connection : connections) {
774             if (connection->Distance() > (*longestConnect)->Distance()) {
775                 *longestConnect = connection;
776             }
777         }
778         DebugTrace2TSP(("LONG =%d %d %.6lf", (*longestConnect)->points[0]->group->index, (*longestConnect)->points[0]->indexInGroup, (*longestConnect)->Distance()));
779         DebugTrace2TSP(("LONG =%d %d %.6lf", (*longestConnect)->points[1]->group->index, (*longestConnect)->points[1]->indexInGroup, (*longestConnect)->Distance()));
780     }
781 
782     return improved;
783 }
784 
785 // Check if connections form a tour
AssertIsTour(std::vector<OrderingGroup * > & groups,std::vector<OrderingGroupConnection * > & connections,OrderingGroupConnection * longestConnection)786 void AssertIsTour(std::vector<OrderingGroup *> &groups, std::vector<OrderingGroupConnection *> &connections, OrderingGroupConnection *longestConnection)
787 {
788     for (auto & connection : connections) {
789         for (auto pnt : connection->points) {
790             assert(pnt->connection == connection);
791             assert(pnt->connection->points[pnt->indexInConnection] == pnt);
792             assert(pnt->group->endpoints[pnt->indexInGroup] == pnt);
793         }
794     }
795 
796     Coord length1 = 0;
797     Coord longest1 = 0;
798     OrderingGroupPoint *current = connections.front()->points[0];
799 
800     for (unsigned int n = 0; n < connections.size(); n++) {
801         DebugTrace2TSP(("Tour test 1 %p g=%d/%d c=%d/%d %p %p %.6lf %.3lf %.3lf %d %d %d", current, current->group->index, current->indexInGroup, current->connection->index, current->indexInConnection, current->connection->points[0], current->connection->points[1], current->connection->Distance(), current->point.x(), 297 - current->point.y(), current->begin, current->front, current->group->items.size()));
802         Coord length = current->connection->Distance();
803         length1 += length;
804         longest1 = std::max(length, longest1);
805         current = current->GetOtherEndConnection();
806 
807         DebugTrace2TSP(("Tour test 2 %p g=%d/%d c=%d/%d %p %p %.6lf %.3lf %.3lf %d %d %d", current, current->group->index, current->indexInGroup, current->connection->index, current->indexInConnection, current->connection->points[0], current->connection->points[1], current->connection->Distance(), current->point.x(), 297 - current->point.y(), current->begin, current->front, current->group->items.size()));
808         current = current->GetOtherEndGroup();
809     }
810     DebugTrace2TSP(("Tour test 3 %p g=%d/%d c=%d/%d %p %p", current, current->group->index, current->indexInGroup, current->connection->index, current->indexInConnection, current->connection->points[0], current->connection->points[1]));
811     assert(current == connections.front()->points[0]);
812 
813     // The other direction
814     Coord length2 = 0;
815     Coord longest2 = 0;
816     current = connections.front()->points[0];
817     for (unsigned int n = 0; n < connections.size(); n++) {
818         current = current->GetOtherEndGroup();
819         Coord length = current->connection->Distance();
820         length2 += length;
821         longest2 = std::max(length, longest2);
822         current = current->GetOtherEndConnection();
823     }
824     assert(current == connections.front()->points[0]);
825 
826     DebugTrace1TSP(("Tour length %.6lf(%.6lf) longest %.6lf(%.6lf) remaining %.6lf(%.6lf)", length1, length2, longest1, longest2, length1 - longest1, length2 - longest2));
827 }
828 
829 // Bring a tour into linear order after a modification
830 
831 /* I would like to avoid this.
832  * It is no problem to travel a tour with changing directions using the GetOtherEnd functions,
833  * but it is difficult to know the segments, that is which endpoint of a connection is connected to which by the unmodified pieces of the tour.
834  * In the end it is probably better to implement the Lin-Kernighan algorithm which avoids this problem by creating connected changes.  */
835 
LinearizeTour(std::vector<OrderingGroupConnection * > & connections)836 void LinearizeTour(std::vector<OrderingGroupConnection *> &connections)
837 {
838     OrderingGroupPoint *current = connections.front()->points[0];
839 
840     for (unsigned int iNew = 0; iNew < connections.size(); iNew++) {
841         // swap the connection at location n with the current connection
842         OrderingGroupConnection *connection = current->connection;
843         unsigned int iOld = connection->index;
844         assert(connections[iOld] == connection);
845 
846         connections[iOld] = connections[iNew];
847         connections[iNew] = connection;
848         connections[iOld]->index = iOld;
849         connections[iNew]->index = iNew;
850 
851         // swap the points of a connection
852         assert(current == connection->points[0] || current == connection->points[1]);
853         if (current != connection->points[0]) {
854             connection->points[1] = connection->points[0];
855             connection->points[0] = current;
856             connection->points[1]->indexInConnection = 1;
857             connection->points[0]->indexInConnection = 0;
858         }
859 
860         current = current->GetOtherEndConnection();
861         current = current->GetOtherEndGroup();
862     }
863 }
864 
865 // Use some Traveling Salesman Problem (TSP) like heuristics to bring several groups into a
866 // order with as short as possible interconnection paths
867 
OrderGroups(std::vector<OrderingGroup * > * groups,const int nDims)868 void OrderGroups(std::vector<OrderingGroup *> *groups, const int nDims)
869 {
870     // There is no point in ordering just one group
871     if (groups->size() <= 1) {
872         return;
873     }
874 
875     // Initialize the endpoints for all groups
876     for (auto & group : *groups) {
877         group->SetEndpoints();
878     }
879 
880     // Find the neighboring points for all end points of all groups and sort by distance
881     for (std::vector<OrderingGroup *>::iterator itThis = groups->begin(); itThis != groups->end(); ++itThis) {
882         for (int i = 0; i < (*itThis)->nEndPoints; i++) {
883             // This can be up to 2x too large, but still better than incrementing the size
884             (*itThis)->endpoints[i]->nearest.reserve(4 * groups->size());
885         }
886 
887         for (std::vector<OrderingGroup *>::iterator itNghb = groups->begin(); itNghb != groups->end(); ++itNghb) {
888             if (itThis != itNghb) {
889                 (*itThis)->AddNeighbors(*itNghb);
890             }
891         }
892 
893         for (int i = 0; i < (*itThis)->nEndPoints; i++) {
894             std::sort((*itThis)->endpoints[i]->nearest.begin(), (*itThis)->endpoints[i]->nearest.end(), OrderingGroupNeighbor::Compare);
895         }
896     }
897 
898     // =========== Step 1: Create a simple nearest neighbor chain ===========
899 
900     // Vector of connection points
901     std::vector<OrderingGroupConnection *> connections;
902     connections.reserve(groups->size());
903     // Total Jump Distance
904     Coord total = 0.0;
905 
906     // Start with the first group and connect always with nearest unused point
907     OrderingGroupPoint *crnt = groups->front()->endpoints[0];
908 
909     // The longest connection is ignored (we don't want cycles)
910     OrderingGroupConnection *longestConnect = nullptr;
911 
912     for (unsigned int nConnected = 0; nConnected < groups->size(); nConnected++) {
913         // Mark both end points of the current segment as used
914         crnt->UsePoint();
915         crnt = crnt->GetOtherEndGroup();
916         crnt->UsePoint();
917 
918         // if this is the last segment, Mark start point of first segment as unused,
919         // so that the end can connect to it
920         if (nConnected == groups->size() - 1) {
921             groups->front()->endpoints[0]->UnusePoint();
922         }
923 
924         // connect to next segment
925         OrderingGroupNeighbor *nghb = crnt->FindNearestUnused();
926         connections.push_back(new OrderingGroupConnection(crnt, nghb->point, connections.size()));
927         total += nghb->distance;
928         crnt = nghb->point;
929 
930         if (!longestConnect || nghb->distance > longestConnect->Distance()) {
931             longestConnect = connections.back();
932         }
933     }
934 
935     DebugTrace1TSP(("Total jump distance %.3lf (closed)", total));
936     DebugTrace1TSP(("Total jump distance %.3lf (open)", total - longestConnect->Distance()));
937 
938     AssertIsTour(*groups, connections, longestConnect);
939 
940     // =========== Step 2: Choose nDims segments to clear and reconnect ===========
941 
942     bool improvement;
943     int nRuns = 0;
944     int nTrials = 0;
945     int nImprovements = 0;
946 
947     do {
948         improvement = false;
949         nRuns ++;
950         std::vector< std::vector<OrderingGroupConnection *>::iterator > iterators;
951 
952         for (
953             triangleit_begin(iterators, connections.begin(), connections.end(), nDims);
954             triangleit_test(iterators, connections.end());
955             triangleit_incr(iterators, connections.end())
956         ) {
957             nTrials ++;
958 
959             Coord dist = 0;
960 
961             std::vector<OrderingSegment> segments(iterators.size());
962             std::vector<OrderingGroupConnection *> changedconnections;
963             changedconnections.reserve(3);
964             OrderingGroupConnection *prev = *iterators.back();
965 
966 
967             for (size_t i = 0; i < iterators.size(); i++) {
968                 dist += (*iterators[i])->Distance();
969                 segments[i].AddPoint(prev->points[1]);
970                 segments[i].AddPoint((*iterators[i])->points[0]);
971                 prev = *iterators[i];
972                 changedconnections.push_back(*iterators[i]);
973             }
974 
975             if (FindShortestReconnect(segments, changedconnections, connections, &longestConnect, &total, dist)) {
976                 nImprovements ++;
977 
978                 AssertIsTour(*groups, connections, longestConnect);
979                 LinearizeTour(connections);
980                 AssertIsTour(*groups, connections, longestConnect);
981                 improvement = true;
982             }
983         }
984     } while (improvement && nRuns < 10);
985 
986     DebugTrace1TSP(("Finished after %d rounds, %d trials, %d improvements", nRuns, nTrials, nImprovements));
987 
988     // =========== Step N: Create vector of groups from vector of connection points ===========
989 
990     std::vector<OrderingGroup *> result;
991     result.reserve(groups->size());
992 
993     // Go through the groups starting with the longest connection (which is this way left out)
994     {
995         OrderingGroupPoint *current = longestConnect->points[1];
996 
997         for (unsigned int n = 0; n < connections.size(); n++) {
998             result.push_back(current->group);
999             current->SetRevInGroup();
1000             current = current->GetOtherEndGroup();
1001             current = current->GetOtherEndConnection();
1002         }
1003     }
1004 
1005     assert(result.size() == groups->size());
1006     assert_unique(result);
1007 
1008     delete_and_clear(connections);
1009 
1010     *groups = result;
1011 }
1012 
1013 // Global optimization of path length
1014 
OrderingAdvanced(std::vector<OrderingInfo> & infos,int nDims)1015 void OrderingAdvanced(std::vector<OrderingInfo> &infos, int nDims)
1016 {
1017     if (infos.size() < 3) {
1018         return;
1019     }
1020 
1021     // Create extended ordering info vector and copy data from normal ordering info
1022     std::vector<OrderingInfoEx *> infoex;
1023     infoex.reserve(infos.size());
1024 
1025     for (std::vector<OrderingInfo>::const_iterator it = infos.begin(); it != infos.end();) {
1026         // Note: This assumes that the index in the OrderingInfo matches the vector index!
1027         infoex.push_back(new OrderingInfoEx(*it, infoex.size()));
1028         ++it;
1029         while (it != infos.end() && it->begOrig == infoex.back()->end.point) {
1030             infoex.back()->end.point = it->endOrig;
1031             infoex.back()->origIndices.push_back(it->index);
1032             ++it;
1033         }
1034     }
1035 
1036     // Find closest 2 points for each point and enforce that 2nd nearest is not further away than 1.8xthe nearest
1037     // If this is not the case, clear nearest and 2nd nearest point
1038     for (std::vector<OrderingInfoEx *>::iterator it = infoex.begin(); it != infoex.end(); ++it) {
1039         (*it)->beg.FindNearest2(infoex);
1040         (*it)->end.FindNearest2(infoex);
1041     }
1042 
1043     DebugTraceGrouping(
1044         DebugTrace2(("STEP1"));
1045     for (std::vector<OrderingInfoEx *>::iterator it = infoex.begin(); it != infoex.end(); ++it) {
1046     (*it)->beg.Dump();
1047         (*it)->end.Dump();
1048     }
1049     )
1050 
1051     // Make sure the nearest points are mutual
1052     for (auto & it : infoex) {
1053         it->beg.EnforceMutual();
1054         it->end.EnforceMutual();
1055     }
1056 
1057     DebugTraceGrouping(
1058         DebugTrace2(("STEP2"));
1059     for (std::vector<OrderingInfoEx *>::iterator it = infoex.begin(); it != infoex.end(); ++it) {
1060     (*it)->beg.Dump();
1061         (*it)->end.Dump();
1062     }
1063     )
1064 
1065     // Make sure the nearest points for begin and end lead to the same sub-path (same index)
1066     for (auto & it : infoex) {
1067         it->beg.EnforceSymmetric(it->end);
1068         it->end.EnforceSymmetric(it->beg);
1069     }
1070 
1071     DebugTraceGrouping(
1072         DebugTrace2(("STEP3"));
1073     for (std::vector<OrderingInfoEx *>::iterator it = infoex.begin(); it != infoex.end(); ++it) {
1074     (*it)->beg.Dump();
1075         (*it)->end.Dump();
1076     }
1077     )
1078 
1079     // The remaining nearest neighbors should be 100% non ambiguous, so group them
1080     std::vector<OrderingGroup *> groups;
1081     for (std::vector<OrderingInfoEx *>::iterator it = infoex.begin(); it != infoex.end(); ++it) {
1082         (*it)->MakeGroup(infoex, &groups);
1083     }
1084 
1085     // Create single groups for ungrouped lines
1086     std::vector<OrderingInfo> result;
1087     result.reserve(infos.size());
1088     int nUngrouped = 0;
1089     for (auto & it : infoex) {
1090         if (!it->grouped) {
1091             groups.push_back(new OrderingGroup(groups.size()));
1092             groups.back()->items.push_back(it);
1093             nUngrouped++;
1094         }
1095     }
1096 
1097     DebugTraceGrouping(
1098         DebugTrace2(("Ungrouped lines = %d", nUngrouped));
1099         DebugTrace2(("%d Groups found", groups.size()));
1100     for (std::vector<OrderingGroup *>::iterator it = groups.begin(); it != groups.end(); ++it) {
1101     DebugTrace2(("Group size %d", (*it)->items.size()));
1102     }
1103     )
1104 
1105     // Order groups, so that the connection path gets shortest
1106     OrderGroups(&groups, nDims);
1107 
1108     // Copy grouped lines to output
1109     for (auto & group : groups) {
1110         for (unsigned int iItem = 0; iItem < group->items.size(); iItem++) {
1111             unsigned int iItemRev = group->revItemList ? group->items.size() - 1 - iItem : iItem;
1112             OrderingInfoEx *item = group->items[iItemRev];
1113 
1114             // If revItems is false, even items shall have reverse=false
1115             // In this case ( ( iItem & 1 ) == 0 )== true, revItems=false, (true==false) == false
1116             bool reverse = ((iItem & 1) == 0) == group->revItems;
1117             if (!reverse) {
1118                 for (int & origIndice : item->origIndices) {
1119                     result.push_back(infos[origIndice]);
1120                     result.back().reverse = false;
1121                 }
1122             } else {
1123                 for (std::vector<int>::reverse_iterator itOrig = item->origIndices.rbegin(); itOrig != item->origIndices.rend(); ++itOrig) {
1124                     result.push_back(infos[*itOrig]);
1125                     result.back().reverse = true;
1126                 }
1127             }
1128         }
1129         result.back().connect = true;
1130     }
1131 
1132 
1133     delete_and_clear(groups);
1134     delete_and_clear(infoex);
1135 
1136     infos = result;
1137 }
1138 
1139 } // namespace LPEEmbroderyStitchOrdering
1140 } // namespace LivePathEffect
1141 } // namespace Inkscape
1142