// 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 &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 &infos, std::vector *groups); // Add this and all connected elements to the group void AddToGroup(std::vector &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 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 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 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 &infos); // Sub-path reordering: reverse every other sub path void OrderingZigZag(std::vector &infos, bool revfirst); // Sub-path reordering: continue with the neartest start or end point of yet unused sub paths void OrderingClosest(std::vector &infos, bool revfirst); // Global optimization of path length void OrderingAdvanced(std::vector &infos, int nDims); } //LPEEmbroderyStitchOrdering } //namespace LivePathEffect } //namespace Inkscape #endif