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 "Path.h"
18 #include "KeyVertex.h"
19 #include "KeyEdge.h"
20 #include "EdgeGeometry.h"
21 #include "VAC.h"
22 #include "../SaveAndLoad.h"
23
24 #include <QMessageBox>
25
26 namespace VectorAnimationComplex
27 {
28
type() const29 Path::PathType Path::type() const
30 {
31 if(vertex_)
32 {
33 return SingleVertex;
34 }
35 else if (halfedges_.isEmpty())
36 {
37 return Invalid;
38 }
39 else
40 {
41 return OpenHalfedgeList;
42 }
43 }
44
isValid() const45 bool Path::isValid() const
46 {
47 return type() != Invalid;
48 }
49
50
Path()51 Path::Path() :
52 tempId_(-1),
53 vertex_(0)
54 {
55 // invalid by default, nothing to do (since edges_ is empty)
56 }
57
Path(KeyVertex * instantVertex)58 Path::Path(KeyVertex * instantVertex) :
59 tempId_(-1),
60 vertex_(instantVertex)
61 {
62 }
63
Path(const QList<KeyHalfedge> halfedges)64 Path::Path(const QList<KeyHalfedge> halfedges) :
65 tempId_(-1),
66 vertex_(0),
67 halfedges_(halfedges)
68 {
69 // Check that it's valid
70 if(halfedges_.size() == 0)
71 return;
72 for(int i=0; i<halfedges_.size()-1; i++)
73 {
74 if( (halfedges_[i].isClosed()) || (halfedges_[i].endVertex() != halfedges_[i+1].startVertex()) )
75 {
76 halfedges_.clear();
77 break;
78 }
79 }
80 }
81
Path(const KeyEdgeSet & edgeSetConst)82 Path::Path(const KeyEdgeSet & edgeSetConst) :
83 tempId_(-1),
84 vertex_(0)
85 {
86 // TODO: For now, assume it is a strict path
87
88 // If no edge, then invalid
89 if(edgeSetConst.isEmpty())
90 return;
91
92 // if not all edges at same time, then invalid
93 KeyEdge * first = *edgeSetConst.begin();
94 Time t = first->time();
95 foreach(KeyEdge * iedge, edgeSetConst)
96 {
97 if(iedge->time() != t)
98 {
99 //QMessageBox::information(0, QObject::tr("operation aborted"),
100 // QObject::tr("not all edges are on same time"));
101 return;
102 }
103 }
104
105 // copy the set to be able to modify it
106 KeyEdgeSet edgeSet = edgeSetConst;
107
108 // insert first edge
109 halfedges_ << KeyHalfedge(first, true);
110 edgeSet.erase(edgeSet.begin());
111
112 // check case where it's a pure loop
113 if(first->isClosed())
114 {
115 // What's commented below was copy-pasted from Cycle.cpp. For Path.cpp, closed edges make no sense at all
116 //if(!edgeSet.isEmpty())
117 //{
118 // //QMessageBox::information(0, QObject::tr("operation aborted"),
119 // QObject::tr("more than one edge and one of them is a pure loop"));
120 halfedges_.clear();
121 return;
122
123 //}
124 // else: good!
125 }
126 else
127 {
128 // not a pure loop, let's find the chain
129 while(!edgeSet.isEmpty())
130 {
131 KeyVertex * lastVertex = halfedges_.last().endVertex(); // hence this is a valid vertex
132 KeyVertex * firstVertex = halfedges_.first().startVertex(); // hence this is a valid vertex
133
134 // find next
135 KeyHalfedge nextHalfedge;
136 auto it = edgeSet.begin();
137 auto itEnd = edgeSet.end();
138 for(;it!=itEnd;++it)
139 {
140 if((*it)->startVertex() == lastVertex)
141 {
142 nextHalfedge = KeyHalfedge(*it, true);
143 break;
144 }
145 else if((*it)->endVertex() == lastVertex)
146 {
147 nextHalfedge = KeyHalfedge(*it, false);
148 break;
149 }
150 }
151
152 // if found: great, append it!
153 if(nextHalfedge.isValid())
154 {
155 halfedges_ << nextHalfedge;
156 edgeSet.erase(it);
157 }
158 else // can still try to prepend one
159 {
160 // find next
161 KeyHalfedge previousHalfedge;
162 it = edgeSet.begin();
163 for(;it!=itEnd;++it)
164 {
165 if((*it)->endVertex() == firstVertex)
166 {
167 previousHalfedge = KeyHalfedge(*it, true);
168 break;
169 }
170 else if((*it)->startVertex() == firstVertex)
171 {
172 previousHalfedge = KeyHalfedge(*it, false);
173 break;
174 }
175 }
176
177 // if found: great, prepend it!
178 if(previousHalfedge.isValid())
179 {
180 halfedges_.prepend(nextHalfedge);
181 edgeSet.erase(it);
182 }
183 else
184 {
185 halfedges_.clear();
186 return;
187 }
188 }
189 }
190
191 // So far, we've inserted all N edges, and every edge i in [0,N-2]
192 // satisfies edges_[i]->endVertex() == edges_[i+1]->startVertex()
193
194 // Check that it's looping
195 //if(halfedges_.last().endVertex() != halfedges_.first().startVertex())
196 //{
197 //QMessageBox::information(0, QObject::tr("operation aborted"),
198 // QObject::tr("not a valid loop: last edge not compatible with first one"));
199 // halfedges_.clear();
200 // return;
201 //}
202
203 // Check that it's simple
204 //KeyVertexSet vertices;
205 //foreach(KeyHalfEdge he, halfedges_)
206 //{
207 // KeyVertex * vertex = he.startVertex();
208 // if(vertices.contains(vertex))
209 // {
210 //QMessageBox::information(0, QObject::tr("operation aborted"),
211 // QObject::tr("not a valid loop: not simple"));
212 // halfedges_.clear();
213 // return;
214 // }
215 // else
216 // {
217 // vertices << vertex;
218 // }
219 //}
220
221 // Done :-) If you're here you have a valid simple loop
222 }
223 }
224
225 // Conversion from proper path
Path(const ProperPath & properPath)226 Path::Path(const ProperPath & properPath) :
227 tempId_(-1),
228 vertex_(0)
229 {
230 if(properPath.isValid())
231 {
232 for(int i=0; i<properPath.size(); ++i)
233 {
234 halfedges_ << properPath[i];
235 }
236 }
237 }
238 // Conversion from proper path
Path(const ProperCycle & properCycle)239 Path::Path(const ProperCycle & properCycle) :
240 tempId_(-1),
241 vertex_(0)
242 {
243 if(properCycle.isValid() && !properCycle[0].isClosed())
244 {
245 for(int i=0; i<properCycle.size(); ++i)
246 {
247 halfedges_ << properCycle[i];
248 }
249 }
250 }
251
time() const252 Time Path::time() const
253 {
254 assert(isValid());
255 switch(type())
256 {
257 case SingleVertex:
258 return vertex_->time();
259 break;
260 case OpenHalfedgeList:
261 return halfedges_[0].time();
262 break;
263 case Invalid:
264 return Time();
265 break;
266 }
267
268 return Time();
269 }
270
singleVertex() const271 KeyVertex * Path::singleVertex() const
272 {
273 return vertex_;
274 }
275
size() const276 int Path::size() const
277 {
278 return halfedges_.size();
279 }
280
operator [](int i) const281 KeyHalfedge Path::operator[](int i) const
282 {
283 return halfedges_[i];
284 }
285
286 // The set of cells this helper points to
cells() const287 KeyCellSet Path::cells() const
288 {
289 KeyCellSet res;
290
291
292 switch(type())
293 {
294 case SingleVertex:
295 res << vertex_;
296 break;
297
298 case OpenHalfedgeList:
299 for(int i=0; i<size(); ++i)
300 {
301 KeyHalfedge he = halfedges_[i];
302 res << he.startVertex() << he.edge;
303 }
304 res << halfedges_.last().endVertex();
305 break;
306
307 case Invalid:
308 break;
309 }
310
311 return res;
312 }
313
startVertex() const314 KeyVertex * Path::startVertex() const
315 {
316 assert(isValid());
317
318 if(type() == SingleVertex)
319 {
320 return singleVertex();
321 }
322 else
323 {
324 return halfedges_.first().startVertex();
325 }
326 }
327
endVertex() const328 KeyVertex * Path::endVertex() const
329 {
330 assert(isValid());
331
332 if(type() == SingleVertex)
333 {
334 return singleVertex();
335 }
336 else
337 {
338 return halfedges_.last().endVertex();
339 }
340 }
341
342
343
remapPointers(VAC * newVAC)344 void Path::remapPointers(VAC * newVAC)
345 {
346 for(int i=0; i<halfedges_.size(); ++i)
347 halfedges_[i].remapPointers(newVAC);
348
349 if(vertex_)
350 {
351 Cell * c = newVAC->getCell(vertex_->id());
352 vertex_ = c->toKeyVertex();
353 }
354 }
355
convertTempIdsToPointers(VAC * vac)356 void Path::convertTempIdsToPointers(VAC * vac)
357 {
358 // Vertex
359 Cell * cell = vac->getCell(tempId_);
360 if(cell)
361 vertex_ = cell->toKeyVertex();
362 else
363 vertex_ = 0;
364
365 // Halfedges
366 for(int i=0; i<halfedges_.size(); ++i)
367 halfedges_[i].convertTempIdsToPointers(vac);
368 }
369
370 // The XML file syntax for paths is either:
371 // [e1,beta1 e2,beta2 ... eN,betaN]
372 // or
373 // [v]
374
toString() const375 QString Path::toString() const
376 {
377 QString res;
378
379 if(vertex_)
380 {
381 // Vertex
382 res += "[" + QString().setNum(vertex_->id()) + "]";
383 }
384 else
385 {
386 // Halfedges
387 res += "[";
388 for(int i=0; i<halfedges_.size(); ++i)
389 {
390 if(i>0)
391 res += " ";
392 res += QString().setNum(halfedges_[i].edge->id()) + (halfedges_[i].side ? "+" : "-");
393 }
394 res += "]";
395 }
396
397 return res;
398 }
399
fromString(const QString & str)400 void Path::fromString(const QString & str)
401 {
402 // Clear
403 tempId_ = -1;
404 vertex_ = 0;
405 halfedges_.clear();
406
407 // Split at ',', '[', ']', or any whitespace character
408 QStringList strList = str.split(QRegExp("[\\,\\s\\[\\]]"), QString::SkipEmptyParts);
409
410 // Get some info to determine cycle type
411 QString firstStr = strList[0];
412 QChar c = firstStr.at(firstStr.length()-1);
413
414 // Switch depending on type
415 if(strList.size() == 1 && c != '+' && c != '-')
416 {
417 // Vertex
418 tempId_ = strList[0].toInt();
419 }
420 else
421 {
422 // Halfedges
423 int n = strList.size();
424 for(int i=0; i<n; ++i)
425 {
426 QString str = strList[i];
427 int l = str.length();
428 QChar side = str.at(l-1);
429 QString edge = str.left(l-1);
430
431 KeyHalfedge h;
432 h.tempId_ = edge.toInt();
433 h.side = (side == '+');
434 halfedges_ << h;
435 }
436 }
437 }
438
439
440 // Replace pointed edges
replaceEdges(KeyEdge * oldEdge,const KeyEdgeList & newEdges)441 void Path::replaceEdges(KeyEdge * oldEdge, const KeyEdgeList & newEdges)
442 {
443 QList<KeyHalfedge> newHalfedges;
444
445 foreach(KeyHalfedge he, halfedges_)
446 {
447 if(he.edge == oldEdge)
448 {
449 // Replace halfedge
450 if(he.side)
451 {
452 for(int i=0; i<newEdges.size(); ++i)
453 newHalfedges << KeyHalfedge(newEdges[i], he.side);
454 }
455 else
456 {
457 for(int i=newEdges.size()-1; i>=0; --i)
458 newHalfedges << KeyHalfedge(newEdges[i], he.side);
459 }
460 }
461 else
462 {
463 // Keep halfedge as is
464 newHalfedges << he;
465 }
466 }
467
468 halfedges_ = newHalfedges;
469 }
470
replaceVertex(KeyVertex * oldVertex,KeyVertex * newVertex)471 void Path::replaceVertex(KeyVertex * oldVertex, KeyVertex * newVertex)
472 {
473 if(vertex_ == oldVertex)
474 vertex_ = newVertex;
475 }
476
477
replaceHalfedge(const KeyHalfedge & oldHalfedge,const KeyHalfedge & newHalfedge)478 void Path::replaceHalfedge(const KeyHalfedge & oldHalfedge, const KeyHalfedge & newHalfedge)
479 {
480 for(int j=0; j<halfedges_.size(); j++)
481 {
482 if(halfedges_[j].edge == oldHalfedge.edge)
483 {
484 halfedges_[j].edge = newHalfedge.edge;
485 halfedges_[j].side = ((halfedges_[j].side == oldHalfedge.side) == newHalfedge.side);
486 }
487 }
488 }
489
length() const490 double Path::length() const
491 {
492 assert(isValid());
493
494 if(type() == SingleVertex)
495 {
496 return 0;
497 }
498 else
499 {
500 double res = 0;
501 foreach(KeyHalfedge he, halfedges_)
502 res += he.edge->geometry()->length();
503 return res;
504 }
505 }
506
sample(int numSamples,QList<EdgeSample> & out) const507 void Path::sample(int numSamples, QList<EdgeSample> & out) const
508 {
509 assert(isValid());
510 out.clear();
511
512 if(type() == SingleVertex)
513 {
514 Eigen::Vector2d pos = singleVertex()->pos();
515 EdgeSample posSample(pos[0],pos[1],0);
516 for(int i=0; i<numSamples; ++i)
517 out << posSample;
518 }
519 else
520 {
521 assert(numSamples >= 2);
522 double ds = length()/(numSamples-1);
523
524 double cumulativeLength = 0;
525 int indexHe = 0;
526 KeyHalfedge he = halfedges_[indexHe];
527 for(int i=0; i<numSamples; ++i)
528 {
529 double s = i*ds;
530 while ( (s > cumulativeLength + he.length()) && (indexHe+1 < halfedges_.size()) )
531 {
532 cumulativeLength += he.length();
533 he = halfedges_[++indexHe];
534 }
535 out << he.sample(s-cumulativeLength);
536 }
537 }
538
539 }
540
sample(int numSamples,QList<Eigen::Vector2d> & out) const541 void Path::sample(int numSamples, QList<Eigen::Vector2d> & out) const
542 {
543 assert(isValid());
544 out.clear();
545
546 if(type() == SingleVertex)
547 {
548 Eigen::Vector2d pos = singleVertex()->pos();
549 for(int i=0; i<numSamples; ++i)
550 out << pos;
551 }
552 else
553 {
554 assert(numSamples >= 2);
555 double ds = length()/(numSamples-1);
556
557 double cumulativeLength = 0;
558 int indexHe = 0;
559 KeyHalfedge he = halfedges_[indexHe];
560 for(int i=0; i<numSamples; ++i)
561 {
562 double s = i*ds;
563 while ( (s > cumulativeLength + he.length()) && (indexHe+1 < halfedges_.size()) )
564 {
565 cumulativeLength += he.length();
566 he = halfedges_[++indexHe];
567 }
568 out << he.pos(s-cumulativeLength);
569 }
570 }
571 }
572
reversed() const573 Path Path::reversed() const
574 {
575 Path res;
576 res.vertex_ = vertex_;
577
578 int n=size();
579 for(int i=0; i<n; ++i)
580 {
581 KeyHalfedge h = halfedges_[n-1-i];
582 h.side = !h.side;
583 res.halfedges_ << h;
584 }
585
586 return res;
587 }
588
589 } // end namespace VectorAnimationComplex
590
operator <<(QTextStream & out,const VectorAnimationComplex::Path & Path)591 QTextStream & operator<<(QTextStream & out, const VectorAnimationComplex::Path & Path)
592 {
593 // Vertex
594 if(Path.vertex_)
595 out << Path.vertex_->id();
596 else
597 out << -1;
598
599 // Halfedges
600 out << " " << Path.halfedges_;
601
602 return out;
603 }
604
operator >>(QTextStream & in,VectorAnimationComplex::Path & Path)605 QTextStream & operator>>(QTextStream & in, VectorAnimationComplex::Path & Path)
606 {
607 in >> Path.tempId_ >> Path.halfedges_;
608
609 return in;
610 }
611