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 "ObjectPropertiesWidget.h"
18 #include "VectorAnimationComplex/VAC.h"
19 #include "VectorAnimationComplex/Cell.h"
20 #include "VectorAnimationComplex/KeyVertex.h"
21 #include "VectorAnimationComplex/KeyEdge.h"
22 #include "VectorAnimationComplex/KeyFace.h"
23 #include "VectorAnimationComplex/InbetweenVertex.h"
24 #include "VectorAnimationComplex/InbetweenEdge.h"
25 #include "VectorAnimationComplex/InbetweenFace.h"
26 #include "Global.h"
27 #include "MainWindow.h"
28 
ObjectPropertiesWidget()29 ObjectPropertiesWidget::ObjectPropertiesWidget()
30 {
31     // ---- Type ----
32     QLabel * typeLabel = new QLabel(tr("Type:"));
33     type_ = new QLabel(tr("no objects"));
34     QHBoxLayout * typeLayout = new QHBoxLayout();
35     typeLayout->addWidget(typeLabel);
36     typeLayout->setAlignment(typeLabel, Qt::AlignTop);
37     typeLayout->addWidget(type_);
38     typeLayout->addStretch();
39 
40     // ---- ID ----
41     moreText_ = tr("more");
42     lessText_ = tr("less");
43     QLabel * idLabel = new QLabel(tr("ID:"));
44     id_ = new QLabel("");
45     idMoreLessButton_ = new QPushButton(moreText_);
46     idMoreLessButton_->setMaximumHeight(15);
47     connect(idMoreLessButton_, SIGNAL(clicked()), this, SLOT(idMoreLessSlot()));
48     QHBoxLayout * idLayout = new QHBoxLayout();
49     idLayout->addWidget(idLabel);
50     idLayout->setAlignment(idLabel, Qt::AlignTop);
51     idLayout->addWidget(id_);
52     idLayout->setAlignment(id_, Qt::AlignTop);
53     idLayout->addWidget(idMoreLessButton_);
54     idLayout->setAlignment(idMoreLessButton_, Qt::AlignTop);
55     idLayout->addStretch();
56 
57     // -- inbetween closed edge --
58     inbetweenClosedEdgeWidgets_ = new QWidget();
59     inbetweenClosedEdgeBeforeCycleSlider_ = new QSlider(Qt::Horizontal);
60     inbetweenClosedEdgeBeforeCycleSlider_->setRange(0,100);
61     connect(inbetweenClosedEdgeBeforeCycleSlider_, SIGNAL(sliderMoved(int)), this, SLOT(updateBeforeCycleStartingPoint(int)));
62     connect(inbetweenClosedEdgeBeforeCycleSlider_, SIGNAL(sliderReleased()), this, SLOT(sliderStartingPointReleased()));
63     inbetweenClosedEdgeAfterCycleSlider_ = new QSlider(Qt::Horizontal);
64     inbetweenClosedEdgeAfterCycleSlider_->setRange(0,100);
65     connect(inbetweenClosedEdgeAfterCycleSlider_, SIGNAL(sliderMoved(int)), this, SLOT(updateAfterCycleStartingPoint(int)));
66     connect(inbetweenClosedEdgeAfterCycleSlider_, SIGNAL(sliderReleased()), this, SLOT(sliderStartingPointReleased()));
67     QGridLayout * inbetweenClosedEdgeLayout = new QGridLayout();
68     //inbetweenClosedEdgeLayout->addWidget(new QLabel("before cycle starting point:"),0,0);
69     inbetweenClosedEdgeLayout->addWidget(new QLabel("Cycle offset:"),1,0);
70     //inbetweenClosedEdgeLayout->addWidget(inbetweenClosedEdgeBeforeCycleSlider_,0,1);
71     inbetweenClosedEdgeLayout->addWidget(inbetweenClosedEdgeAfterCycleSlider_,1,1);
72     inbetweenClosedEdgeWidgets_->setLayout(inbetweenClosedEdgeLayout);
73 
74     // -- animated cycle --
75     animatedCycleComboBox_ = new QComboBox();
76     animatedCycleShowHide_ = new QPushButton("Show");
77     connect(animatedCycleShowHide_, SIGNAL(clicked()), this, SLOT(toggleAnimatedCycleShowHide()));
78     animatedCycleEdit_ = new QPushButton("Edit");
79     connect(animatedCycleEdit_, SIGNAL(clicked()), this, SLOT(animatedCycleEdit()));
80     animatedCycleNew_ = new QPushButton("New");
81     connect(animatedCycleNew_, SIGNAL(clicked()), this, SLOT(animatedCycleNew()));
82     animatedCycleDelete_ = new QPushButton("Delete");
83     connect(animatedCycleDelete_, SIGNAL(clicked()), this, SLOT(animatedCycleDelete()));
84     animatedCycleWidget_ = new AnimatedCycleWidget();
85     animatedCycleWidget_->hide();
86 
87     animatedCycleWidgets_ = new QWidget();
88     QWidget * hStretch = new QWidget();
89     hStretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
90     QHBoxLayout * animatedCycleButtonsLayout = new QHBoxLayout();
91     animatedCycleButtonsLayout->addWidget(animatedCycleComboBox_);
92     animatedCycleComboBox_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
93     connect(animatedCycleComboBox_, SIGNAL(currentIndexChanged(int)), this, SLOT(setAnimatedCycle(int)));
94     animatedCycleButtonsLayout->addWidget(animatedCycleShowHide_);
95     animatedCycleButtonsLayout->addWidget(animatedCycleEdit_);
96     animatedCycleButtonsLayout->addWidget(animatedCycleNew_);
97     animatedCycleButtonsLayout->addWidget(animatedCycleDelete_);
98     animatedCycleButtonsLayout->addStretch();
99     QVBoxLayout * animatedCycleWidgetsLayout = new QVBoxLayout();
100     animatedCycleWidgetsLayout->addLayout(animatedCycleButtonsLayout);
101     animatedCycleWidgetsLayout->addWidget(animatedCycleWidget_);
102     animatedCycleWidgets_->setLayout(animatedCycleWidgetsLayout);
103 
104     // -- Main layout --
105     mainLayout_ = new QVBoxLayout();
106     mainLayout_->addLayout(typeLayout);
107     mainLayout_->addLayout(idLayout);
108     mainLayout_->addWidget(inbetweenClosedEdgeWidgets_);
109     mainLayout_->addWidget(animatedCycleWidgets_);
110     bottomStretch_ = new QWidget();
111     bottomStretch_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
112     mainLayout_->addWidget(bottomStretch_);
113     //mainLayout_->addStretch();
114     setLayout(mainLayout_);
115 
116     // -- Set that no object is selected --
117     setObjects(VectorAnimationComplex::CellSet());
118 }
119 
~ObjectPropertiesWidget()120 ObjectPropertiesWidget::~ObjectPropertiesWidget()
121 {
122 
123 }
124 
hideAnimatedCycleWidget()125 void ObjectPropertiesWidget::hideAnimatedCycleWidget()
126 {
127     animatedCycleWidget_->hide();
128     animatedCycleWidget_->stop();
129     animatedCycleShowHide_->setText(tr("Show"));
130     bottomStretch_->show();
131 }
132 
showAnimatedCycleWidget()133 void ObjectPropertiesWidget::showAnimatedCycleWidget()
134 {
135     animatedCycleWidget_->show();
136     animatedCycleWidget_->start();
137     animatedCycleShowHide_->setText(tr("Hide"));
138     bottomStretch_->hide();
139 }
140 
toggleAnimatedCycleShowHide()141 void ObjectPropertiesWidget::toggleAnimatedCycleShowHide()
142 {
143     if(animatedCycleWidget_->isVisible())
144     {
145         hideAnimatedCycleWidget();
146     }
147     else
148     {
149         showAnimatedCycleWidget();
150     }
151 }
152 
animatedCycleEdit()153 void ObjectPropertiesWidget::animatedCycleEdit()
154 {
155     global()->mainWindow()->editAnimatedCycle(inbetweenFace_,animatedCycleComboBox_->currentIndex());
156     hideAnimatedCycleWidget();
157 }
158 
animatedCycleNew()159 void ObjectPropertiesWidget::animatedCycleNew()
160 {
161     inbetweenFace_->addAnimatedCycle();
162     updateAnimatedCycleComboBox();
163 
164     animatedCycleComboBox_->setCurrentIndex(inbetweenFace_->numAnimatedCycles()-1);
165     animatedCycleEdit();
166 
167     VectorAnimationComplex::VAC * vac = inbetweenFace_->vac();
168     emit vac->needUpdatePicking();
169     emit vac->changed();
170     emit vac->checkpoint();
171 }
172 
animatedCycleDelete()173 void ObjectPropertiesWidget::animatedCycleDelete()
174 {
175     int i = animatedCycleComboBox_->currentIndex();
176     int n = inbetweenFace_->numAnimatedCycles();
177     if(0<=i && i<n)
178         inbetweenFace_->removeCycle(i);
179     updateAnimatedCycleComboBox();
180 
181     VectorAnimationComplex::VAC * vac = inbetweenFace_->vac();
182     emit vac->needUpdatePicking();
183     emit vac->changed();
184     emit vac->checkpoint();
185 }
186 
updateBeforeCycleStartingPoint(int i)187 void ObjectPropertiesWidget::updateBeforeCycleStartingPoint(int i)
188 {
189     if(inbetweenEdge_ && inbetweenEdge_->isClosed())
190     {
191         inbetweenEdge_->setBeforeCycleStartingPoint( (double)i / 100.0);
192 
193         VectorAnimationComplex::VAC * vac = inbetweenEdge_->vac();
194         emit vac->needUpdatePicking();
195         emit vac->changed();
196     }
197 }
198 
updateAfterCycleStartingPoint(int i)199 void ObjectPropertiesWidget::updateAfterCycleStartingPoint(int i)
200 {
201     if(inbetweenEdge_ && inbetweenEdge_->isClosed())
202     {
203         inbetweenEdge_->setAfterCycleStartingPoint( (double)i / 100.0);
204 
205         VectorAnimationComplex::VAC * vac = inbetweenEdge_->vac();
206         emit vac->needUpdatePicking();
207         emit vac->changed();
208     }
209 }
210 
sliderStartingPointReleased()211 void ObjectPropertiesWidget::sliderStartingPointReleased()
212 {
213 
214     if(inbetweenEdge_ && inbetweenEdge_->isClosed())
215     {
216         VectorAnimationComplex::VAC * vac = inbetweenEdge_->vac();
217         emit vac->checkpoint();
218     }
219 }
220 
hideInbetweenClosedEdgeWidgets()221 void ObjectPropertiesWidget::hideInbetweenClosedEdgeWidgets()
222 {
223     inbetweenClosedEdgeWidgets_->hide();
224     bottomStretch_->show();
225 }
226 
showInbetweenClosedEdgeWidgets()227 void ObjectPropertiesWidget::showInbetweenClosedEdgeWidgets()
228 {
229     inbetweenClosedEdgeWidgets_->show();
230     bottomStretch_->show();
231 }
232 
hideAnimatedCycleWidgets()233 void ObjectPropertiesWidget::hideAnimatedCycleWidgets()
234 {
235     animatedCycleWidgets_->hide();
236     animatedCycleWidget_->stop();
237     animatedCycleWidget_->setAnimatedCycle(AnimatedCycle());
238     bottomStretch_->show();
239 }
240 
showAnimatedCycleWidgets()241 void ObjectPropertiesWidget::showAnimatedCycleWidgets()
242 {
243     animatedCycleWidgets_->show();
244     if(animatedCycleWidget_->isVisible())
245     {
246         animatedCycleWidget_->start();
247         bottomStretch_->hide();
248     }
249 }
250 
setAnimatedCycle(int i)251 void ObjectPropertiesWidget::setAnimatedCycle(int i)
252 {
253     if(i>=0 && i<inbetweenFace_->numAnimatedCycles())
254         animatedCycleWidget_->setAnimatedCycle(inbetweenFace_, i);
255 }
256 
updateAnimatedCycleComboBox()257 void ObjectPropertiesWidget::updateAnimatedCycleComboBox()
258 {
259     animatedCycleComboBox_->clear();
260     for(int i=1; i<=inbetweenFace_->numAnimatedCycles(); ++i)
261     {
262         animatedCycleComboBox_->addItem(tr("Animated Cycle %1").arg(i));
263     }
264 }
265 
setObject(InbetweenEdge * inbetweenEdge)266 void ObjectPropertiesWidget::setObject(InbetweenEdge * inbetweenEdge)
267 {
268     inbetweenEdge_ = inbetweenEdge;
269 
270     if(inbetweenEdge_->isClosed())
271     {
272         inbetweenClosedEdgeBeforeCycleSlider_->setValue((int) (inbetweenEdge_->beforeCycleStartingPoint() * 100) );
273         inbetweenClosedEdgeAfterCycleSlider_->setValue((int) (inbetweenEdge_->afterCycleStartingPoint() * 100) );
274         showInbetweenClosedEdgeWidgets();
275     }
276     else
277     {
278 
279     }
280 }
281 
setObject(InbetweenFace * inbetweenFace)282 void ObjectPropertiesWidget::setObject(InbetweenFace * inbetweenFace)
283 {
284     inbetweenFace_ = inbetweenFace;
285 
286     showAnimatedCycleWidgets();
287     updateAnimatedCycleComboBox();
288 
289     animatedCycleComboBox_->setCurrentIndex(0);
290     setAnimatedCycle(0);
291 }
292 
setObjects(const VectorAnimationComplex::CellSet & cells)293 void ObjectPropertiesWidget::setObjects(const VectorAnimationComplex::CellSet & cells)
294 {
295     // Always visible info
296     setType(getStringType(cells));
297     setId(cells);
298 
299     // Hide all other selection-dependent info
300     hideInbetweenClosedEdgeWidgets();
301     hideAnimatedCycleWidgets();
302 
303     // set pointers to null
304     inbetweenEdge_ = 0;
305     inbetweenFace_ = 0;
306 
307     // Show selection-dependent info
308     if(cells.size() == 1)
309     {
310         Cell * cell = *cells.begin();
311         InbetweenEdge * sedge = cell->toInbetweenEdge();
312         InbetweenFace * sface = cell->toInbetweenFace();
313         if(sedge)
314         {
315             setObject(sedge);
316         }
317         else if(sface)
318         {
319             setObject(sface);
320         }
321     }
322 }
323 
324 
getStringType(VectorAnimationComplex::Cell * cell)325 QString ObjectPropertiesWidget::getStringType(VectorAnimationComplex::Cell * cell)
326 {
327     if(cell->toKeyVertex())
328         return tr("key vertex");
329     else if(cell->toKeyEdge())
330         return tr("key edge");
331     else if(cell->toKeyFace())
332         return tr("key face");
333     else if(cell->toInbetweenVertex())
334         return tr("inbetween vertex");
335     else if(cell->toInbetweenEdge())
336         return tr("inbetween edge");
337     else if(cell->toInbetweenFace())
338         return tr("inbetween face");
339     else
340         return tr("unknown object");
341 }
342 
getStringType(const VectorAnimationComplex::CellSet & cells)343 QString ObjectPropertiesWidget::getStringType(const VectorAnimationComplex::CellSet & cells)
344 {
345     //VectorAnimationComplex::KeyVertexSet kv = cells;
346     //VectorAnimationComplex::KeyEdgeSet ke = cells;
347     //VectorAnimationComplex::KeyFaceSet kf = cells;
348     //VectorAnimationComplex::InbetweenVertexSet sv = cells;
349     //VectorAnimationComplex::InbetweenEdgeSet se = cells;
350     //VectorAnimationComplex::InbetweenFaceSet sf = cells;
351 
352     // Count cells
353     int nkv = 0;
354     int nkoe = 0;
355     int nkce = 0;
356     int nkf = 0;
357     int nsv = 0;
358     int nsoe = 0;
359     int nsce = 0;
360     int nsf = 0;
361     foreach(VectorAnimationComplex::Cell * cell, cells)
362     {
363         if(cell->toKeyVertex())
364             ++nkv;
365         else if(cell->toKeyEdge())
366         {
367             if(cell->toKeyEdge()->isClosed())
368                 ++nkce;
369             else
370                 ++nkoe;
371         }
372         else if(cell->toKeyFace())
373             ++nkf;
374         else if(cell->toInbetweenVertex())
375             ++nsv;
376         else if(cell->toInbetweenEdge())
377         {
378             if(cell->toInbetweenEdge()->isClosed())
379                 ++nsce;
380             else
381                 ++nsoe;
382         }
383         else if(cell->toInbetweenFace())
384             ++nsf;
385     }
386 
387     // Set string according to count
388     QStringList stringList;
389     if(nkv == 1)
390         stringList << tr("1 key vertex");
391     if(nkv > 1)
392         stringList << QString().setNum(nkv) + tr(" key vertices");
393     if(nkce == 1)
394         stringList << tr("1 key closed edge");
395     if(nkce > 1)
396         stringList << QString().setNum(nkce) + tr(" key closed edges");
397     if(nkoe == 1)
398         stringList << tr("1 key open edge");
399     if(nkoe > 1)
400         stringList << QString().setNum(nkoe) + tr(" key open edges");
401     if(nkf == 1)
402         stringList << tr("1 key face");
403     if(nkf > 1)
404         stringList << QString().setNum(nkf) + tr(" key faces");
405     if(nsv == 1)
406         stringList << tr("1 inbetween vertex");
407     if(nsv > 1)
408         stringList << QString().setNum(nsv) + tr(" inbetween vertices");
409     if(nsce == 1)
410         stringList << tr("1 inbetween closed edge");
411     if(nsce > 1)
412         stringList << QString().setNum(nsce) + tr(" inbetween closed edges");
413     if(nsoe == 1)
414         stringList << tr("1 inbetween open edge");
415     if(nsoe > 1)
416         stringList << QString().setNum(nsoe) + tr(" inbetween open edges");
417     if(nsf == 1)
418         stringList << tr("1 inbetween face");
419     if(nsf > 1)
420         stringList << QString().setNum(nsf) + tr(" inbetween faces");
421 
422     if(stringList.isEmpty())
423         return tr("no objects");
424     else
425     {
426         QString res = stringList[0];
427         for(int i=1; i<stringList.size(); ++i)
428             res += QString("\n") + stringList[i];
429         return res;
430     }
431 }
432 
setType(const QString & type)433 void ObjectPropertiesWidget::setType(const QString & type)
434 {
435     type_->setText(type);
436 }
437 
setId(const VectorAnimationComplex::CellSet & cells)438 void ObjectPropertiesWidget::setId(const VectorAnimationComplex::CellSet & cells)
439 {
440     int numIdPerLine = 5;
441 
442     idLess_ = QString("");
443     idMore_ = QString("");
444     idMoreLessButton_->hide();
445 
446     int i = 0; // count cells
447     int j = 0; // num on line
448     foreach(VectorAnimationComplex::Cell * cell, cells)
449     {
450         j = i % numIdPerLine;
451         if(i==0)
452         {
453             idMore_ += QString().setNum(cell->id());
454             idLess_ += QString().setNum(cell->id());
455         }
456         else if(j==0)
457         {
458             idMore_ += QString(",\n") + QString().setNum(cell->id());
459 
460             if(i == numIdPerLine)
461             {
462                 idLess_ += QString(",...");
463                 idMoreLessButton_->show();
464             }
465         }
466         else
467         {
468             idMore_ += QString(", ") + QString().setNum(cell->id());
469 
470             if(i < numIdPerLine)
471             {
472                 idLess_ += QString(", ") + QString().setNum(cell->id());
473             }
474 
475 
476         }
477         ++i;
478     }
479 
480     setIdFromString();
481 }
482 
setIdFromString()483 void ObjectPropertiesWidget::setIdFromString()
484 {
485     if(idMoreLessButton_->text() == moreText_)
486     {
487         id_->setText(idLess_);
488     }
489     else
490     {
491         id_->setText(idMore_);
492     }
493     update();
494 }
495 
idMoreLessSlot()496 void ObjectPropertiesWidget::idMoreLessSlot()
497 {
498     if(idMoreLessButton_->text() ==  moreText_)
499     {
500         idMoreLessButton_->setText(lessText_);
501     }
502     else
503     {
504         idMoreLessButton_->setText(moreText_);
505     }
506     setIdFromString();
507 }
508