summaryrefslogtreecommitdiffstats
path: root/src/live_effects/lpe-embrodery-stitch-ordering.h
blob: 9899f667825b2f17daf4365437624f3cf579fc17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Sub-path Ordering functions for embroidery stitch LPE
 *
 * Copyright (C) 2016 Michael Soegtrop
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifndef INKSCAPE_LPE_EMBRODERY_STITCH_ORDERING_H
#define INKSCAPE_LPE_EMBRODERY_STITCH_ORDERING_H

#include "live_effects/effect.h"

namespace Inkscape {
namespace LivePathEffect {
namespace LPEEmbroderyStitchOrdering {

// Structure keeping information on the ordering and reversing of sub paths
// Used for simple ordering functions like zig-zag

struct OrderingInfo {
    int   index;
    bool  reverse;
    bool  used;
    bool  connect;
    Geom::Point begOrig; // begin point in original orientation
    Geom::Point endOrig; // end point in original orientation

    Geom::Point GetBegOrig() const
    {
        return begOrig;
    }
    Geom::Point GetEndOrig() const
    {
        return endOrig;
    }
    Geom::Point GetBegRev() const
    {
        return reverse ? endOrig : begOrig;
    }
    Geom::Point GetEndRev() const
    {
        return reverse ? begOrig : endOrig;
    }
};

// Structure for a path end-point in OrderingInfoEx.
// This keeps information about the two nearest neighbor points.

struct OrderingInfoEx;

struct OrderingPoint {
    OrderingPoint(const Geom::Point &pointIn, OrderingInfoEx *infoexIn, bool beginIn) :
        point(pointIn),
        infoex(infoexIn),
        begin(beginIn)
    {
        nearest[0] = nearest[1] = nullptr;
    }

    // Check if both nearest values are valid
    bool IsNearestValid() const
    {
        return nearest[0] && nearest[1];
    }
    // Check if at least one nearest values are valid
    bool HasNearest() const
    {
        return nearest[0] || nearest[1];
    }
    // Find 2 nearest points to given point
    void FindNearest2(const std::vector<OrderingInfoEx *> &infos);
    // Check if "this" is among the nearest of its nearest
    void EnforceMutual();
    // Check if the subpath indices of this and other are the same, otherwise zero both nearest
    void EnforceSymmetric(const OrderingPoint &other);
    // Dump point information
    void Dump();

    Geom::Point point;
    OrderingInfoEx *infoex;
    bool  begin;
    const OrderingPoint *nearest[2];
};

// Structure keeping information on the ordering and reversing of sub paths
// Used for advanced ordering functions with block creation and Traveling Salesman Problem Optimization
// A OrderingInfoEx may contain several original sub-paths in case sub-paths are directly connected.
// Directly connected sub-paths happen e.g. after a slice boolean operation.

struct OrderingGroup;

struct OrderingInfoEx {
    OrderingInfoEx(const OrderingInfo &infoIn, int idxIn) :
        beg(infoIn.begOrig, this, true),
        end(infoIn.endOrig, this, false),
        idx(idxIn),
        grouped(false)
    {
        origIndices.push_back(infoIn.index);
    }

    // If this element can be grouped (has neighbours) but is not yet grouped, create a new group
    void MakeGroup(std::vector<OrderingInfoEx *> &infos, std::vector<OrderingGroup *> *groups);
    // Add this and all connected elements to the group
    void AddToGroup(std::vector<OrderingInfoEx *> &infos, OrderingGroup *group);

    int idx;
    bool grouped;      // true if this element has been grouped
    OrderingPoint beg; // begin point in original orientation
    OrderingPoint end; // end point in original orientation
    std::vector<int> origIndices; // Indices of the original OrderInfos (more than 1 if directly connected
};

// Neighbor information for OrderingGroupPoint - a distance and a OrderingGroupPoint

struct OrderingGroupPoint;

struct OrderingGroupNeighbor {
    OrderingGroupNeighbor(OrderingGroupPoint *me, OrderingGroupPoint *other);

    // Distance from owner of this neighbor info
    Geom::Coord distance;
    // Neighbor point (which in turn contains a pointer to the neighbor group
    OrderingGroupPoint *point;

    // Comparison function for sorting by distance
    static bool Compare(const OrderingGroupNeighbor &a, const OrderingGroupNeighbor &b);
};

// An end point in an OrderingGroup, with nearest neighbor information

struct OrderingGroupConnection;

struct OrderingGroupPoint {
    OrderingGroupPoint(const Geom::Point &pointIn, OrderingGroup *groupIn, int indexIn, bool beginIn, bool frontIn) :
        point(pointIn),
        group(groupIn),
        indexInGroup(indexIn),
        connection(nullptr),
        indexInConnection(0),
        begin(beginIn),
        front(frontIn),
        used(false)
    {
    }

