1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 #include "SmartKeyEdgeSet.h"
18
19 #include <QStack>
20
21 namespace VectorAnimationComplex
22 {
23
24 /////////////////////////////////////////////////////////////////////
25 // Single Connected Component computation //
26
SmartConnectedKeyEdgeSet(const KeyEdgeSet & edgeSet)27 SmartConnectedKeyEdgeSet::SmartConnectedKeyEdgeSet(const KeyEdgeSet & edgeSet):
28 edgeSet_(edgeSet),
29 path_(edgeSet),
30 loop_(edgeSet),
31 hole_(edgeSet)
32 {
33 }
34
type() const35 SmartConnectedKeyEdgeSet::EdgeSetType SmartConnectedKeyEdgeSet::type() const
36 {
37 KeyEdge * singleEdge = edge();
38
39 if(edgeSet_.size() == 0)
40 {
41 return EMPTY;
42 }
43 else if(singleEdge)
44 {
45 if(singleEdge->isClosed())
46 {
47 return CLOSED_EDGE;
48 }
49 else if(singleEdge->isSplittedLoop())
50 {
51 return OPEN_EDGE_LOOP;
52 }
53 else
54 {
55 return OPEN_EDGE_PATH;
56 }
57 }
58 else if(path_.isValid())
59 {
60 return SIMPLE_PATH;
61 }
62 else if(loop_.isValid())
63 {
64 return SIMPLE_LOOP;
65 }
66 else if(hole_.isValid())
67 {
68 return PATH_LOOP_DECOMPOSITION;
69 }
70 else
71 {
72 return GENERAL;
73 }
74 }
75
edge() const76 KeyEdge * SmartConnectedKeyEdgeSet::edge() const
77 {
78 if(edgeSet_.size() == 1)
79 {
80 return *edgeSet_.begin();
81 }
82 else
83 {
84 return 0;
85 }
86 }
87
path() const88 ProperPath SmartConnectedKeyEdgeSet::path() const
89 {
90 return path_;
91 }
92
loop() const93 ProperCycle SmartConnectedKeyEdgeSet::loop() const
94 {
95 return loop_;
96 }
97
hole() const98 CycleHelper SmartConnectedKeyEdgeSet::hole() const
99 {
100 return hole_;
101 }
102
edgeSet() const103 KeyEdgeSet SmartConnectedKeyEdgeSet::edgeSet() const
104 {
105 return edgeSet_;
106 }
107
108
109 /////////////////////////////////////////////////////////////////////
110 // Multiple Connected Component computation //
111
112
SmartKeyEdgeSet(const KeyEdgeSet & edgeSetConst)113 SmartKeyEdgeSet::SmartKeyEdgeSet(const KeyEdgeSet & edgeSetConst) :
114 edgeSet_(edgeSetConst)
115 {
116 // ----- Compute connected components -----
117
118 // This variable store the edges that have not yet been assigned a connected component
119 KeyEdgeSet remainingEdges = edgeSet_;
120
121
122 // ===== First, each closed edge is its own connected component =====
123
124 KeyEdgeSet remainingEdgesCopy = remainingEdges;
125 foreach(KeyEdge * iedge, remainingEdgesCopy)
126 {
127 if(iedge->isClosed())
128 {
129 remainingEdges.remove(iedge);
130 KeyEdgeSet connectedEdges;
131 connectedEdges << iedge;
132 connectedComponents_ << SmartConnectedKeyEdgeSet(connectedEdges);
133 }
134 }
135
136
137 // ===== Define the 1-subcomplex made of the remaining vertices and open edges =====
138
139 // Data structure for cells of the original complex
140 typedef KeyVertex Vertex;
141 typedef KeyEdge Edge;
142
143 // Data structure for cells of the subcomplex
144 struct SubVertex;
145 struct SubEdge
146 {
147 SubEdge(Edge * edge) : edge(edge), leftSubVertex(0), rightSubVertex(0), marked(false) {}
148
149 // Edge from SubEdge
150 Edge * edge;
151
152 // Boundary SubVertices
153 SubVertex * leftSubVertex;
154 SubVertex * rightSubVertex;
155
156 // Has the edge already been visited
157 bool marked;
158 };
159 struct SubVertex
160 {
161 SubVertex(Vertex * vertex) : vertex(vertex), subEdges() {}
162
163 // Vertex from SubVertex
164 Vertex * vertex;
165
166 // Star SubEdges
167 QSet<SubEdge*> subEdges;
168 };
169
170 // Populate subcomplex
171 QSet<SubEdge*> subEdges;
172 QSet<SubVertex*> subVertices;
173 QMap<Vertex*, SubVertex*> vertexToSubVertex;
174 QMap<Edge*, SubEdge*> edgeToSubEdge;
175 foreach(Edge * edge, remainingEdges)
176 {
177 // create new subcomplex edge
178 SubEdge * subEdge = new SubEdge(edge);
179 subEdges << subEdge;
180 edgeToSubEdge[edge] = subEdge;
181
182 // assign its left subcomplex vertex (create it if doesn't exist yet)
183 Vertex * startVertex = edge->startVertex(); // not NULL because edge is open
184 SubVertex * leftSubVertex = vertexToSubVertex.value(startVertex, 0); // returns 0 if not found
185 if(!leftSubVertex)
186 {
187 leftSubVertex = new SubVertex(startVertex);
188 subVertices << leftSubVertex;
189 vertexToSubVertex[startVertex] = leftSubVertex;
190 }
191 subEdge->leftSubVertex = leftSubVertex;
192 leftSubVertex->subEdges << subEdge;
193
194 // assign its right subcomplex vertex (create it if doesn't exist yet)
195 Vertex * endVertex = edge->endVertex(); // we know it's not null since we rejected pure loops
196 SubVertex * rightSubVertex = vertexToSubVertex.value(endVertex, 0); // returns 0 if not found
197 if(!rightSubVertex)
198 {
199 rightSubVertex = new SubVertex(endVertex);
200 subVertices << rightSubVertex;
201 vertexToSubVertex[endVertex] = rightSubVertex;
202 }
203 subEdge->rightSubVertex = rightSubVertex;
204 rightSubVertex->subEdges << subEdge;
205 }
206
207 // ===== Compute connected components of subcomplex =====
208
209 while(!remainingEdges.isEmpty())
210 {
211 // New connected component
212 KeyEdgeSet connectedEdges;
213
214 // get the first edge/subEdge of a new connected component
215 Edge * firstEdge = *remainingEdges.begin();
216 SubEdge * firstSubEdge = edgeToSubEdge[firstEdge];
217
218 // get all subEdges/edges connected to this sub/edge/edge
219 QStack<SubEdge *> toProcess;
220 toProcess.push(firstSubEdge);
221 while(!toProcess.isEmpty())
222 {
223 SubEdge * subEdge = toProcess.top();
224 toProcess.pop();
225
226 // marked means:
227 // - it has already been removed from remainingEdges
228 // - it has already been added to connectedEdges
229 // - all its neighbours have already been inserted in toProcess (possibly twice)
230
231 if(!subEdge->marked)
232 {
233 // Mark and remove
234 remainingEdges.remove(subEdge->edge);
235 connectedEdges.insert(subEdge->edge);
236 subEdge->marked = true;
237
238 // Insert neighbours in stack
239 foreach(SubEdge * neighbour, subEdge->leftSubVertex->subEdges)
240 toProcess.push(neighbour);
241 foreach(SubEdge * neighbour, subEdge->rightSubVertex->subEdges)
242 toProcess.push(neighbour);
243 }
244 }
245
246 // Process connected component
247 connectedComponents_ << SmartConnectedKeyEdgeSet(connectedEdges);
248 }
249
250
251 // ===== Release memory =====
252
253 // release memory
254 foreach(SubEdge * subEdge, subEdges)
255 delete subEdge;
256 foreach(SubVertex * subVertex, subVertices)
257 delete subVertex;
258 }
259
numConnectedComponents() const260 int SmartKeyEdgeSet::numConnectedComponents() const
261 {
262 return connectedComponents_.size();
263 }
264
operator [](int i)265 SmartConnectedKeyEdgeSet & SmartKeyEdgeSet::operator [] (int i)
266 {
267 return connectedComponents_[i];
268 }
269
270
271 }
272