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