    // Find the nearest unused neighbor point
    OrderingGroupNeighbor *FindNearestUnused();
    // Return the other end in the group of the point
    OrderingGroupPoint *GetOtherEndGroup();
    // Return the alternate point (if one exists), 0 otherwise
    OrderingGroupPoint *GetAltPointGroup();
    // Sets the rev flags in the group assuming that the group starts with this point
    void SetRevInGroup();
    // Mark an end point as used and also mark the two other alternating points as used
    // Returns the used point
    void UsePoint();
    // Mark an end point as unused and possibly also mark the two other alternating points as unused
    // Returns the used point
    void UnusePoint();
    // Return the other end in the connection
    OrderingGroupPoint *GetOtherEndConnection();

    // The coordinates of the point
    Geom::Point point;
    // The group to which the point belongs
    OrderingGroup *group;
    // The end-point index within the group
    int indexInGroup;
    // The connection, which connects this point
    OrderingGroupConnection *connection;
    // The end point index in the connection
    int indexInConnection;
    // True if this is a begin point (rather than an end point)
    bool begin;
    // True if this is a front point (rather than a back point)
    bool front;
    // True if the point is used/connected to another point
    bool used;
    // The nearest neighbors, to which this group end point may connect
    std::vector<OrderingGroupNeighbor> nearest;
};

// A connection between two points/groups
struct OrderingGroupConnection {
    OrderingGroupConnection(OrderingGroupPoint *fromIn, OrderingGroupPoint *toIn, int indexIn) :
        index(indexIn)
    {
        assert(fromIn->connection == 0);
        assert(toIn->connection == 0);
        points[0] = nullptr;
        points[1] = nullptr;
        Connect(0, fromIn);
        Connect(1, toIn);
    }

    // Connect one of the connection endpoints to the given point
    void Connect(int index, OrderingGroupPoint *point)
    {
        assert(point);
        points[index] = point;
        point->connection = this;
        point->indexInConnection = index;
    }

    // Get length of connection
    Geom::Coord Distance()
    {
        return Geom::distance(points[0]->point, points[1]->point);
    }

    OrderingGroupPoint *points[2];
    // index of connection in the connections vector (just for debugging)
    int index;
};

// A group of OrderingInfoEx, which build a block in path interconnect length optimization.
// A block can have two sets of endpoints.
// If a block has 2 sets of endpoints, one can swap between the two sets.

struct OrderingGroup {
    OrderingGroup(int indexIn) :
        nEndPoints(0),
        revItemList(false),
        revItems(false),
        index(indexIn)
    {
        for (auto & endpoint : endpoints) {
            endpoint = nullptr;
        }
    }

    ~OrderingGroup()
    {
        for (int i = 0; i < nEndPoints; i++) {
            delete endpoints[i];
        }
    }

    // Set the endpoints of a group from the items
    void SetEndpoints();
    // Add all points from another group as neighbors
    void AddNeighbors(OrderingGroup *nghb);
    // Mark an end point as used and also mark the two other alternating points as used
    // Returns the used point
    OrderingGroupPoint *UsePoint(int index);
    // Mark an end point as unused and possibly also mark the two other alternating points as unused
    // Returns the used point
    void UnusePoint(int index);

    // Items on the group
    std::vector<OrderingInfoEx *> items;
    // End points of the group
    OrderingGroupPoint *endpoints[4];
    // Number of endpoints used (either 2 or 4)
    int nEndPoints;
    // Index of the group (just for debugging purposes)
    int index;
    // If true, the items in the group shall be output from back to front.
    bool revItemList;
    // If false, the individual items are output alternatingly normal-reversed
    // If true, the individual items are output alternatingly reversed-normal
    bool revItems;
};

// A segment is either a OrderingGroup or a series of groups and connections.
// Usually a segment has just 2 end points.
// If a segment is just one ordering group, it has the same number of end points as the ordering group
// A main difference between a segment and a group is that the segment does not own the end points.

struct OrderingSegment {
    OrderingSegment() :
        nEndPoints(0),
        endbit(0),
        swapbit(0)
    {}

    // Add an end point
    void AddPoint(OrderingGroupPoint *point);
    // Get begin point (taking swap and end bit into account
    OrderingGroupPoint *GetBeginPoint(unsigned int iSwap, unsigned int iEnd);
    // Get end point (taking swap and end bit into account
    OrderingGroupPoint *GetEndPoint(unsigned int iSwap, unsigned int iEnd);

    // End points of the group
    OrderingGroupPoint *endpoints[4];
    // Number of endpoints used (either 2 or 4)
    int nEndPoints;
    // bit index in the end counter
    int endbit;
    // bit index in the swap counter
    int swapbit;
};


// Sub-path reordering: do nothing - keep original order
void OrderingOriginal(std::vector<OrderingInfo> &infos);

// Sub-path reordering: reverse every other sub path
void OrderingZigZag(std::vector<OrderingInfo> &infos, bool revfirst);

// Sub-path reordering: continue with the neartest start or end point of yet unused sub paths
void OrderingClosest(std::vector<OrderingInfo> &infos, bool revfirst);

// Global optimization of path length
void OrderingAdvanced(std::vector<OrderingInfo> &infos, int nDims);

} //LPEEmbroderyStitchOrdering
} //namespace LivePathEffect
} //namespace Inkscape

#endif