Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / imports / particles / V1 / qdeclarativeparticles.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativeparticles_p.h"
43
44 #include <qdeclarativeinfo.h>
45 #include <QtQuick1/private/qdeclarativeitem_p.h>
46
47 #include <QtQuick1/private/qdeclarativepixmapcache_p.h>
48 #include <QtCore/QAbstractAnimation>
49
50 #include <QPainter>
51 #include <QtWidgets/qdrawutil.h>
52 #include <QVarLengthArray>
53
54 #include <stdlib.h>
55 #include <math.h>
56
57 #ifndef M_PI
58 #define M_PI 3.14159265358979323846
59 #define M_PI_2 (M_PI / 2.)
60 #endif
61 #ifndef INT_MAX
62 #define INT_MAX 2147483647
63 #endif
64
65 QT_BEGIN_NAMESPACE
66 #define PI_SQR 9.8696044
67 // parabolic approximation
68 inline qreal fastSin(qreal theta)
69 {
70     const qreal b = 4 / M_PI;
71     const qreal c = -4 / PI_SQR;
72
73     qreal y = b * theta + c * theta * qAbs(theta);
74     return y;
75 }
76
77 inline qreal fastCos(qreal theta)
78 {
79     theta += M_PI_2;
80     if (theta > M_PI)
81         theta -= 2 * M_PI;
82
83     return fastSin(theta);
84 }
85
86 class QDeclarativeParticle
87 {
88 public:
89     QDeclarativeParticle(int time) : lifeSpan(1000), fadeOutAge(800)
90         , opacity(0), birthTime(time), x_velocity(0), y_velocity(0)
91         , state(FadeIn), data(0)
92     {
93     }
94
95     int lifeSpan;
96     int fadeOutAge;
97     qreal x;
98     qreal y;
99     qreal opacity;
100     int birthTime;
101     qreal x_velocity;
102     qreal y_velocity;
103     enum State { FadeIn, Solid, FadeOut };
104     State state;
105     void *data;
106 };
107
108 //---------------------------------------------------------------------------
109
110 /*!
111     \class QDeclarativeParticleMotion
112     \ingroup group_effects
113     \brief The QDeclarativeParticleMotion class is the base class for particle motion.
114     \internal
115
116     This class causes the particles to remain static.
117 */
118
119 /*!
120     Constructs a QDeclarativeParticleMotion with parent object \a parent.
121 */
122 QDeclarativeParticleMotion::QDeclarativeParticleMotion(QObject *parent) :
123     QObject(parent)
124 {
125 }
126
127 /*!
128     Move the \a particle to its new position.  \a interval is the number of
129     milliseconds elapsed since it was last moved.
130 */
131 void QDeclarativeParticleMotion::advance(QDeclarativeParticle &particle, int interval)
132 {
133     Q_UNUSED(particle);
134     Q_UNUSED(interval);
135 }
136
137 /*!
138     The \a particle has just been created.  Some motion strategies require
139     additional state information.  This can be allocated by this function.
140 */
141 void QDeclarativeParticleMotion::created(QDeclarativeParticle &particle)
142 {
143     Q_UNUSED(particle);
144 }
145
146 /*!
147     The \a particle is about to be destroyed.  Any additional memory
148     that has been allocated for the particle should be freed.
149 */
150 void QDeclarativeParticleMotion::destroy(QDeclarativeParticle &particle)
151 {
152     Q_UNUSED(particle);
153 }
154
155 /*!
156     \qmlclass ParticleMotionLinear QDeclarativeParticleMotionLinear
157     \ingroup qml-particle-elements
158     \since 4.7
159     \brief The ParticleMotionLinear object moves particles linearly.
160
161     \sa Particles
162
163     This is the default motion, and moves the particles according to the
164     properties specified in the Particles element.
165
166     It has no further properties.
167 */
168 void QDeclarativeParticleMotionLinear::advance(QDeclarativeParticle &p, int interval)
169 {
170     p.x += interval * p.x_velocity;
171     p.y += interval * p.y_velocity;
172 }
173
174 /*!
175     \qmlclass ParticleMotionGravity QDeclarativeParticleMotionGravity
176     \ingroup qml-particle-elements
177     \since 4.7
178     \brief The ParticleMotionGravity object moves particles towards a point.
179
180     This motion attracts the particles to the specified point with the specified acceleration.
181     To mimic earth gravity, set yattractor to -6360000 and acceleration to 9.8.
182
183     The defaults are all 0, not earth gravity, and so no motion will occur without setting
184     at least the acceleration property.
185
186
187     \sa Particles
188 */
189
190 /*!
191     \qmlproperty real ParticleMotionGravity::xattractor
192     \qmlproperty real ParticleMotionGravity::yattractor
193     These properties hold the x and y coordinates of the point attracting the particles.
194 */
195
196 /*!
197     \qmlproperty real ParticleMotionGravity::acceleration
198     This property holds the acceleration to apply to the particles.
199 */
200
201 /*!
202     \property QDeclarativeParticleMotionGravity::xattractor
203     \brief the x coordinate of the point attracting the particles.
204 */
205
206 /*!
207     \property QDeclarativeParticleMotionGravity::yattractor
208     \brief the y coordinate of the point attracting the particles.
209 */
210
211 /*!
212     \property QDeclarativeParticleMotionGravity::acceleration
213     \brief the acceleration to apply to the particles.
214 */
215
216 void QDeclarativeParticleMotionGravity::setXAttractor(qreal x)
217 {
218     if (qFuzzyCompare(x, _xAttr))
219         return;
220     _xAttr = x;
221     emit xattractorChanged();
222 }
223
224 void QDeclarativeParticleMotionGravity::setYAttractor(qreal y)
225 {
226     if (qFuzzyCompare(y, _yAttr))
227         return;
228     _yAttr = y;
229     emit yattractorChanged();
230 }
231
232 void QDeclarativeParticleMotionGravity::setAcceleration(qreal accel)
233 {
234     qreal scaledAccel = accel/1000000.0;
235     if (qFuzzyCompare(scaledAccel, _accel))
236         return;
237     _accel = scaledAccel;
238     emit accelerationChanged();
239 }
240
241 void QDeclarativeParticleMotionGravity::advance(QDeclarativeParticle &p, int interval)
242 {
243     qreal xdiff = _xAttr - p.x;
244     qreal ydiff = _yAttr - p.y;
245     qreal absXdiff = qAbs(xdiff);
246     qreal absYdiff = qAbs(ydiff);
247
248     qreal xcomp = xdiff / (absXdiff + absYdiff);
249     qreal ycomp = ydiff / (absXdiff + absYdiff);
250
251     p.x_velocity += xcomp * _accel * interval;
252     p.y_velocity += ycomp * _accel * interval;
253
254     p.x += interval * p.x_velocity;
255     p.y += interval * p.y_velocity;
256 }
257
258 /*!
259     \qmlclass ParticleMotionWander QDeclarativeParticleMotionWander
260     \ingroup qml-particle-elements
261     \since 4.7
262     \brief The ParticleMotionWander object moves particles in a somewhat random fashion.
263
264     The particles will continue roughly in the original direction, however will randomly
265     drift to each side.
266
267     The code below produces an effect similar to falling snow.
268
269     \qml
270 Rectangle {
271     width: 240
272     height: 320
273     color: "black"
274
275     Particles {
276         y: 0
277         width: parent.width
278         height: 30
279         source: "star.png"
280         lifeSpan: 5000
281         count: 50
282         angle: 70
283         angleDeviation: 36
284         velocity: 30
285         velocityDeviation: 10
286         ParticleMotionWander {
287             xvariance: 30
288             pace: 100
289         }
290     }
291 }
292     \endqml
293
294     \sa Particles
295 */
296
297 /*!
298     \qmlproperty real ParticleMotionWander::xvariance
299     \qmlproperty real ParticleMotionWander::yvariance
300
301     These properties set the amount to wander in the x and y directions.
302 */
303
304 /*!
305     \qmlproperty real ParticleMotionWander::pace
306     This property holds how quickly the paricles will move from side to side.
307 */
308
309 void QDeclarativeParticleMotionWander::advance(QDeclarativeParticle &p, int interval)
310 {
311     if (!particles)
312         particles = qobject_cast<QDeclarativeParticles*>(parent());
313     if (particles) {
314         Data *d = (Data*)p.data;
315         if (_xvariance != 0.) {
316             qreal xdiff = p.x_velocity - d->x_targetV;
317             if ((xdiff > d->x_peak && d->x_var > 0.0) || (xdiff < -d->x_peak && d->x_var < 0.0)) {
318                 d->x_var = -d->x_var;
319                 d->x_peak = _xvariance + _xvariance * qreal(qrand()) / RAND_MAX;
320             }
321             p.x_velocity += d->x_var * interval;
322         }
323         p.x += interval * p.x_velocity;
324
325         if (_yvariance != 0.) {
326             qreal ydiff = p.y_velocity - d->y_targetV;
327             if ((ydiff > d->y_peak && d->y_var > 0.0) || (ydiff < -d->y_peak && d->y_var < 0.0)) {
328                 d->y_var = -d->y_var;
329                 d->y_peak = _yvariance + _yvariance * qreal(qrand()) / RAND_MAX;
330             }
331             p.y_velocity += d->y_var * interval;
332         }
333         p.y += interval * p.y_velocity;
334     }
335 }
336
337 void QDeclarativeParticleMotionWander::created(QDeclarativeParticle &p)
338 {
339     if (!p.data) {
340         Data *d = new Data;
341         p.data = (void*)d;
342         d->x_targetV = p.x_velocity;
343         d->y_targetV = p.y_velocity;
344         d->x_peak = _xvariance;
345         d->y_peak = _yvariance;
346         d->x_var = _pace * qreal(qrand()) / RAND_MAX / 1000.0;
347         d->y_var = _pace * qreal(qrand()) / RAND_MAX / 1000.0;
348     }
349 }
350
351 void QDeclarativeParticleMotionWander::destroy(QDeclarativeParticle &p)
352 {
353     if (p.data)
354         delete (Data*)p.data;
355 }
356
357 void QDeclarativeParticleMotionWander::setXVariance(qreal var)
358 {
359     qreal scaledVar = var / 1000.0;
360     if (qFuzzyCompare(scaledVar, _xvariance))
361         return;
362     _xvariance = scaledVar;
363     emit xvarianceChanged();
364 }
365
366 void QDeclarativeParticleMotionWander::setYVariance(qreal var)
367 {
368     qreal scaledVar = var / 1000.0;
369     if (qFuzzyCompare(scaledVar, _yvariance))
370         return;
371     _yvariance = scaledVar;
372     emit yvarianceChanged();
373 }
374
375 void QDeclarativeParticleMotionWander::setPace(qreal pace)
376 {
377     qreal scaledPace = pace / 1000.0;
378     if (qFuzzyCompare(scaledPace, _pace))
379         return;
380     _pace = scaledPace;
381     emit paceChanged();
382 }
383
384 //---------------------------------------------------------------------------
385 class QDeclarativeParticlesPainter : public QDeclarativeItem
386 {
387 public:
388     QDeclarativeParticlesPainter(QDeclarativeParticlesPrivate *p, QDeclarativeItem* parent)
389         : QDeclarativeItem(parent), d(p)
390     {
391         setFlag(QGraphicsItem::ItemHasNoContents, false);
392         maxX = minX = maxY = minY = 0;
393     }
394
395     void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
396
397     void updateSize();
398
399     qreal maxX;
400     qreal minX;
401     qreal maxY;
402     qreal minY;
403     QDeclarativeParticlesPrivate* d;
404 };
405
406 //an animation that just gives a tick
407 template<class T, void (T::*method)(int)>
408 class TickAnimationProxy : public QAbstractAnimation
409 {
410 public:
411     TickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {}
412     virtual int duration() const { return -1; }
413 protected:
414     virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); }
415
416 private:
417     T *m_p;
418 };
419
420 //---------------------------------------------------------------------------
421 class QDeclarativeParticlesPrivate : public QDeclarativeItemPrivate
422 {
423     Q_DECLARE_PUBLIC(QDeclarativeParticles)
424 public:
425     QDeclarativeParticlesPrivate()
426         : count(1), emissionRate(-1), emissionVariance(0.5), lifeSpan(1000)
427         , lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300)
428         , angle(0), angleDev(0), velocity(0), velocityDev(0), emissionCarry(0.)
429         , addParticleTime(0), addParticleCount(0), lastAdvTime(0)
430         , motion(0), clock(this)
431     {
432     }
433
434     ~QDeclarativeParticlesPrivate()
435     {
436     }
437
438     void init()
439     {
440         Q_Q(QDeclarativeParticles);
441         paintItem = new QDeclarativeParticlesPainter(this, q);
442     }
443
444     void tick(int time);
445     void createParticle(int time);
446     void updateOpacity(QDeclarativeParticle &p, int age);
447
448     QUrl url;
449     QDeclarative1Pixmap image;
450     int count;
451     int emissionRate;
452     qreal emissionVariance;
453     int lifeSpan;
454     int lifeSpanDev;
455     int fadeInDur;
456     int fadeOutDur;
457     qreal angle;
458     qreal angleDev;
459     qreal velocity;
460     qreal velocityDev;
461     qreal emissionCarry;
462     int addParticleTime;
463     int addParticleCount;
464     int lastAdvTime;
465     QDeclarativeParticleMotion *motion;
466     QDeclarativeParticlesPainter *paintItem;
467
468
469     QList<QPair<int, int> > bursts;//countLeft, emissionRate pairs
470     QList<QDeclarativeParticle> particles;
471     TickAnimationProxy<QDeclarativeParticlesPrivate, &QDeclarativeParticlesPrivate::tick> clock;
472
473 };
474
475 void QDeclarativeParticlesPrivate::tick(int time)
476 {
477     Q_Q(QDeclarativeParticles);
478     if (!motion)
479         motion = new QDeclarativeParticleMotionLinear(q);
480
481     int oldCount = particles.count();
482     int removed = 0;
483     int interval = time - lastAdvTime;
484     for (int i = 0; i < particles.count(); ) {
485         QDeclarativeParticle &particle = particles[i];
486         int age = time - particle.birthTime;
487         if (age >= particle.lifeSpan)  {
488             QDeclarativeParticle part = particles.takeAt(i);
489             motion->destroy(part);
490             ++removed;
491         } else {
492             updateOpacity(particle, age);
493             motion->advance(particle, interval);
494             ++i;
495         }
496     }
497
498     if(emissionRate == -1)//Otherwise leave emission to the emission rate
499         while(removed-- && ((count == -1) || particles.count() < count))
500             createParticle(time);
501
502     if (!addParticleTime)
503         addParticleTime = time;
504
505     //Possibly emit new particles
506     if (((count == -1) || particles.count() < count) && emissionRate
507             && !(count==-1 && emissionRate==-1)) {
508         int emissionCount = -1;
509         if (emissionRate != -1){
510             qreal variance = 1.;
511             if (emissionVariance > 0.){
512                 variance += (qreal(qrand())/RAND_MAX) * emissionVariance * (qrand()%2?-1.:1.);
513             }
514             qreal emission = emissionRate * (qreal(interval)/1000.);
515             emission = emission * variance + emissionCarry;
516             double tmpDbl;
517             emissionCarry = modf(emission, &tmpDbl);
518             emissionCount = (int)tmpDbl;
519             emissionCount = qMax(0,emissionCount);
520         }
521         while(((count == -1) || particles.count() < count) &&
522                 (emissionRate==-1 || emissionCount--))
523             createParticle(time);
524     }
525
526     //Deal with emissions from requested bursts
527     for(int i=0; i<bursts.size(); i++){
528         int emission = 0;
529         if(bursts[i].second == -1){
530             emission = bursts[i].first;
531         }else{
532             qreal variance = 1.;
533             if (emissionVariance > 0.){
534                 variance += (qreal(qrand())/RAND_MAX) * emissionVariance * (qrand()%2?-1.:1.);
535             }
536             qreal workingEmission = bursts[i].second * (qreal(interval)/1000.);
537             workingEmission *= variance;
538             emission = (int)workingEmission;
539             emission = qMax(emission, 0);
540         }
541         emission = qMin(emission, bursts[i].first);
542         bursts[i].first -= emission;
543         while(emission--)
544             createParticle(time);
545     }
546     for(int i=bursts.size()-1; i>=0; i--)
547         if(bursts[i].first <= 0)
548             bursts.removeAt(i);
549
550     lastAdvTime = time;
551     paintItem->updateSize();
552     paintItem->update();
553     if (!(oldCount || particles.count()) && (!count || !emissionRate) && bursts.isEmpty()) {
554         lastAdvTime = 0;
555         clock.stop();
556     }
557 }
558
559 void QDeclarativeParticlesPrivate::createParticle(int time)
560 {
561     Q_Q(QDeclarativeParticles);
562     QDeclarativeParticle p(time);
563     p.x = q->x() + q->width() * qreal(qrand()) / RAND_MAX - image.width()/2.0;
564     p.y = q->y() + q->height() * qreal(qrand()) / RAND_MAX - image.height()/2.0;
565     p.lifeSpan = lifeSpan;
566     if (lifeSpanDev)
567         p.lifeSpan += int(lifeSpanDev/2 - lifeSpanDev * qreal(qrand()) / RAND_MAX);
568     p.fadeOutAge = p.lifeSpan - fadeOutDur;
569     if (fadeInDur == 0.) {
570         p.state= QDeclarativeParticle::Solid;
571         p.opacity = 1.0;
572     }
573     qreal a = angle;
574     if (angleDev)
575         a += angleDev/2 - angleDev * qreal(qrand()) / RAND_MAX;
576     if (a > M_PI)
577         a = a - 2 * M_PI;
578     qreal v = velocity;
579     if (velocityDev)
580         v += velocityDev/2 - velocityDev * qreal(qrand()) / RAND_MAX;
581     p.x_velocity = v * fastCos(a);
582     p.y_velocity = v * fastSin(a);
583     particles.append(p);
584     motion->created(particles.last());
585 }
586
587 void QDeclarativeParticlesPrivate::updateOpacity(QDeclarativeParticle &p, int age)
588 {
589     switch (p.state) {
590     case QDeclarativeParticle::FadeIn:
591         if (age <= fadeInDur) {
592             p.opacity = qreal(age) / fadeInDur;
593             break;
594         } else {
595             p.opacity = 1.0;
596             p.state = QDeclarativeParticle::Solid;
597             // Fall through
598         }
599     case QDeclarativeParticle::Solid:
600         if (age <= p.fadeOutAge) {
601             break;
602         } else {
603             p.state = QDeclarativeParticle::FadeOut;
604             // Fall through
605         }
606     case QDeclarativeParticle::FadeOut:
607         p.opacity = qreal(p.lifeSpan - age) / fadeOutDur;
608         break;
609     }
610 }
611
612 /*!
613     \qmlclass Particles QDeclarativeParticles
614     \ingroup qml-particle-elements
615     \since 4.7
616     \brief The Particles object generates and moves particles.
617     \inherits Item
618
619     Particles are available in the \bold{Qt.labs.particles 1.0} module.
620     \e {Elements in the Qt.labs module are not guaranteed to remain compatible
621     in future versions.}
622
623     This element provides preliminary support for particles in QML,
624     and may be heavily changed or removed in later versions.
625
626     The particles created by this object cannot be dealt with
627     directly, they can only be controlled through the parameters of
628     the Particles object. The particles are all the same pixmap,
629     specified by the user.
630
631     The particles are painted relative to the parent of the Particles
632     object.  Moving the Particles object will not move the particles
633     already emitted.
634
635     The below example creates two differently behaving particle
636     sources.  The top one has particles falling from the top like
637     snow, the lower one has particles expelled up like a fountain.
638
639     \qml
640 import QtQuick 1.0
641 import Qt.labs.particles 1.0
642
643 Rectangle {
644     width: 240
645     height: 320
646     color: "black"
647     Particles {
648         y: 0
649         width: parent.width
650         height: 30
651         source: "star.png"
652         lifeSpan: 5000
653         count: 50
654         angle: 70
655         angleDeviation: 36
656         velocity: 30
657         velocityDeviation: 10
658         ParticleMotionWander {
659             xvariance: 30
660             pace: 100
661         }
662     }
663     Particles {
664         y: 300
665         x: 120
666         width: 1
667         height: 1
668         source: "star.png"
669         lifeSpan: 5000
670         count: 200
671         angle: 270
672         angleDeviation: 45
673         velocity: 50
674         velocityDeviation: 30
675         ParticleMotionGravity {
676             yattractor: 1000
677             xattractor: 0
678             acceleration: 25
679         }
680     }
681 }
682     \endqml
683     \image particles.gif
684 */
685
686 QDeclarativeParticles::QDeclarativeParticles(QDeclarativeItem *parent)
687     : QDeclarativeItem(*(new QDeclarativeParticlesPrivate), parent)
688 {
689     Q_D(QDeclarativeParticles);
690     d->init();
691 }
692
693 QDeclarativeParticles::~QDeclarativeParticles()
694 {
695 }
696
697 /*!
698     \qmlproperty string Particles::source
699     This property holds the URL of the particle image.
700 */
701
702 /*!
703     \property QDeclarativeParticles::source
704     \brief the URL of the particle image.
705 */
706 QUrl QDeclarativeParticles::source() const
707 {
708     Q_D(const QDeclarativeParticles);
709     return d->url;
710 }
711
712 void QDeclarativeParticles::imageLoaded()
713 {
714     Q_D(QDeclarativeParticles);
715     if (d->image.isError())
716         qmlInfo(this) << d->image.error();
717     d->paintItem->updateSize();
718     d->paintItem->update();
719 }
720
721 void QDeclarativeParticles::setSource(const QUrl &name)
722 {
723     Q_D(QDeclarativeParticles);
724
725     if ((d->url.isEmpty() == name.isEmpty()) && name == d->url)
726         return;
727
728     if (name.isEmpty()) {
729         d->url = name;
730         d->image.clear(this);
731         d->paintItem->updateSize();
732         d->paintItem->update();
733     } else {
734         d->url = name;
735         Q_ASSERT(!name.isRelative());
736         d->image.load(qmlEngine(this), d->url);
737         if (d->image.isLoading()) {
738             d->image.connectFinished(this, SLOT(imageLoaded()));
739         } else {
740             if (d->image.isError()) 
741                 qmlInfo(this) << d->image.error();
742             //### unify with imageLoaded
743             d->paintItem->updateSize();
744             d->paintItem->update();
745         }
746     }
747     emit sourceChanged();
748 }
749
750 /*!
751     \qmlproperty int Particles::count
752     This property holds the maximum number of particles
753
754     The particles element emits particles until it has count active
755     particles. When this number is reached, new particles are not emitted until
756     some of the current particles reach the end of their lifespan.
757
758     If count is -1 then there is no maximum number of active particles, and
759     particles will be constantly emitted at the rate specified by emissionRate.
760
761     The default value for count is 1.
762
763     If both count and emissionRate are set to -1, nothing will be emitted.
764
765 */
766
767 /*!
768     \property QDeclarativeParticles::count
769     \brief the maximum number of particles
770 */
771 int QDeclarativeParticles::count() const
772 {
773     Q_D(const QDeclarativeParticles);
774     return d->count;
775 }
776
777 void QDeclarativeParticles::setCount(int cnt)
778 {
779     Q_D(QDeclarativeParticles);
780     if (cnt == d->count)
781         return;
782
783     int oldCount = d->count;
784     d->count = cnt;
785     d->addParticleTime = 0;
786     d->addParticleCount = d->particles.count();
787     if (!oldCount && d->clock.state() != QAbstractAnimation::Running && d->count && d->emissionRate) {
788         d->clock.start();
789     }
790     d->paintItem->updateSize();
791     d->paintItem->update();
792     emit countChanged();
793 }
794
795
796 /*!
797     \qmlproperty int Particles::emissionRate
798     This property holds the target number of particles to emit every second.
799
800     The particles element will emit up to emissionRate particles every
801     second. Fewer particles may be emitted per second if the maximum number of
802     particles has been reached.
803
804     If emissionRate is set to -1 there is no limit to the number of
805     particles emitted per second, and particles will be instantly emitted to
806     reach the maximum number of particles specified by count.
807
808     The default value for emissionRate is -1.
809
810     If both count and emissionRate are set to -1, nothing will be emitted.
811 */
812
813 /*!
814     \property QDeclarativeParticles::emissionRate
815     \brief the emission rate of particles
816 */
817 int QDeclarativeParticles::emissionRate() const
818 {
819     Q_D(const QDeclarativeParticles);
820     return d->emissionRate;
821 }
822 void QDeclarativeParticles::setEmissionRate(int er)
823 {
824     Q_D(QDeclarativeParticles);
825     if(er == d->emissionRate)
826         return;
827     d->emissionRate = er;
828     if (d->clock.state() != QAbstractAnimation::Running && d->count && d->emissionRate) {
829             d->clock.start();
830     }
831     emit emissionRateChanged();
832 }
833
834 /*!
835     \qmlproperty real Particles::emissionVariance
836     This property holds how inconsistent the rate of particle emissions are.
837     It is a number between 0 (no variance) and 1 (some variance).
838
839     The expected number of particles emitted per second is emissionRate. If
840     emissionVariance is 0 then particles will be emitted consistently throughout
841     each second to reach that number. If emissionVariance is greater than 0 the
842     rate of particle emission will vary randomly throughout the second, with the
843     consequence that the actual number of particles emitted in one second will
844     vary randomly as well.
845
846     emissionVariance is the maximum deviation from emitting
847     emissionRate particles per second. An emissionVariance of 0 means you should
848     get exactly emissionRate particles emitted per second,
849     and an emissionVariance of 1 means you will get between zero and two times
850     emissionRate particles per second, but you should get emissionRate particles
851     per second on average.
852
853     Note that even with an emissionVariance of 0 there may be some variance due
854     to performance and hardware constraints.
855
856     The default value of emissionVariance is 0.5
857 */
858
859 /*!
860     \property QDeclarativeParticles::emissionVariance
861     \brief how much the particle emission amounts vary per tick
862 */
863
864 qreal QDeclarativeParticles::emissionVariance() const
865 {
866     Q_D(const QDeclarativeParticles);
867     return d->emissionVariance;
868 }
869
870 void QDeclarativeParticles::setEmissionVariance(qreal ev)
871 {
872     Q_D(QDeclarativeParticles);
873     if(d->emissionVariance == ev)
874         return;
875     d->emissionVariance = ev;
876     emit emissionVarianceChanged();
877 }
878
879 /*!
880     \qmlproperty int Particles::lifeSpan
881     \qmlproperty int Particles::lifeSpanDeviation
882
883     These properties describe the life span of each particle.
884
885     The default lifespan for a particle is 1000ms.
886
887     lifeSpanDeviation randomly varies the lifeSpan up to the specified variation.  For
888     example, the following creates particles whose lifeSpan will vary
889     from 150ms to 250ms:
890
891     \qml
892 Particles {
893     source: "star.png"
894     lifeSpan: 200
895     lifeSpanDeviation: 100
896 }
897     \endqml
898 */
899
900 /*!
901     \property QDeclarativeParticles::lifeSpan
902     \brief the life span of each particle.
903
904     Default value is 1000ms.
905
906     \sa QDeclarativeParticles::lifeSpanDeviation
907 */
908 int QDeclarativeParticles::lifeSpan() const
909 {
910     Q_D(const QDeclarativeParticles);
911     return d->lifeSpan;
912 }
913
914 void QDeclarativeParticles::setLifeSpan(int ls)
915 {
916     Q_D(QDeclarativeParticles);
917     if(d->lifeSpan == ls)
918         return;
919     d->lifeSpan = ls;
920     emit lifeSpanChanged();
921 }
922
923 /*!
924     \property QDeclarativeParticles::lifeSpanDeviation
925     \brief the maximum possible deviation from the set lifeSpan.
926
927     Randomly varies the lifeSpan up to the specified variation.  For
928     example, the following creates particles whose lifeSpan will vary
929     from 150ms to 250ms:
930
931 \qml
932 Particles {
933     source: "star.png"
934     lifeSpan: 200
935     lifeSpanDeviation: 100
936 }
937 \endqml
938
939     \sa QDeclarativeParticles::lifeSpan
940 */
941 int QDeclarativeParticles::lifeSpanDeviation() const
942 {
943     Q_D(const QDeclarativeParticles);
944     return d->lifeSpanDev;
945 }
946
947 void QDeclarativeParticles::setLifeSpanDeviation(int dev)
948 {
949     Q_D(QDeclarativeParticles);
950     if(d->lifeSpanDev == dev)
951         return;
952     d->lifeSpanDev = dev;
953     emit lifeSpanDeviationChanged();
954 }
955
956 /*!
957     \qmlproperty int Particles::fadeInDuration
958     \qmlproperty int Particles::fadeOutDuration
959     These properties hold the time taken to fade the particles in and out.
960
961     By default fade in is 200ms and fade out is 300ms.
962 */
963
964 /*!
965     \property QDeclarativeParticles::fadeInDuration
966     \brief the time taken to fade in the particles.
967
968     Default value is 200ms.
969 */
970 int QDeclarativeParticles::fadeInDuration() const
971 {
972     Q_D(const QDeclarativeParticles);
973     return d->fadeInDur;
974 }
975
976 void QDeclarativeParticles::setFadeInDuration(int dur)
977 {
978     Q_D(QDeclarativeParticles);
979     if (dur < 0.0 || dur == d->fadeInDur)
980         return;
981     d->fadeInDur = dur;
982     emit fadeInDurationChanged();
983 }
984
985 /*!
986     \property QDeclarativeParticles::fadeOutDuration
987     \brief the time taken to fade out the particles.
988
989     Default value is 300ms.
990 */
991 int QDeclarativeParticles::fadeOutDuration() const
992 {
993     Q_D(const QDeclarativeParticles);
994     return d->fadeOutDur;
995 }
996
997 void QDeclarativeParticles::setFadeOutDuration(int dur)
998 {
999     Q_D(QDeclarativeParticles);
1000     if (dur < 0.0 || d->fadeOutDur == dur)
1001         return;
1002     d->fadeOutDur = dur;
1003     emit fadeOutDurationChanged();
1004 }
1005
1006 /*!
1007     \qmlproperty real Particles::angle
1008     \qmlproperty real Particles::angleDeviation
1009
1010     These properties control particle direction.
1011
1012     angleDeviation randomly varies the direction up to the specified variation.  For
1013     example, the following creates particles whose initial direction will
1014     vary from 15 degrees to 105 degrees:
1015
1016     \qml
1017 Particles {
1018     source: "star.png"
1019     angle: 60
1020     angleDeviation: 90
1021 }
1022     \endqml
1023 */
1024
1025 /*!
1026     \property QDeclarativeParticles::angle
1027     \brief the initial angle of direction.
1028
1029     \sa QDeclarativeParticles::angleDeviation
1030 */
1031 qreal QDeclarativeParticles::angle() const
1032 {
1033     Q_D(const QDeclarativeParticles);
1034     return d->angle * 180.0 / M_PI;
1035 }
1036
1037 void QDeclarativeParticles::setAngle(qreal angle)
1038 {
1039     Q_D(QDeclarativeParticles);
1040     qreal radAngle = angle * M_PI / 180.0;
1041     if(radAngle == d->angle)
1042         return;
1043     d->angle = radAngle;
1044     emit angleChanged();
1045 }
1046
1047 /*!
1048     \property QDeclarativeParticles::angleDeviation
1049     \brief the maximum possible deviation from the set angle.
1050
1051     Randomly varies the direction up to the specified variation.  For
1052     example, the following creates particles whose initial direction will
1053     vary from 15 degrees to 105 degrees:
1054
1055 \qml
1056 Particles {
1057     source: "star.png"
1058     angle: 60
1059     angleDeviation: 90
1060 }
1061 \endqml
1062
1063     \sa QDeclarativeParticles::angle
1064 */
1065 qreal QDeclarativeParticles::angleDeviation() const
1066 {
1067     Q_D(const QDeclarativeParticles);
1068     return d->angleDev * 180.0 / M_PI;
1069 }
1070
1071 void QDeclarativeParticles::setAngleDeviation(qreal dev)
1072 {
1073     Q_D(QDeclarativeParticles);
1074     qreal radDev = dev * M_PI / 180.0;
1075     if(radDev == d->angleDev)
1076         return;
1077     d->angleDev = radDev;
1078     emit angleDeviationChanged();
1079 }
1080
1081 /*!
1082     \qmlproperty real Particles::velocity
1083     \qmlproperty real Particles::velocityDeviation
1084
1085     These properties control the velocity of the particles.
1086
1087     velocityDeviation randomly varies the velocity up to the specified variation.  For
1088     example, the following creates particles whose initial velocity will
1089     vary from 40 to 60.
1090
1091     \qml
1092 Particles {
1093     source: "star.png"
1094     velocity: 50
1095     velocityDeviation: 20
1096 }
1097     \endqml
1098 */
1099
1100 /*!
1101     \property QDeclarativeParticles::velocity
1102     \brief the initial velocity of the particles.
1103
1104     \sa QDeclarativeParticles::velocityDeviation
1105 */
1106 qreal QDeclarativeParticles::velocity() const
1107 {
1108     Q_D(const QDeclarativeParticles);
1109     return d->velocity * 1000.0;
1110 }
1111
1112 void QDeclarativeParticles::setVelocity(qreal velocity)
1113 {
1114     Q_D(QDeclarativeParticles);
1115     qreal realVel = velocity / 1000.0;
1116     if(realVel == d->velocity)
1117         return;
1118     d->velocity = realVel;
1119     emit velocityChanged();
1120 }
1121
1122 /*!
1123     \property QDeclarativeParticles::velocityDeviation
1124     \brief the maximum possible deviation from the set velocity.
1125
1126     Randomly varies the velocity up to the specified variation.  For
1127     example, the following creates particles whose initial velocity will
1128     vary from 40 to 60.
1129
1130 \qml
1131 Particles {
1132     source: "star.png"
1133     velocity: 50
1134     velocityDeviation: 20
1135 }
1136 \endqml
1137
1138     \sa QDeclarativeParticles::velocity
1139 */
1140 qreal QDeclarativeParticles::velocityDeviation() const
1141 {
1142     Q_D(const QDeclarativeParticles);
1143     return d->velocityDev * 1000.0;
1144 }
1145
1146 void QDeclarativeParticles::setVelocityDeviation(qreal velocity)
1147 {
1148     Q_D(QDeclarativeParticles);
1149     qreal realDev = velocity / 1000.0;
1150     if(realDev == d->velocityDev)
1151         return;
1152     d->velocityDev = realDev;
1153     emit velocityDeviationChanged();
1154 }
1155
1156 /*!
1157     \qmlproperty ParticleMotion Particles::motion
1158     This property sets the type of motion to apply to the particles.
1159
1160     When a particle is created it will have an initial direction and velocity.
1161     The motion of the particle during its lifeSpan is then influenced by the
1162     motion property.
1163
1164     Default motion is ParticleMotionLinear.
1165 */
1166
1167 /*!
1168     \property QDeclarativeParticles::motion
1169     \brief sets the type of motion to apply to the particles.
1170
1171     When a particle is created it will have an initial direction and velocity.
1172     The motion of the particle during its lifeSpan is then influenced by the
1173     motion property.
1174
1175     Default motion is QDeclarativeParticleMotionLinear.
1176 */
1177 QDeclarativeParticleMotion *QDeclarativeParticles::motion() const
1178 {
1179     Q_D(const QDeclarativeParticles);
1180     return d->motion;
1181 }
1182
1183 void QDeclarativeParticles::setMotion(QDeclarativeParticleMotion *motion)
1184 {
1185     Q_D(QDeclarativeParticles);
1186     if (motion == d->motion)
1187         return;
1188     d->motion = motion;
1189     emit motionChanged();
1190 }
1191
1192 /*!
1193     \qmlmethod Particles::burst(int count, int emissionRate)
1194
1195     Initiates a burst of particles.
1196
1197     This method takes two arguments. The first argument is the number
1198     of particles to emit and the second argument is the emissionRate for the
1199     burst. If the second argument is omitted, it is treated as -1. The burst
1200     of particles has a separate emissionRate and count to the normal emission of
1201     particles. The burst uses the same values as normal emission for all other
1202     properties, including emissionVariance.
1203
1204     The normal emission of particles will continue during the burst, however
1205     the particles created by the burst count towards the maximum number used by
1206     normal emission. To avoid this behavior, use two Particles elements.
1207
1208 */
1209 void QDeclarativeParticles::burst(int count, int emissionRate)
1210 {
1211     Q_D(QDeclarativeParticles);
1212     d->bursts << qMakePair(count, emissionRate);
1213     if (d->clock.state() != QAbstractAnimation::Running)
1214         d->clock.start();
1215 }
1216
1217 void QDeclarativeParticlesPainter::updateSize()
1218 {
1219     if (!d->componentComplete)
1220         return;
1221
1222     const int parentX = parentItem()->x();
1223     const int parentY = parentItem()->y();
1224     for (int i = 0; i < d->particles.count(); ++i) {
1225         const QDeclarativeParticle &particle = d->particles.at(i);
1226         if(particle.x > maxX)
1227             maxX = particle.x;
1228         if(particle.x < minX)
1229             minX = particle.x;
1230         if(particle.y > maxY)
1231             maxY = particle.y;
1232         if(particle.y < minY)
1233             minY = particle.y;
1234     }
1235
1236     int myWidth = (int)(maxX-minX+0.5)+d->image.width();
1237     int myX = (int)(minX - parentX);
1238     int myHeight = (int)(maxY-minY+0.5)+d->image.height();
1239     int myY = (int)(minY - parentY);
1240     setWidth(myWidth);
1241     setHeight(myHeight);
1242     setX(myX);
1243     setY(myY);
1244 }
1245
1246 void QDeclarativeParticles::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
1247 {
1248     Q_UNUSED(p);
1249     //painting is done by the ParticlesPainter, so it can have the right size
1250 }
1251
1252 void QDeclarativeParticlesPainter::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
1253 {
1254     if (d->image.isNull() || d->particles.isEmpty())
1255         return;
1256
1257     const int myX = x() + parentItem()->x();
1258     const int myY = y() + parentItem()->y();
1259
1260     QVarLengthArray<QPainter::PixmapFragment, 256> pixmapData;
1261     pixmapData.resize(d->particles.count());
1262
1263     const QRectF sourceRect = d->image.rect();
1264     qreal halfPWidth = sourceRect.width()/2.;
1265     qreal halfPHeight = sourceRect.height()/2.;
1266     for (int i = 0; i < d->particles.count(); ++i) {
1267         const QDeclarativeParticle &particle = d->particles.at(i);
1268         pixmapData[i].x = particle.x - myX + halfPWidth;
1269         pixmapData[i].y = particle.y - myY + halfPHeight;
1270         pixmapData[i].opacity = particle.opacity;
1271
1272         //these never change
1273         pixmapData[i].rotation = 0;
1274         pixmapData[i].scaleX = 1;
1275         pixmapData[i].scaleY = 1;
1276         pixmapData[i].sourceLeft = sourceRect.left();
1277         pixmapData[i].sourceTop = sourceRect.top();
1278         pixmapData[i].width = sourceRect.width();
1279         pixmapData[i].height = sourceRect.height();
1280     }
1281     p->drawPixmapFragments(pixmapData.data(), d->particles.count(), d->image);
1282 }
1283
1284 void QDeclarativeParticles::componentComplete()
1285 {
1286     Q_D(QDeclarativeParticles);
1287     QDeclarativeItem::componentComplete();
1288     if (d->count && d->emissionRate) {
1289         d->paintItem->updateSize();
1290         d->clock.start();
1291     }
1292     if (d->lifeSpanDev > d->lifeSpan)
1293         d->lifeSpanDev = d->lifeSpan;
1294 }
1295
1296 QT_END_NAMESPACE