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 <QApplication>
18 #include "../OpenGL.h"
19 #include <QtDebug>
20 #include <QTextStream>
21 #include "../Picking.h"
22 #include "../DevSettings.h"
23 #include "../Global.h"
24 
25 #include "Cell.h"
26 
27 #include "VAC.h"
28 
29 #include "KeyCell.h"
30 #include "KeyVertex.h"
31 #include "KeyEdge.h"
32 #include "KeyFace.h"
33 #include "InbetweenCell.h"
34 #include "InbetweenVertex.h"
35 #include "InbetweenEdge.h"
36 #include "InbetweenFace.h"
37 #include "Algorithms.h"
38 
39 #include "../ViewSettings.h"
40 #include "../View3DSettings.h"
41 
42 #include "../SaveAndLoad.h"
43 
44 #include "../XmlStreamReader.h"
45 #include "../XmlStreamWriter.h"
46 
47 #include "../CssColor.h"
48 
49 namespace VectorAnimationComplex
50 {
51 
Cell(VAC * vac)52 Cell::Cell(VAC * vac) :
53     vac_(vac), id_(-1),
54     isHovered_(0), isSelected_(0)
55 {
56     colorHighlighted_[0] = 1;
57     colorHighlighted_[1] = 0.7;
58     colorHighlighted_[2] = 0.7;
59     colorHighlighted_[3] = 1;
60 
61     colorSelected_[0] = 1;
62     colorSelected_[1] = 0;
63     colorSelected_[2] = 0;
64     colorSelected_[3] = 1;
65 
66     color_[0] = 0;
67     color_[1] = 0;
68     color_[2] = 0;
69     color_[3] = 1;
70 }
71 
toCell()72 Cell * Cell::toCell()                       { return this; }
toKeyCell()73 KeyCell * Cell::toKeyCell()                 { return dynamic_cast<KeyCell*>(this); }
toInbetweenCell()74 InbetweenCell * Cell::toInbetweenCell()     { return dynamic_cast<InbetweenCell*>(this); }
toVertexCell()75 VertexCell * Cell::toVertexCell()           { return dynamic_cast<VertexCell*>(this); }
toEdgeCell()76 EdgeCell * Cell::toEdgeCell()               { return dynamic_cast<EdgeCell*>(this); }
toFaceCell()77 FaceCell * Cell::toFaceCell()               { return dynamic_cast<FaceCell*>(this); }
toKeyVertex()78 KeyVertex * Cell::toKeyVertex()             { return dynamic_cast<KeyVertex*>(this); }
toKeyEdge()79 KeyEdge * Cell::toKeyEdge()                 { return dynamic_cast<KeyEdge*>(this); }
toKeyFace()80 KeyFace * Cell::toKeyFace()                 { return dynamic_cast<KeyFace*>(this); }
toInbetweenVertex()81 InbetweenVertex * Cell::toInbetweenVertex() { return dynamic_cast<InbetweenVertex*>(this); }
toInbetweenEdge()82 InbetweenEdge * Cell::toInbetweenEdge()     { return dynamic_cast<InbetweenEdge*>(this); }
toInbetweenFace()83 InbetweenFace * Cell::toInbetweenFace()     { return dynamic_cast<InbetweenFace*>(this); }
84 
~Cell()85 Cell::~Cell()
86 {
87 }
destroy()88 void Cell::destroy()
89 {
90     vac()->deleteCell(this);
91 }
destroyStar()92 void Cell::destroyStar()
93 {
94     while(!star().isEmpty())
95         (*star().begin())->destroy();
96 }
informBoundaryImGettingDestroyed()97 void Cell::informBoundaryImGettingDestroyed()
98 {
99     removeMeFromStarOfBoundary_();
100 }
101 
addObserver(CellObserver * observer)102 void Cell::addObserver(CellObserver * observer)
103 {
104     observers_.insert(observer);
105 }
106 
removeObserver(CellObserver * observer)107 void Cell::removeObserver(CellObserver * observer)
108 {
109     observers_.remove(observer);
110 }
111 
dimension()112 int Cell::dimension()
113 {
114     if (toKeyVertex())
115         return 0;
116     else if (toKeyEdge() || toInbetweenVertex())
117         return 1;
118     else if (toKeyFace() || toInbetweenEdge())
119         return 2;
120     else
121         return 3;
122 }
123 
124 // Update cell boundary
updateBoundary_preprocess()125 void Cell::updateBoundary_preprocess()
126 {
127     removeMeFromStarOfBoundary_();
128 }
129 
updateBoundary_postprocess()130 void Cell::updateBoundary_postprocess()
131 {
132     addMeToStarOfBoundary_();
133 }
134 
updateBoundary(KeyVertex * oldVertex,KeyVertex * newVertex)135 void Cell::updateBoundary(KeyVertex * oldVertex, KeyVertex * newVertex)
136 {
137     updateBoundary_preprocess();
138     updateBoundary_impl(oldVertex,newVertex);
139     updateBoundary_postprocess();
140 }
141 
updateBoundary(const KeyHalfedge & oldHalfedge,const KeyHalfedge & newHalfedge)142 void Cell::updateBoundary(const KeyHalfedge & oldHalfedge, const KeyHalfedge & newHalfedge)
143 {
144     updateBoundary_preprocess();
145     updateBoundary_impl(oldHalfedge,newHalfedge);
146     updateBoundary_postprocess();
147 }
148 
updateBoundary(KeyEdge * oldEdge,const KeyEdgeList & newEdges)149 void Cell::updateBoundary(KeyEdge * oldEdge, const KeyEdgeList & newEdges)
150 {
151     updateBoundary_preprocess();
152     updateBoundary_impl(oldEdge,newEdges);
153     updateBoundary_postprocess();
154 }
155 
updateBoundary_impl(KeyVertex *,KeyVertex *)156 void Cell::updateBoundary_impl(KeyVertex * , KeyVertex * )    {}
updateBoundary_impl(const KeyHalfedge &,const KeyHalfedge &)157 void Cell::updateBoundary_impl(const KeyHalfedge & , const KeyHalfedge & ) {}
updateBoundary_impl(KeyEdge *,const KeyEdgeList &)158 void Cell::updateBoundary_impl(KeyEdge * , const KeyEdgeList & ) {}
159 
160 
Cell(Cell * other)161 Cell::Cell(Cell * other)
162 {
163     vac_ = other->vac_;
164     id_ = other->id_;
165     //isHighlighted_ = other->isHighlighted_;
166     isHovered_ = 0;
167     isSelected_ = other->isSelected_;
168     // this sounds like a bad architecture: other->colorHighlighted
169     // should not be zero. Kept like this for now anyway
170     if (other->colorHighlighted_)
171         for (int i=0; i<4; i++)
172             colorHighlighted_[i] = other->colorHighlighted_[i];
173     if (other->colorSelected_)
174         for (int i=0; i<4; i++)
175             colorSelected_[i] = other->colorSelected_[i];
176     if (other->color_)
177         for (int i=0; i<4; i++)
178             color_[i] = other->color_[i];
179     spatialStar_ = other->spatialStar_;
180     temporalStarBefore_ = other->temporalStarBefore_;
181     temporalStarAfter_ = other->temporalStarAfter_;
182 }
183 
remapPointers(VAC * newVAC)184 void Cell::remapPointers(VAC * newVAC)
185 {
186     vac_ = newVAC;
187 
188     {
189         CellSet old = spatialStar_;
190         spatialStar_.clear();
191         auto it = old.begin();
192         auto itEnd = old.end();
193         for(; it != itEnd; ++it)
194             spatialStar_ << newVAC->getCell((*it)->id());
195     }
196     {
197         CellSet old = temporalStarBefore_;
198         temporalStarBefore_.clear();
199         auto it = old.begin();
200         auto itEnd = old.end();
201         for(; it != itEnd; ++it)
202             temporalStarBefore_ << newVAC->getCell((*it)->id());
203     }
204     {
205         CellSet old = temporalStarAfter_;
206         temporalStarAfter_.clear();
207         auto it = old.begin();
208         auto itEnd = old.end();
209         for(; it != itEnd; ++it)
210             temporalStarAfter_ << newVAC->getCell((*it)->id());
211     }
212 }
213 
getCell(int id)214 Cell * Cell::getCell(int id)
215 {
216     return vac()->getCell(id);
217 }
218 
color() const219 QColor Cell::color() const
220 {
221     QColor res;
222     res.setRgbF(color_[0],color_[1],color_[2],color_[3]);
223     return res;
224 }
225 
setColor(const QColor & c)226 void Cell::setColor(const QColor & c)
227 {
228     color_[0] = c.redF();
229     color_[1] = c.greenF();
230     color_[2] = c.blueF();
231     color_[3] = c.alphaF();
232 }
233 
isHighlighted() const234 bool Cell::isHighlighted() const
235 {
236     if(isHovered())
237     {
238         if((global()->toolMode() == Global::SELECT))
239         {
240             Qt::KeyboardModifiers keys = global()->keyboardModifiers();
241 
242             if(isSelected())
243             {
244                 if((keys & Qt::AltModifier))
245                 {
246                     return true;
247                 }
248                 else
249                 {
250                     return false;
251                 }
252             }
253             else
254             {
255                 if( (keys & Qt::AltModifier) && !(keys & Qt::ShiftModifier) )
256                 {
257                     return false;
258                 }
259                 else
260                 {
261                     return true;
262                 }
263             }
264         }
265         else if((global()->toolMode() == Global::SKETCH))
266         {
267             return false;
268         }
269         else if((global()->toolMode() == Global::EDIT_CANVAS_SIZE))
270         {
271             return false;
272         }
273         else
274         {
275             return true;
276         }
277     }
278     else
279     {
280         return false;
281     }
282 }
283 
284 
285 
glColorTopology_()286 void Cell::glColorTopology_()
287 {
288     if(isHighlighted())
289         glColor4dv(colorHighlighted_);
290     else if(isSelected() && global()->toolMode() == Global::SELECT)
291         glColor4dv(colorSelected_);
292     else
293     {
294         bool inbetweenOutlineDifferentColor = true;
295         if(inbetweenOutlineDifferentColor)
296         {
297             if(toKeyVertex())
298                 glColor4d(0,0.165,0.514,1);
299             else if(toKeyEdge())
300                 glColor4d(0.18,0.60,0.90,1);
301             else if(toKeyFace())
302                 glColor4d(0.75,0.90,1.00,1);
303             else if(toInbetweenVertex())
304                 glColor4d(0.12,0.34,0,1);
305             else if(toInbetweenEdge())
306                 glColor4d(0.47,0.72,0.40,1);
307             else if(toInbetweenFace())
308                 glColor4d(0.94,1.00,0.91,1);
309             else // shouldn't happen
310                 glColor4d(0,0,0,1);
311         }
312         else
313         {
314             if(toVertexCell())
315                 glColor4d(0,0.165,0.514,1);
316             else if(toEdgeCell())
317                 glColor4d(0.18,0.60,0.90,1);
318             else // shouldn't happen
319                 glColor4d(0,0,0,1);
320         }
321     }
322 }
323 
getColor(Time,ViewSettings &) const324 QColor Cell::getColor(Time /*time*/, ViewSettings & /*viewSettings*/) const
325 {
326     QColor res;
327     res.setRedF(color_[0]);
328     res.setGreenF(color_[1]);
329     res.setBlueF(color_[2]);
330     res.setAlphaF(color_[3]);
331     return res;
332 }
333 
glColor_(Time time,ViewSettings & viewSettings)334 void Cell::glColor_(Time time, ViewSettings & viewSettings)
335 {
336     if(global()->displayMode() == Global::ILLUSTRATION_OUTLINE && !toFaceCell())
337     {
338         QColor c = getColor(time, viewSettings);
339         glColor4d(c.redF(), c.greenF(), c.blueF(), c.alphaF());
340     }
341     else if(isHighlighted())
342         glColor4dv(colorHighlighted_);
343     else if(isSelected() && global()->toolMode() == Global::SELECT)
344         glColor4dv(colorSelected_);
345     else
346     {
347         QColor c = getColor(time, viewSettings);
348         glColor4d(c.redF(), c.greenF(), c.blueF(), c.alphaF());
349     }
350 }
351 
glColor3D_()352 void Cell::glColor3D_()
353 {
354     if(global()->displayMode() == Global::ILLUSTRATION_OUTLINE && !toFaceCell())
355     {
356         glColor4dv(color_);
357     }
358     else if(isHighlighted())
359         glColor4dv(colorHighlighted_);
360     else if(isSelected() && global()->toolMode() == Global::SELECT)
361         glColor4dv(colorSelected_);
362     else
363     {
364         glColor4dv(color_);
365     }
366 }
367 
368 
369 
370 ////////////////////////////     Draw    ///////////////////////////////
371 
draw(Time time,ViewSettings & viewSettings)372 void Cell::draw(Time time, ViewSettings & viewSettings)
373 {
374     if (!exists(time))
375         return;
376 
377     glColor_(time, viewSettings);
378     drawRaw(time, viewSettings);
379 }
380 
drawRaw(Time time,ViewSettings &)381 void Cell::drawRaw(Time time, ViewSettings & /*viewSettings*/)
382 {
383     triangles(time).draw();
384 }
385 
drawPick(Time time,ViewSettings & viewSettings)386 void Cell::drawPick(Time time, ViewSettings & viewSettings)
387 {
388     if (!isPickable(time))
389     {
390         return;
391     }
392     else
393     {
394         Picking::glColor(id());
395         drawPickCustom(time, viewSettings);
396     }
397 }
398 
drawPickCustom(Time time,ViewSettings & viewSettings)399 void Cell::drawPickCustom(Time time, ViewSettings & viewSettings)
400 {
401     drawRaw(time, viewSettings);
402 }
403 
404 
405 
406 /////////////////////////     Draw Topology   /////////////////////////////
407 
drawTopology(Time time,ViewSettings & viewSettings)408 void Cell::drawTopology(Time time, ViewSettings & viewSettings)
409 {
410     if (!exists(time))
411         return;
412 
413     glColorTopology_();
414     drawRawTopology(time, viewSettings);
415 }
416 
drawRawTopology(Time time,ViewSettings &)417 void Cell::drawRawTopology(Time time, ViewSettings & /*viewSettings*/)
418 {
419     triangles(time).draw();
420 }
421 
drawPickTopology(Time time,ViewSettings & viewSettings)422 void Cell::drawPickTopology(Time time, ViewSettings & viewSettings)
423 {
424     if (!isPickable(time))
425     {
426         return;
427     }
428     else
429     {
430         Picking::glColor(id());
431         drawPickTopologyCustom(time, viewSettings);
432     }
433 }
434 
drawPickTopologyCustom(Time time,ViewSettings & viewSettings)435 void Cell::drawPickTopologyCustom(Time time, ViewSettings & viewSettings)
436 {
437     drawRawTopology(time, viewSettings);
438 }
439 
440 
441 
442 /////////////////////////     Draw 3D   /////////////////////////////
443 
draw3D(View3DSettings & viewSettings)444 void Cell::draw3D(View3DSettings & viewSettings)
445 {
446     glColor3D_();
447     this->drawRaw3D(viewSettings);
448 }
449 
drawRaw3D(View3DSettings &)450 void Cell::drawRaw3D(View3DSettings & /*viewSettings*/)
451 {
452 }
453 
drawPick3D(View3DSettings &)454 void Cell::drawPick3D(View3DSettings & /*viewSettings*/)
455 {
456 }
457 
isPickable(Time time) const458 bool Cell::isPickable(Time time) const
459 {
460     if (!exists(time))
461         return false;
462     else
463         return isPickableCustom(time);
464 }
465 
isPickableCustom(Time) const466 bool Cell::isPickableCustom(Time /*time*/) const
467 {
468     return false;
469 }
470 
471 // Topological Navigation Information
472 // ------------ Boundary ------------
boundary() const473 CellSet Cell::boundary() const
474 {
475     CellSet res = this->spatialBoundary();
476     CellSet other = this->temporalBoundary();
477     res.unite(other);
478     return res;
479 }
spatialBoundary() const480 CellSet Cell::spatialBoundary() const { return CellSet(); }
spatialBoundary(Time t) const481 CellSet Cell::spatialBoundary(Time t) const
482 {
483     CellSet res;
484     foreach(Cell * obj, this->spatialBoundary())
485         if(obj->exists(t))
486             res << obj;
487     return res;
488 }
temporalBoundary() const489 KeyCellSet Cell::temporalBoundary() const
490 {
491     KeyCellSet res = this->beforeCells();
492     res.unite(this->afterCells());
493     return res;
494 }
beforeCells() const495 KeyCellSet Cell::beforeCells() const { return KeyCellSet(); }
afterCells() const496 KeyCellSet Cell::afterCells() const { return KeyCellSet(); }
497 // -------------- Star --------------
star() const498 CellSet Cell::star() const
499 {
500     CellSet res = this->spatialStar();
501     CellSet other = this->temporalStar();
502     res.unite(other);
503     return res;
504 }
spatialStar() const505 CellSet Cell::spatialStar() const
506 {
507     return spatialStar_;
508 }
spatialStar(Time t) const509 CellSet Cell::spatialStar(Time t) const
510 {
511     if (exists(t))
512         return this->spatialStar();
513     else
514         return CellSet();
515     // this comes from the property that the spatial star
516     // of the cell does not depend on time, since by construction
517     // the temporal boundary of cells is instant
518     // -> (as far as I remember... would be good to check again)
519 }
temporalStar() const520 CellSet Cell::temporalStar() const
521 {
522     CellSet res = this->temporalStarBefore();
523     res.unite(this->temporalStarAfter());
524     return res;
525 }
temporalStarBefore() const526 CellSet Cell::temporalStarBefore() const
527 {
528     return temporalStarBefore_;
529 }
temporalStarAfter() const530 CellSet Cell::temporalStarAfter() const
531 {
532     return temporalStarAfter_;
533 }
534 
535 // ---------- Neighbourhood ---------
neighbourhood() const536 CellSet Cell::neighbourhood()  const
537 {
538     CellSet res = this->boundary();
539     res.unite(this->star());
540     return res;
541 }
spatialNeighbourhood() const542 CellSet Cell::spatialNeighbourhood() const
543 {
544     CellSet res = this->spatialBoundary();
545     res.unite(this->spatialStar());
546     return res;
547 }
spatialNeighbourhood(Time t) const548 CellSet Cell::spatialNeighbourhood(Time t)  const
549 {
550     CellSet res = this->spatialBoundary(t);
551     res.unite(this->spatialStar(t));
552     return res;
553 }
temporalNeighbourhood() const554 CellSet Cell::temporalNeighbourhood() const
555 {
556     CellSet res = this->temporalBoundary();
557     CellSet other = this->temporalStar();
558     res.unite(other);
559     return res;
560 }
temporalNeighbourhoodBefore() const561 CellSet Cell::temporalNeighbourhoodBefore() const
562 {
563     CellSet res = this->beforeCells();
564     CellSet other = this->temporalStarBefore();
565     res.unite(other);
566     return res;
567 }
temporalNeighbourhoodAfter() const568 CellSet Cell::temporalNeighbourhoodAfter() const
569 {
570     CellSet res = this->afterCells();
571     CellSet other = this->temporalStarAfter();
572     res.unite(other);
573     return res;
574 }
575 // -- Modifying star of boundary --
addMeToStarOfBoundary_()576 void Cell::addMeToStarOfBoundary_()
577 {
578     foreach(Cell * c, spatialBoundary())
579         addMeToSpatialStarOf_(c);
580 
581     foreach(KeyCell * c, beforeCells())
582         addMeToTemporalStarAfterOf_(c);
583 
584     foreach(KeyCell * c, afterCells())
585         addMeToTemporalStarBeforeOf_(c);
586 }
587 
removeMeFromStarOfBoundary_()588 void Cell::removeMeFromStarOfBoundary_()
589 {
590     foreach(Cell * c, spatialBoundary())
591         removeMeFromSpatialStarOf_(c);
592 
593     foreach(KeyCell * c, beforeCells())
594         removeMeFromTemporalStarAfterOf_(c);
595 
596     foreach(KeyCell * c, afterCells())
597         removeMeFromTemporalStarBeforeOf_(c);
598 }
599 
removeMeFromStarOf_(Cell * c)600 void Cell::removeMeFromStarOf_(Cell * c)
601 {
602     removeMeFromSpatialStarOf_(c);
603     removeMeFromTemporalStarBeforeOf_(c);
604     removeMeFromTemporalStarAfterOf_(c);
605 }
606 
addMeToSpatialStarOf_(Cell * c)607 void Cell::addMeToSpatialStarOf_(Cell * c)
608 {
609     c->spatialStar_ << this;
610 }
addMeToTemporalStarBeforeOf_(Cell * c)611 void Cell::addMeToTemporalStarBeforeOf_(Cell *c)
612 {
613     c->temporalStarBefore_ << this;
614 }
addMeToTemporalStarAfterOf_(Cell * c)615 void Cell::addMeToTemporalStarAfterOf_(Cell *c)
616 {
617     c->temporalStarAfter_ << this;
618 
619 }
removeMeFromSpatialStarOf_(Cell * c)620 void Cell::removeMeFromSpatialStarOf_(Cell * c)
621 {
622     c->spatialStar_.remove(this);
623 }
removeMeFromTemporalStarBeforeOf_(Cell * c)624 void Cell::removeMeFromTemporalStarBeforeOf_(Cell *c)
625 {
626     c->temporalStarBefore_.remove(this);
627 }
removeMeFromTemporalStarAfterOf_(Cell * c)628 void Cell::removeMeFromTemporalStarAfterOf_(Cell * c)
629 {
630     c->temporalStarAfter_.remove(this);
631 }
632 
save(QTextStream & out)633 void Cell::save(QTextStream & out)
634 {
635     // properties shared by all objects
636     out << Save::newField("Type") << stringType();
637     out << Save::newField("ID") << id();
638     out << Save::newField("Color")
639         << color_[0] << " "
640         << color_[1] << " "
641         << color_[2] << " "
642         << color_[3];
643 
644     // specific properties
645     save_(out);
646 
647 }
648 
save_(QTextStream &)649 void Cell::save_(QTextStream & /*out*/)
650 {
651 }
652 
exportSVG(Time,QTextStream &)653 void Cell::exportSVG(Time /*t*/, QTextStream & /*out*/)
654 {
655 }
656 
read1stPass(VAC * vac,QTextStream & in)657 Cell * Cell::read1stPass(VAC * vac, QTextStream & in)
658 {
659     Field field;
660     QString type;
661     in >> field >> type;
662 
663     if(type == "Vertex" || type == "KeyVertex" || type == "InstantVertex" )
664     {
665         return KeyVertex::Read1stPass::create(vac, in);
666     }
667     else if(type == "Edge" || type == "KeyEdge" || type == "InstantEdge" )
668     {
669         return KeyEdge::Read1stPass::create(vac, in);
670     }
671     else if(type == "Face" || type == "KeyFace" || type == "InstantFace" )
672     {
673         return KeyFace::Read1stPass::create(vac, in);
674     }
675     else if(type == "InbetweenVertex" || type == "SpacetimeVertex" )
676     {
677         return InbetweenVertex::Read1stPass::create(vac, in);
678     }
679     else if(type == "InbetweenEdge" || type == "SpacetimeEdge" )
680     {
681         return InbetweenEdge::Read1stPass::create(vac, in);
682     }
683     else if(type == "InbetweenFace" || type == "SpacetimeFace" )
684     {
685         return InbetweenFace::Read1stPass::create(vac, in);
686     }
687     else
688         return 0;
689 
690 }
read2ndPass()691 void Cell::read2ndPass()
692 {
693     // transform IDs to pointers via vac()->getCell(id);
694 }
695 
696 // Note: with this constructor, it is the VAC's responsibility
697 // to insert it in its list of objects.
Cell(VAC * vac,QTextStream & in)698 Cell::Cell(VAC * vac, QTextStream & in) :
699     vac_(vac), id_(-1),
700     isHovered_(0), isSelected_(0)
701 {
702     Field field;
703     in >> field >> id_;
704 
705     colorHighlighted_[0] = 1;
706     colorHighlighted_[1] = 0.7;
707     colorHighlighted_[2] = 0.7;
708     colorHighlighted_[3] = 1;
709 
710     colorSelected_[0] = 1;
711     colorSelected_[1] = 0;
712     colorSelected_[2] = 0;
713     colorSelected_[3] = 1;
714 
715     double r, g, b, a;
716     in >> field >> r >> g >> b >> a;
717     QColor c;
718     c.setRedF(r);
719     c.setGreenF(g);
720     c.setBlueF(b);
721     c.setAlphaF(a);
722     setColor(c);
723 }
724 
write(XmlStreamWriter & xml) const725 void Cell::write(XmlStreamWriter & xml) const
726 {
727     xml.writeStartElement(xmlType_());
728     xml.writeAttribute("id", QString().setNum(id()));
729     write_(xml);
730     CssColor cssColor(color_);
731     xml.writeAttribute("color", cssColor.toString());
732     xml.writeEndElement();
733 }
734 
write_(XmlStreamWriter &) const735 void Cell::write_(XmlStreamWriter & /*xml*/) const
736 {
737 
738 }
739 
xmlType_() const740 QString Cell::xmlType_() const
741 {
742     return "cell";
743 }
744 
Cell(VAC * vac,XmlStreamReader & xml)745 Cell::Cell(VAC * vac, XmlStreamReader & xml) :
746     vac_(vac), id_(-1),
747     isHovered_(0), isSelected_(0)
748 {
749     id_ = xml.attributes().value("id").toInt();
750 
751     if(xml.attributes().hasAttribute("color"))
752     {
753         CssColor c(xml.attributes().value("color").toString());
754         color_[0] = c.rF();
755         color_[1] = c.gF();
756         color_[2] = c.bF();
757         color_[3] = c.aF();
758     }
759     else
760     {
761         color_[0] = 0;
762         color_[1] = 0;
763         color_[2] = 0;
764         color_[3] = 1;
765     }
766 
767     colorHighlighted_[0] = 1;
768     colorHighlighted_[1] = 0.7;
769     colorHighlighted_[2] = 0.7;
770     colorHighlighted_[3] = 1;
771 
772     colorSelected_[0] = 1;
773     colorSelected_[1] = 0;
774     colorSelected_[2] = 0;
775     colorSelected_[3] = 1;
776 }
777 
check() const778 bool Cell::check() const
779 {
780     // check incident cells share the same VAC
781 
782     // check that the cell belongs to its VAC
783     if(!vac()->checkContains(this))
784         return false;
785 
786     // other type-specific checks
787     return check_();
788 }
789 
790 
791 //###################################################################
792 //                         GEOMETRY
793 //###################################################################
794 
triangles(Time t) const795 const Triangles & Cell::triangles(Time t) const
796 {
797     // Get cache key
798     int key = std::floor(t.floatTime() * 60 + 0.5);
799 
800     // Compute triangles if not yet cached
801     if(!triangles_.contains(key))
802         triangulate_(t, triangles_[key]);
803 
804     // Return cached triangles
805     return triangles_[key];
806 }
807 
boundingBox(Time t) const808 const BoundingBox & Cell::boundingBox(Time t) const
809 {
810     // Get cache key
811     int key = std::floor(t.floatTime() * 60 + 0.5);
812 
813     // Compute bounding box if not yet cached
814     if(!boundingBoxes_.contains(key))
815         boundingBoxes_[key] = triangles(t).boundingBox();
816 
817     // Return cached bounding box
818     return boundingBoxes_[key];
819 }
820 
outlineBoundingBox(Time t) const821 const BoundingBox & Cell::outlineBoundingBox(Time t) const
822 {
823     // Get cache key
824     int key = std::floor(t.floatTime() * 60 + 0.5);
825 
826     // Compute bounding box if not yet cached
827     if(!outlineBoundingBoxes_.contains(key))
828         computeOutlineBoundingBox_(t, outlineBoundingBoxes_[key]);
829 
830     // Return cached bounding box
831     return outlineBoundingBoxes_[key];
832 }
833 
intersects(Time t,const BoundingBox & bb) const834 bool Cell::intersects(Time t, const BoundingBox & bb) const
835 {
836     return triangles(t).intersects(bb);
837 }
838 
processGeometryChanged_()839 void Cell::processGeometryChanged_()
840 {
841     CellSet toClearCells = geometryDependentCells_();
842     foreach(Cell * cell, toClearCells)
843         cell->clearCachedGeometry_();
844 }
845 
clearCachedGeometry_()846 void Cell::clearCachedGeometry_()
847 {
848     triangles_.clear();
849     boundingBoxes_.clear();
850     outlineBoundingBoxes_.clear();
851 }
852 
853 // XXX this could be cached, it is called many times during
854 // drag and drop and affine transform while not changing
geometryDependentCells_()855 CellSet Cell::geometryDependentCells_()
856 {
857     CellSet res;
858     res << this;
859 
860     // Because of the Catmull-Rom scheme, need to reach further
861     KeyVertex * keyVertex = toKeyVertex();
862     if(keyVertex)
863     {
864         CellSet beforeVertices = keyVertex->beforeVertices();
865         CellSet afterVertices = keyVertex->afterVertices();
866         res.unite(beforeVertices);
867         res.unite(afterVertices);
868     }
869 
870     return Algorithms::fullstar(res);
871 }
872 
873 }
874