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