Update copyright headers
[qt:qt.git] / demos / qtdemo / itemcircleanimation.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "itemcircleanimation.h"
43 #include "demoitemanimation.h"
44 #include "colors.h"
45 #include "menumanager.h"
46 #include "mainwindow.h"
47 #include "menumanager.h"
48
49 static QGraphicsScene *sscene;
50
51 //////////////////// POST EFFECT STUFF ////////////////////////////////////////
52
53 class TickerPostEffect
54 {
55 public:
56     virtual ~TickerPostEffect(){};
57     virtual void tick(float){};
58     virtual void transform(DemoItem *, QPointF &){};
59 };
60
61 class PostRotateXY : public TickerPostEffect
62 {
63 public:
64     float currRotX, currRotY;
65     float speedx, speedy, curvx, curvy;
66
67     PostRotateXY(float speedx, float speedy, float curvx, float curvy)
68         : currRotX(0), currRotY(0),
69         speedx(speedx), speedy(speedy),
70         curvx(curvx), curvy(curvy){};
71
72     void tick(float adjust)
73     {
74         currRotX += speedx * adjust;
75         currRotY += speedy * adjust;
76     }
77
78     void transform(DemoItem *item, QPointF &pos)
79     {
80         DemoItem *parent = (DemoItem *) item->parentItem();
81         QPointF center = parent->boundingRect().center();
82         pos.setX(center.x() + (pos.x() - center.x()) * cos(currRotX + pos.x() * curvx));
83         pos.setY(center.y() + (pos.y() - center.y()) * cos(currRotY + pos.y() * curvy));
84     }
85 };
86
87 class PostRotateXYTwist : public TickerPostEffect
88 {
89 public:
90     float currRotX, currRotY;
91     float speedx, speedy, curvx, curvy;
92
93     PostRotateXYTwist(float speedx, float speedy, float curvx, float curvy)
94         : currRotX(0), currRotY(0),
95         speedx(speedx), speedy(speedy),
96         curvx(curvx), curvy(curvy){};
97
98     void tick(float adjust)
99     {
100         currRotX += speedx * adjust;
101         currRotY += speedy * adjust;
102     }
103
104     void transform(DemoItem *item, QPointF &pos)
105     {
106         DemoItem *parent = (DemoItem *) item->parentItem();
107         QPointF center = parent->boundingRect().center();
108         pos.setX(center.x() + (pos.x() - center.x()) * cos(currRotX + pos.y() * curvx));
109         pos.setY(center.y() + (pos.y() - center.y()) * cos(currRotY + pos.x() * curvy));
110     }
111 };
112
113 //////////////////// TICKER EFFECT STUFF //////////////////////////////////////
114
115 class TickerEffect
116 {
117     TickerPostEffect *postEffect;
118 public:
119     enum EffectStatus{Normal, Intro, Outro} status;
120     LetterList *letters;
121     float morphSpeed, moveSpeed;
122     float normalMorphSpeed, normalMoveSpeed;
123     bool useSheepDog, morphBetweenModels;
124
125     TickerEffect(LetterList *letters)
126         : postEffect(new TickerPostEffect()), status(Intro), letters(letters),
127         morphSpeed(Colors::tickerMorphSpeed), moveSpeed(Colors::tickerMoveSpeed),
128         normalMorphSpeed(Colors::tickerMorphSpeed), normalMoveSpeed(Colors::tickerMoveSpeed),
129         useSheepDog(true), morphBetweenModels(!Colors::noTickerMorph){}
130
131     void setPostEffect(TickerPostEffect *effect)
132     {
133         delete postEffect;
134         postEffect = effect;
135     }
136
137     virtual ~TickerEffect()
138     {
139         delete postEffect;
140     }
141
142     void slowDownAfterIntro(float adjust)
143     {
144         if (morphBetweenModels){
145             if (status == Intro){
146                 float dec = 0.1 * adjust;
147                 moveSpeed -= dec;
148                 if (moveSpeed < Colors::tickerMoveSpeed){
149                     moveSpeed = normalMoveSpeed;
150                     morphSpeed = normalMorphSpeed;
151                     status = Normal;
152                 }
153             }
154         }
155     }
156
157     void moveLetters(float adjust)
158     {
159         float adaptedMoveSpeed = this->moveSpeed * adjust;
160         float adaptedMorphSpeed = this->morphSpeed * adjust;
161         postEffect->tick(adjust);
162
163         for (int i=0; i<letters->size(); i++){
164             LetterItem *letter = letters->at(i);
165             letter->guideAdvance(this->morphBetweenModels ? adaptedMoveSpeed : Colors::tickerMoveSpeed);
166             letter->guideMove(this->morphBetweenModels ? adaptedMorphSpeed : -1);
167
168             QPointF pos = letter->getGuidedPos();
169             postEffect->transform(letter, pos);
170
171             if (useSheepDog)
172                 letter->setPosUsingSheepDog(pos, QRectF(0, 0, 800, 600));
173             else
174                 letter->setPos(pos);
175         }
176     }
177
178     virtual void tick(float adjust)
179     {
180         slowDownAfterIntro(adjust);
181         moveLetters(adjust);
182     }
183
184 };
185
186 class EffectWhirlWind : public TickerEffect
187 {
188 public:
189     EffectWhirlWind(LetterList *letters) : TickerEffect(letters)
190     {
191         moveSpeed = 50;
192         for (int i=0; i<this->letters->size(); i++){
193             LetterItem *letter = this->letters->at(i);
194             letter->setGuidedPos(QPointF(0, 100));
195         }
196     }
197 };
198
199 class EffectSnake : public TickerEffect
200 {
201 public:
202     EffectSnake(LetterList *letters) : TickerEffect(letters)
203     {
204         moveSpeed = 40;
205         for (int i=0; i<this->letters->size(); i++){
206             LetterItem *letter = this->letters->at(i);
207             letter->setGuidedPos(QPointF(0, -250 - (i * 5)));
208         }
209     }
210 };
211
212 class EffectScan : public TickerEffect
213 {
214 public:
215     EffectScan(LetterList *letters) : TickerEffect(letters)
216     {
217         for (int i=0; i<this->letters->size(); i++){
218             LetterItem *letter = this->letters->at(i);
219             letter->setGuidedPos(QPointF(100, -300));
220         }
221     }
222 };
223
224 class EffectRaindrops : public TickerEffect
225 {
226 public:
227     EffectRaindrops(LetterList *letters) : TickerEffect(letters)
228     {
229         for (int i=0; i<this->letters->size(); i++){
230             LetterItem *letter = this->letters->at(i);
231             letter->setGuidedPos(QPointF(-100 + rand() % 200, - 200.0f - rand() % 1300));
232         }
233     }
234 };
235
236 class EffectLine : public TickerEffect
237 {
238 public:
239     EffectLine(LetterList *letters) : TickerEffect(letters)
240     {
241         for (int i=0; i<this->letters->size(); i++){
242             LetterItem *letter = this->letters->at(i);
243             letter->setGuidedPos(QPointF(100, 500.0f + i * 20));
244         }
245     }
246 };
247
248 //////////////////// TICKER STUFF /////////////////////////////////////////////
249
250 ItemCircleAnimation::ItemCircleAnimation(QGraphicsScene *scene, QGraphicsItem *parent)
251     : DemoItem(scene, parent)
252 {
253     sscene = scene;
254     this->letterCount = Colors::tickerLetterCount;
255     this->scale = 1;
256     this->showCount = -1;
257     this->tickOnPaint = false;
258     this->paused = false;
259     this->doIntroTransitions = true;
260     this->setAcceptsHoverEvents(true);
261     this->setCursor(Qt::OpenHandCursor);
262     this->setupGuides();
263     this->setupLetters();
264     this->useGuideQt();
265     this->effect = 0;//new TickerEffect(this->letterList);
266 }
267
268 ItemCircleAnimation::~ItemCircleAnimation()
269 {
270     delete this->letterList;
271     delete this->qtGuide1;
272     delete this->qtGuide2;
273     delete this->qtGuide3;
274     delete this->effect;
275 }
276
277 void ItemCircleAnimation::createLetter(char c)
278 {
279     LetterItem *letter = new LetterItem(c, sscene, this);
280     this->letterList->append(letter);
281 }
282
283 void ItemCircleAnimation::setupLetters()
284 {
285     this->letterList = new LetterList();
286
287     QString s = Colors::tickerText;
288     int len = s.length();
289     int i = 0;
290     for (; i < this->letterCount - len; i += len)
291         for (int l=0; l<len; l++)
292             createLetter(s[l].toLatin1());
293
294     // Fill inn with blanks:
295     for (; i < this->letterCount; ++i)
296         createLetter(' ');
297 }
298
299 void ItemCircleAnimation::setupGuides()
300 {
301     int x = 0;
302     int y = 20;
303
304     this->qtGuide1 = new GuideCircle(QRectF(x, y, 260, 260), -36, 342);
305     this->currGuide = 0;
306     new GuideLine(QPointF(x + 240, y + 268), this->qtGuide1);
307     new GuideLine(QPointF(x + 265, y + 246), this->qtGuide1);
308     new GuideLine(QPointF(x + 158, y + 134), this->qtGuide1);
309     new GuideLine(QPointF(x + 184, y + 109), this->qtGuide1);
310     new GuideLine(QPointF(x + 160, y +  82), this->qtGuide1);
311     new GuideLine(QPointF(x +  77, y + 163), this->qtGuide1); // T-top
312     new GuideLine(QPointF(x + 100, y + 190), this->qtGuide1);
313     new GuideLine(QPointF(x + 132, y + 159), this->qtGuide1);
314     new GuideLine(QPointF(x + 188, y + 211), this->qtGuide1);
315     new GuideCircle(QRectF(x + 30, y + 30, 200, 200), -30, 336, GuideCircle::CW, this->qtGuide1);
316     new GuideLine(QPointF(x + 238, y + 201), this->qtGuide1);
317
318     y = 30;
319     this->qtGuide2 = new GuideCircle(QRectF(x + 30, y + 30, 200, 200), 135, 270, GuideCircle::CCW);
320     new GuideLine(QPointF(x + 222, y + 38), this->qtGuide2);
321     new GuideCircle(QRectF(x, y, 260, 260), 135, 270, GuideCircle::CW, this->qtGuide2);
322     new GuideLine(QPointF(x + 59, y + 59), this->qtGuide2);
323
324     x = 115;
325     y = 10;
326     this->qtGuide3 = new GuideLine(QLineF(x, y, x + 30, y));
327     new GuideLine(QPointF(x + 30, y + 170), this->qtGuide3);
328     new GuideLine(QPointF(x, y + 170), this->qtGuide3);
329     new GuideLine(QPointF(x, y), this->qtGuide3);
330
331     this->qtGuide1->setFence(QRectF(0, 0, 800, 600));
332     this->qtGuide2->setFence(QRectF(0, 0, 800, 600));
333     this->qtGuide3->setFence(QRectF(0, 0, 800, 600));
334 }
335
336 void ItemCircleAnimation::useGuide(Guide *guide, int firstLetter, int lastLetter)
337 {
338     float padding = guide->lengthAll() / float(lastLetter - firstLetter);
339     for (int i=firstLetter; i<lastLetter; i++){
340         LetterItem *letter = this->letterList->at(i);
341         letter->useGuide(guide, (i - firstLetter) * padding);
342     }
343 }
344
345 void ItemCircleAnimation::useGuideQt()
346 {
347     if (this->currGuide != this->qtGuide1){
348         this->useGuide(qtGuide1, 0, this->letterCount);
349         this->currGuide = qtGuide1;
350     }
351 }
352
353 void ItemCircleAnimation::useGuideTt()
354 {
355     if (this->currGuide != this->qtGuide2){
356         int split = int(this->letterCount * 5.0 / 7.0);
357         this->useGuide(qtGuide2, 0, split);
358         this->useGuide(qtGuide3, split, this->letterCount);
359         this->currGuide = qtGuide2;
360     }
361 }
362
363 QRectF ItemCircleAnimation::boundingRect() const
364 {
365     return QRectF(0, 0, 300, 320);
366 }
367
368 void ItemCircleAnimation::prepare()
369 {
370 }
371
372 void ItemCircleAnimation::switchToNextEffect()
373 {
374     ++this->showCount;
375     delete this->effect;
376
377     switch (this->showCount){
378     case 1:
379         this->effect = new EffectSnake(this->letterList);
380         break;
381     case 2:
382         this->effect = new EffectLine(this->letterList);
383         this->effect->setPostEffect(new PostRotateXYTwist(0.01f, 0.0f, 0.003f, 0.0f));
384         break;
385     case 3:
386         this->effect = new EffectRaindrops(this->letterList);
387         this->effect->setPostEffect(new PostRotateXYTwist(0.01f, 0.005f, 0.003f, 0.003f));
388         break;
389     case 4:
390         this->effect = new EffectScan(this->letterList);
391         this->effect->normalMoveSpeed = 0;
392         this->effect->setPostEffect(new PostRotateXY(0.008f, 0.0f, 0.005f, 0.0f));
393         break;
394     default:
395         this->showCount = 0;
396         this->effect = new EffectWhirlWind(this->letterList);
397     }
398 }
399
400 void ItemCircleAnimation::animationStarted(int id)
401 {
402     if (id == DemoItemAnimation::ANIM_IN){
403         if (this->doIntroTransitions){
404             // Make all letters dissapear
405             for (int i=0; i<this->letterList->size(); i++){
406                 LetterItem *letter = this->letterList->at(i);
407                 letter->setPos(1000, 0);
408             }
409             this->switchToNextEffect();
410             this->useGuideQt();
411             this->scale = 1;
412             // The first time we run, we have a rather large
413             // delay to perform benchmark before the ticker shows.
414             // But now, since we are showing, use a more appropriate value:
415             this->currentAnimation->startDelay = 1500;
416         }
417     }
418     else if (this->effect)
419         this->effect->useSheepDog = false;
420
421     this->tickTimer = QTime::currentTime();
422 }
423
424 void ItemCircleAnimation::animationStopped(int)
425 {
426     // Nothing to do.
427 }
428
429 void ItemCircleAnimation::swapModel(){
430     if (this->currGuide == this->qtGuide2)
431         this->useGuideQt();
432     else
433         this->useGuideTt();
434 }
435
436 void ItemCircleAnimation::hoverEnterEvent(QGraphicsSceneHoverEvent *)
437 {
438 //  Skip swap here to enhance ticker dragging
439 //    this->swapModel();
440 }
441
442 void ItemCircleAnimation::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
443 {
444     this->swapModel();
445 }
446
447 void ItemCircleAnimation::setTickerScale(float s)
448 {
449     this->scale = s;
450     qtGuide1->setScale(this->scale, this->scale);
451     qtGuide2->setScale(this->scale, this->scale);
452     qtGuide3->setScale(this->scale, this->scale);
453 }
454
455 void ItemCircleAnimation::mousePressEvent(QGraphicsSceneMouseEvent *event)
456 {
457     this->mouseMoveLastPosition = event->scenePos();
458     if (event->button() == Qt::LeftButton)
459         this->setCursor(Qt::ClosedHandCursor);
460     else
461         this->switchToNextEffect();
462 }
463
464 void ItemCircleAnimation::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
465 {
466     if (event->button() == Qt::LeftButton)
467         this->setCursor(Qt::OpenHandCursor);
468 }
469
470 void ItemCircleAnimation::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
471 {
472     QPointF newPosition = event->scenePos();
473     this->setPosUsingSheepDog(this->pos() + newPosition - this->mouseMoveLastPosition, QRectF(-260, -280, 1350, 1160));
474     this->mouseMoveLastPosition = newPosition;
475 }
476
477 void ItemCircleAnimation::wheelEvent(QGraphicsSceneWheelEvent *event)
478 {
479     this->effect->moveSpeed = this->effect->moveSpeed + (event->delta() > 0 ? -0.20 : 0.20);
480     if (this->effect->moveSpeed < 0)
481         this->effect->moveSpeed = 0;
482 }
483
484 void ItemCircleAnimation::pause(bool on)
485 {
486     this->paused = on;
487     this->tickTimer = QTime::currentTime();
488 }
489
490 void ItemCircleAnimation::tick()
491 {
492     if (this->paused || !this->effect)
493         return;
494
495     float t = this->tickTimer.msecsTo(QTime::currentTime());
496     this->tickTimer = QTime::currentTime();
497     this->effect->tick(t/10.0f);
498 }
499
500 void ItemCircleAnimation::paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
501 {
502     if (this->tickOnPaint)
503         tick();
504 }
505
506
507
508