1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Sub-path Ordering functions for embroidery stitch LPE
4  *
5  * Copyright (C) 2016 Michael Soegtrop
6  *
7  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
8  */
9 
10 #ifndef INKSCAPE_LPE_EMBRODERY_STITCH_ORDERING_H
11 #define INKSCAPE_LPE_EMBRODERY_STITCH_ORDERING_H
12 
13 #include "live_effects/effect.h"
14 
15 namespace Inkscape {
16 namespace LivePathEffect {
17 namespace LPEEmbroderyStitchOrdering {
18 
19 // Structure keeping information on the ordering and reversing of sub paths
20 // Used for simple ordering functions like zig-zag
21 
22 struct OrderingInfo {
23     int   index;
24     bool  reverse;
25     bool  used;
26     bool  connect;
27     Geom::Point begOrig; // begin point in original orientation
28     Geom::Point endOrig; // end point in original orientation
29 
GetBegOrigOrderingInfo30     Geom::Point GetBegOrig() const
31     {
32         return begOrig;
33     }
GetEndOrigOrderingInfo34     Geom::Point GetEndOrig() const
35     {
36         return endOrig;
37     }
GetBegRevOrderingInfo38     Geom::Point GetBegRev() const
39     {
40         return reverse ? endOrig : begOrig;
41     }
GetEndRevOrderingInfo42     Geom::Point GetEndRev() const
43     {
44         return reverse ? begOrig : endOrig;
45     }
46 };
47 
48 // Structure for a path end-point in OrderingInfoEx.
49 // This keeps information about the two nearest neighbor points.
50 
51 struct OrderingInfoEx;
52 
53 struct OrderingPoint {
OrderingPointOrderingPoint54     OrderingPoint(const Geom::Point &pointIn, OrderingInfoEx *infoexIn, bool beginIn) :
55         point(pointIn),
56         infoex(infoexIn),
57         begin(beginIn)
58     {
59         nearest[0] = nearest[1] = nullptr;
60     }
61 
62     // Check if both nearest values are valid
IsNearestValidOrderingPoint63     bool IsNearestValid() const
64     {
65         return nearest[0] && nearest[1];
66     }
67     // Check if at least one nearest values are valid
HasNearestOrderingPoint68     bool HasNearest() const
69     {
70         return nearest[0] || nearest[1];
71     }
72     // Find 2 nearest points to given point
73     void FindNearest2(const std::vector<OrderingInfoEx *> &infos);
74     // Check if "this" is among the nearest of its nearest
75     void EnforceMutual();
76     // Check if the subpath indices of this and other are the same, otherwise zero both nearest
77     void EnforceSymmetric(const OrderingPoint &other);
78     // Dump point information
79     void Dump();
80 
81     Geom::Point point;
82     OrderingInfoEx *infoex;
83     bool  begin;
84     const OrderingPoint *nearest[2];
85 };
86 
87 // Structure keeping information on the ordering and reversing of sub paths
88 // Used for advanced ordering functions with block creation and Traveling Salesman Problem Optimization
89 // A OrderingInfoEx may contain several original sub-paths in case sub-paths are directly connected.
90 // Directly connected sub-paths happen e.g. after a slice boolean operation.
91 
92 struct OrderingGroup;
93 
94 struct OrderingInfoEx {
OrderingInfoExOrderingInfoEx95     OrderingInfoEx(const OrderingInfo &infoIn, int idxIn) :
96         beg(infoIn.begOrig, this, true),
97         end(infoIn.endOrig, this, false),
98         idx(idxIn),
99         grouped(false)
100     {
101         origIndices.push_back(infoIn.index);
102     }
103 
104     // If this element can be grouped (has neighbours) but is not yet grouped, create a new group
105     void MakeGroup(std::vector<OrderingInfoEx *> &infos, std::vector<OrderingGroup *> *groups);
106     // Add this and all connected elements to the group
107     void AddToGroup(std::vector<OrderingInfoEx *> &infos, OrderingGroup *group);
108 
109     int idx;
110     bool grouped;      // true if this element has been grouped
111     OrderingPoint beg; // begin point in original orientation
112     OrderingPoint end; // end point in original orientation
113     std::vector<int> origIndices; // Indices of the original OrderInfos (more than 1 if directly connected
114 };
115 
116 // Neighbor information for OrderingGroupPoint - a distance and a OrderingGroupPoint
117 
118 struct OrderingGroupPoint;
119 
120 struct OrderingGroupNeighbor {
121     OrderingGroupNeighbor(OrderingGroupPoint *me, OrderingGroupPoint *other);
122 
123     // Distance from owner of this neighbor info
124     Geom::Coord distance;
125     // Neighbor point (which in turn contains a pointer to the neighbor group
126     OrderingGroupPoint *point;
127 
128     // Comparison function for sorting by distance
129     static bool Compare(const OrderingGroupNeighbor &a, const OrderingGroupNeighbor &b);
130 };
131 
132 // An end point in an OrderingGroup, with nearest neighbor information
133 
134 struct OrderingGroupConnection;
135 
136 struct OrderingGroupPoint {
OrderingGroupPointOrderingGroupPoint137     OrderingGroupPoint(const Geom::Point &pointIn, OrderingGroup *groupIn, int indexIn, bool beginIn, bool frontIn) :
138         point(pointIn),
139         group(groupIn),
140         indexInGroup(indexIn),
141         connection(nullptr),
142         indexInConnection(0),
143         begin(beginIn),
144         front(frontIn),
145         used(false)
146     {
147     }
148 
149     // Find the nearest unused neighbor point
150     OrderingGroupNeighbor *FindNearestUnused();
151     // Return the other end in the group of the point
152     OrderingGroupPoint *GetOtherEndGroup();
153     // Return the alternate point (if one exists), 0 otherwise
154     OrderingGroupPoint *GetAltPointGroup();
155     // Sets the rev flags in the group assuming that the group starts with this point
156     void SetRevInGroup();
157     // Mark an end point as used and also mark the two other alternating points as used
158     // Returns the used point
159     void UsePoint();
160     // Mark an end point as unused and possibly also mark the two other alternating points as unused
161     // Returns the used point
162     void UnusePoint();
163     // Return the other end in the connection
164     OrderingGroupPoint *GetOtherEndConnection();
165 
166     // The coordinates of the point
167     Geom::Point point;
168     // The group to which the point belongs
169     OrderingGroup *group;
170     // The end-point index within the group
171     int indexInGroup;
172     // The connection, which connects this point
173     OrderingGroupConnection *connection;
174     // The end point index in the connection
175     int indexInConnection;
176     // True if this is a begin point (rather than an end point)
177     bool begin;
178     // True if this is a front point (rather than a back point)
179     bool front;
180     // True if the point is used/connected to another point
181     bool used;
182     // The nearest neighbors, to which this group end point may connect
183     std::vector<OrderingGroupNeighbor> nearest;
184 };
185 
186 // A connection between two points/groups
187 struct OrderingGroupConnection {
OrderingGroupConnectionOrderingGroupConnection188     OrderingGroupConnection(OrderingGroupPoint *fromIn, OrderingGroupPoint *toIn, int indexIn) :
189         index(indexIn)
190     {
191         assert(fromIn->connection == 0);
192         assert(toIn->connection == 0);
193         points[0] = nullptr;
194         points[1] = nullptr;
195         Connect(0, fromIn);
196         Connect(1, toIn);
197     }
198 
199     // Connect one of the connection endpoints to the given point
ConnectOrderingGroupConnection200     void Connect(int index, OrderingGroupPoint *point)
201     {
202         assert(point);
203         points[index] = point;
204         point->connection = this;
205         point->indexInConnection = index;
206     }
207 
208     // Get length of connection
DistanceOrderingGroupConnection209     Geom::Coord Distance()
210     {
211         return Geom::distance(points[0]->point, points[1]->point);
212     }
213 
214     OrderingGroupPoint *points[2];
215     // index of connection in the connections vector (just for debugging)
216     int index;
217 };
218 
219 // A group of OrderingInfoEx, which build a block in path interconnect length optimization.
220 // A block can have two sets of endpoints.
221 // If a block has 2 sets of endpoints, one can swap between the two sets.
222 
223 struct OrderingGroup {
OrderingGroupOrderingGroup224     OrderingGroup(int indexIn) :
225         nEndPoints(0),
226         revItemList(false),
227         revItems(false),
228         index(indexIn)
229     {
230         for (auto & endpoint : endpoints) {
231             endpoint = nullptr;
232         }
233     }
234 
~OrderingGroupOrderingGroup235     ~OrderingGroup()
236     {
237         for (int i = 0; i < nEndPoints; i++) {
238             delete endpoints[i];
239         }
240     }
241 
242     // Set the endpoints of a group from the items
243     void SetEndpoints();
244     // Add all points from another group as neighbors
245     void AddNeighbors(OrderingGroup *nghb);
246     // Mark an end point as used and also mark the two other alternating points as used
247     // Returns the used point
248     OrderingGroupPoint *UsePoint(int index);
249     // Mark an end point as unused and possibly also mark the two other alternating points as unused
250     // Returns the used point
251     void UnusePoint(int index);
252 
253     // Items on the group
254     std::vector<OrderingInfoEx *> items;
255     // End points of the group
256     OrderingGroupPoint *endpoints[4];
257     // Number of endpoints used (either 2 or 4)
258     int nEndPoints;
259     // Index of the group (just for debugging purposes)
260     int index;
261     // If true, the items in the group shall be output from back to front.
262     bool revItemList;
263     // If false, the individual items are output alternatingly normal-reversed
264     // If true, the individual items are output alternatingly reversed-normal
265     bool revItems;
266 };
267 
268 // A segment is either a OrderingGroup or a series of groups and connections.
269 // Usually a segment has just 2 end points.
270 // If a segment is just one ordering group, it has the same number of end points as the ordering group
271 // A main difference between a segment and a group is that the segment does not own the end points.
272 
273 struct OrderingSegment {
OrderingSegmentOrderingSegment274     OrderingSegment() :
275         nEndPoints(0),
276         endbit(0),
277         swapbit(0)
278     {}
279 
280     // Add an end point
281     void AddPoint(OrderingGroupPoint *point);
282     // Get begin point (taking swap and end bit into account
283     OrderingGroupPoint *GetBeginPoint(unsigned int iSwap, unsigned int iEnd);
284     // Get end point (taking swap and end bit into account
285     OrderingGroupPoint *GetEndPoint(unsigned int iSwap, unsigned int iEnd);
286 
287     // End points of the group
288     OrderingGroupPoint *endpoints[4];
289     // Number of endpoints used (either 2 or 4)
290     int nEndPoints;
291     // bit index in the end counter
292     int endbit;
293     // bit index in the swap counter
294     int swapbit;
295 };
296 
297 
298 // Sub-path reordering: do nothing - keep original order
299 void OrderingOriginal(std::vector<OrderingInfo> &infos);
300 
301 // Sub-path reordering: reverse every other sub path
302 void OrderingZigZag(std::vector<OrderingInfo> &infos, bool revfirst);
303 
304 // Sub-path reordering: continue with the neartest start or end point of yet unused sub paths
305 void OrderingClosest(std::vector<OrderingInfo> &infos, bool revfirst);
306 
307 // Global optimization of path length
308 void OrderingAdvanced(std::vector<OrderingInfo> &infos, int nDims);
309 
310 } //LPEEmbroderyStitchOrdering
311 } //namespace LivePathEffect
312 } //namespace Inkscape
313 
314 #endif
315