From 0ef5d89b540be963d1a8131f6625f990a4fe66e8 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 5 Nov 2009 14:03:47 +1000 Subject: Particles cleaned up and placed in in qmlgraphics/ API changes: Removed bool streamIn Added int emissionRate Added int emissionVariance Added void burst(count, emissionRate=-1) While rewriting the internals to accomodate this, all other outstanding particles bugs were believed fixed. Task-number: QT-2392 QT-2391 QT-2390 QT-2406 --- demos/declarative/minehunt/Explosion.qml | 7 +- demos/declarative/samegame/content/BoomBlock.qml | 12 +- doc/src/declarative/advtutorial4.qdoc | 6 +- .../samegame/samegame4/content/BoomBlock.qml | 13 +- src/declarative/QmlChanges.txt | 2 + src/declarative/extra/extra.pri | 2 - src/declarative/extra/qmlgraphicsparticles.cpp | 1134 ----------------- src/declarative/extra/qmlgraphicsparticles_p.h | 230 ---- src/declarative/graphicsitems/graphicsitems.pri | 2 + .../graphicsitems/qmlgraphicsparticles.cpp | 1278 ++++++++++++++++++++ .../graphicsitems/qmlgraphicsparticles_p.h | 252 ++++ .../qmlgraphicsparticles/data/particles.qml | 2 +- .../tst_qmlgraphicsparticles.cpp | 7 +- 13 files changed, 1558 insertions(+), 1389 deletions(-) delete mode 100644 src/declarative/extra/qmlgraphicsparticles.cpp delete mode 100644 src/declarative/extra/qmlgraphicsparticles_p.h create mode 100644 src/declarative/graphicsitems/qmlgraphicsparticles.cpp create mode 100644 src/declarative/graphicsitems/qmlgraphicsparticles_p.h diff --git a/demos/declarative/minehunt/Explosion.qml b/demos/declarative/minehunt/Explosion.qml index a997048..e337c46 100644 --- a/demos/declarative/minehunt/Explosion.qml +++ b/demos/declarative/minehunt/Explosion.qml @@ -16,13 +16,10 @@ Item { velocity: 100 velocityDeviation: 20 z: 100 - opacity: 0 - streamIn: false + opacity: 1 } states: [ State { name: "exploding"; when: explode == true - PropertyChanges { target: particles; count: 200 } - PropertyChanges { target: particles; opacity: 1 } - PropertyChanges { target: particles; emitting: false } // i.e. emit only once + StateChangeScript {script: particles.burst(200); } } ] diff --git a/demos/declarative/samegame/content/BoomBlock.qml b/demos/declarative/samegame/content/BoomBlock.qml index b0c297b..723e62a 100644 --- a/demos/declarative/samegame/content/BoomBlock.qml +++ b/demos/declarative/samegame/content/BoomBlock.qml @@ -26,9 +26,11 @@ Item { id:block } Particles { id: particles - width:1; height:1; anchors.centerIn: parent; opacity: 0 - lifeSpan: 700; lifeSpanDeviation: 600; count:0; streamIn: false - angle: 0; angleDeviation: 360; velocity: 100; velocityDeviation:30 + width:1; height:1; anchors.centerIn: parent; + emissionRate: 0; + lifeSpan: 700; lifeSpanDeviation: 600; + angle: 0; angleDeviation: 360; + velocity: 100; velocityDeviation:30; source: { if(type == 0){ "pics/redStar.png"; @@ -45,9 +47,7 @@ Item { id:block PropertyChanges { target: img; opacity: 1 } }, State{ name: "DeathState"; when: dying == true - PropertyChanges { target: particles; count: 50 } - PropertyChanges { target: particles; opacity: 1 } - PropertyChanges { target: particles; emitting: false } // i.e. emit only once + StateChangeScript { script: particles.burst(50); } PropertyChanges { target: img; opacity: 0 } StateChangeScript { script: block.destroy(1000); } } diff --git a/doc/src/declarative/advtutorial4.qdoc b/doc/src/declarative/advtutorial4.qdoc index e30fe84..5f8c0e9 100644 --- a/doc/src/declarative/advtutorial4.qdoc +++ b/doc/src/declarative/advtutorial4.qdoc @@ -106,9 +106,9 @@ the block, like so: \snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 3 -To fully understand this you'll want to look at the Particles element documentation, but it's important to note that count is set -to zero. -We next extend the 'dying' state, which triggers the particles by setting the count to non-zero. The code for the states now look +To fully understand this you'll want to look at the Particles element documentation, but it's important to note that emissionRate is set +to zero, so that no particles are emitted normally. +We next extend the 'dying' state, which creates a burst of particles by calling the burst method on the particles element. The code for the states now look like this: \snippet declarative/tutorials/samegame/samegame4/content/BoomBlock.qml 4 diff --git a/examples/declarative/tutorials/samegame/samegame4/content/BoomBlock.qml b/examples/declarative/tutorials/samegame/samegame4/content/BoomBlock.qml index 7ad8b07..4c2ba43 100644 --- a/examples/declarative/tutorials/samegame/samegame4/content/BoomBlock.qml +++ b/examples/declarative/tutorials/samegame/samegame4/content/BoomBlock.qml @@ -31,9 +31,11 @@ Item { id:block //![3] Particles { id: particles - width:1; height:1; anchors.centerIn: parent; opacity: 0 - lifeSpan: 700; lifeSpanDeviation: 600; count:0; streamIn: false - angle: 0; angleDeviation: 360; velocity: 100; velocityDeviation:30 + width:1; height:1; anchors.centerIn: parent; + emissionRate: 0; + lifeSpan: 700; lifeSpanDeviation: 600; + angle: 0; angleDeviation: 360; + velocity: 100; velocityDeviation:30; source: { if(type == 0){ "pics/redStar.png"; @@ -52,10 +54,9 @@ Item { id:block PropertyChanges { target: img; opacity: 1 } }, State{ name: "DeathState"; when: dying == true - PropertyChanges { target: particles; count: 50 } - PropertyChanges { target: particles; opacity: 1 } - PropertyChanges { target: particles; emitting: false } // i.e. emit only once + StateChangeScript { script: particles.burst(50); } PropertyChanges { target: img; opacity: 0 } + StateChangeScript { script: block.destroy(1000); } } ] //![4] diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index 465ee9e..c85ef77 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -82,6 +82,7 @@ Loader: add sourceComponent property Loader: add resizeMode property ListView: preferredHighlightBegin, preferredHighlightEnd ListView: strictlyEnforceHighlightRange +Particles: Added emissionRate, emissionVariance and burst() Deletions: Column/VerticalPositioner: lost "margins" property @@ -92,6 +93,7 @@ Flickable: removed "dragMode" property ComponentInstance: removed. Replaced by Loader.sourceComponent ListView: removed currentItemMode. Replaced by highligh range. ListView: removed snapPos. +Particles: removed streamIn. Replaced by emissionRate Other Changes: ids must be lowercase: Text { id: foo }, not Text { id: Foo } diff --git a/src/declarative/extra/extra.pri b/src/declarative/extra/extra.pri index 9d0e760..a81416d 100644 --- a/src/declarative/extra/extra.pri +++ b/src/declarative/extra/extra.pri @@ -4,7 +4,6 @@ SOURCES += \ extra/qmldatetimeformatter.cpp \ extra/qmlgraphicsintegermodel.cpp \ extra/qmlgraphicsanimatedimageitem.cpp \ - extra/qmlgraphicsparticles.cpp \ extra/qmlbehavior.cpp \ extra/qmlfontloader.cpp @@ -15,7 +14,6 @@ HEADERS += \ extra/qmlgraphicsintegermodel_p.h \ extra/qmlgraphicsanimatedimageitem_p.h \ extra/qmlgraphicsanimatedimageitem_p_p.h \ - extra/qmlgraphicsparticles_p.h \ extra/qmlbehavior_p.h \ extra/qmlfontloader_p.h diff --git a/src/declarative/extra/qmlgraphicsparticles.cpp b/src/declarative/extra/qmlgraphicsparticles.cpp deleted file mode 100644 index 0349a4e..0000000 --- a/src/declarative/extra/qmlgraphicsparticles.cpp +++ /dev/null @@ -1,1134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "private/qmlgraphicsitem_p.h" - -#include -#include -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#define M_PI_2 (M_PI / 2.) -#endif -#ifndef INT_MAX -#define INT_MAX 2147483647 -#endif -#include -#include -#include -#include - -#include "qmlgraphicsparticles_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE -#define PI_SQR 9.8696044 -// parabolic approximation -inline qreal fastSin(qreal theta) -{ - const qreal b = 4 / M_PI; - const qreal c = -4 / PI_SQR; - - qreal y = b * theta + c * theta * qAbs(theta); - return y; -} - -inline qreal fastCos(qreal theta) -{ - theta += M_PI_2; - if (theta > M_PI) - theta -= 2 * M_PI; - - return fastSin(theta); -} - -class QmlGraphicsParticle -{ -public: - QmlGraphicsParticle(int time) : lifeSpan(1000), fadeOutAge(800) - , opacity(0), birthTime(time), x_velocity(0), y_velocity(0) - , state(FadeIn), data(0) - { - } - - int lifeSpan; - int fadeOutAge; - qreal x; - qreal y; - qreal opacity; - int birthTime; - qreal x_velocity; - qreal y_velocity; - enum State { FadeIn, Solid, FadeOut }; - State state; - void *data; -}; - -//--------------------------------------------------------------------------- - -QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotion,QmlGraphicsParticleMotion) - -/*! - \class QmlGraphicsParticleMotion - \ingroup group_effects - \brief The QmlGraphicsParticleMotion class is the base class for particle motion. - \internal - - This class causes the particles to remain static. -*/ - -/*! - Constructs a QmlGraphicsParticleMotion with parent object \a parent. -*/ -QmlGraphicsParticleMotion::QmlGraphicsParticleMotion(QObject *parent) : - QObject(parent) -{ -} - -/*! - Move the \a particle to its new position. \a interval is the number of - milliseconds elapsed since it was last moved. -*/ -void QmlGraphicsParticleMotion::advance(QmlGraphicsParticle &particle, int interval) -{ - Q_UNUSED(particle); - Q_UNUSED(interval); -} - -/*! - The \a particle has just been created. Some motion strategies require - additional state information. This can be allocated by this function. -*/ -void QmlGraphicsParticleMotion::created(QmlGraphicsParticle &particle) -{ - Q_UNUSED(particle); -} - -/*! - The \a particle is about to be destroyed. Any additional memory - that has been allocated for the particle should be freed. -*/ -void QmlGraphicsParticleMotion::destroy(QmlGraphicsParticle &particle) -{ - Q_UNUSED(particle); -} - -/*! - \qmlclass ParticleMotionLinear - \brief The ParticleMotionLinear object moves particles linearly. - - \sa Particles -*/ - -/*! - \internal - \class QmlGraphicsParticleMotionLinear - \ingroup group_effects - \brief The QmlGraphicsParticleMotionLinear class moves the particles linearly. -*/ - -QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotionLinear,QmlGraphicsParticleMotionLinear) - -void QmlGraphicsParticleMotionLinear::advance(QmlGraphicsParticle &p, int interval) -{ - p.x += interval * p.x_velocity; - p.y += interval * p.y_velocity; -} - -/*! - \qmlclass ParticleMotionGravity - \brief The ParticleMotionGravity object moves particles towards a point. - - \sa Particles -*/ - -/*! - \internal - \class QmlGraphicsParticleMotionGravity - \ingroup group_effects - \brief The QmlGraphicsParticleMotionGravity class moves the particles towards a point. -*/ - -QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotionGravity,QmlGraphicsParticleMotionGravity) - -/*! - \qmlproperty int ParticleMotionGravity::xattractor - \qmlproperty int ParticleMotionGravity::yattractor - These properties hold the x and y coordinates of the point attracting the particles. -*/ - -/*! - \qmlproperty int ParticleMotionGravity::acceleration - This property holds the acceleration to apply to the particles. -*/ - -/*! - \property QmlGraphicsParticleMotionGravity::xattractor - \brief the x coordinate of the point attracting the particles. -*/ - -/*! - \property QmlGraphicsParticleMotionGravity::yattractor - \brief the y coordinate of the point attracting the particles. -*/ - -/*! - \property QmlGraphicsParticleMotionGravity::acceleration - \brief the acceleration to apply to the particles. -*/ - -void QmlGraphicsParticleMotionGravity::advance(QmlGraphicsParticle &p, int interval) -{ - qreal xdiff = p.x - _xAttr; - qreal ydiff = p.y - _yAttr; - - qreal xcomp = xdiff / (xdiff + ydiff); - qreal ycomp = ydiff / (xdiff + ydiff); - - p.x_velocity += xcomp * _accel * interval; - p.y_velocity += ycomp * _accel * interval; - - p.x += interval * p.x_velocity; - p.y += interval * p.y_velocity; -} - -/*! - \qmlclass ParticleMotionWander - \brief The ParticleMotionWander object moves particles in a somewhat random fashion. - - The particles will continue roughly in the original direction, however will randomly - drift to each side. - - The code below produces an effect similar to falling snow. - - \qml -Rectangle { - width: 240 - height: 320 - color: "black" - - Particles { - y: 0 - width: parent.width - height: 30 - source: "star.png" - lifeSpan: 5000 - count: 50 - angle: 70 - angleDeviation: 36 - velocity: 30 - velocityDeviation: 10 - ParticleMotionWander { - xvariance: 30 - pace: 100 - } - } -} - \endqml - - \sa Particles -*/ - -/*! - \internal - \class QmlGraphicsParticleMotionWander - \ingroup group_effects - \brief The QmlGraphicsParticleMotionWander class moves particles in a somewhat random fashion. - - The particles will continue roughly in the original direction, however will randomly - drift to each side. -*/ - -/*! - \qmlproperty int QmlGraphicsParticleMotionWander::xvariance - \qmlproperty int QmlGraphicsParticleMotionWander::yvariance - - These properties set the amount to wander in the x and y directions. -*/ - -/*! - \qmlproperty int QmlGraphicsParticleMotionWander::pace - This property holds how quickly the paricles will move from side to side. -*/ - -QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotionWander,QmlGraphicsParticleMotionWander) - -void QmlGraphicsParticleMotionWander::advance(QmlGraphicsParticle &p, int interval) -{ - if (!particles) - particles = qobject_cast(parent()); - if (particles) { - Data *d = (Data*)p.data; - if (_xvariance != 0.) { - qreal xdiff = p.x_velocity - d->x_targetV; - if ((xdiff > d->x_peak && d->x_var > 0.0) || (xdiff < -d->x_peak && d->x_var < 0.0)) { - d->x_var = -d->x_var; - d->x_peak = _xvariance + _xvariance * qreal(rand()) / RAND_MAX; - } - p.x_velocity += d->x_var * interval; - } - p.x += interval * p.x_velocity; - - if (_yvariance != 0.) { - qreal ydiff = p.y_velocity - d->y_targetV; - if ((ydiff > d->y_peak && d->y_var > 0.0) || (ydiff < -d->y_peak && d->y_var < 0.0)) { - d->y_var = -d->y_var; - d->y_peak = _yvariance + _yvariance * qreal(rand()) / RAND_MAX; - } - p.y_velocity += d->y_var * interval; - } - p.y += interval * p.y_velocity; - } -} - -void QmlGraphicsParticleMotionWander::created(QmlGraphicsParticle &p) -{ - if (!p.data) { - Data *d = new Data; - p.data = (void*)d; - d->x_targetV = p.x_velocity; - d->y_targetV = p.y_velocity; - d->x_peak = _xvariance; - d->y_peak = _yvariance; - d->x_var = _pace * qreal(rand()) / RAND_MAX / 1000.0; - d->y_var = _pace * qreal(rand()) / RAND_MAX / 1000.0; - } -} - -void QmlGraphicsParticleMotionWander::destroy(QmlGraphicsParticle &p) -{ - if (p.data) - delete (Data*)p.data; -} - -//--------------------------------------------------------------------------- -class QmlGraphicsParticlesPainter : public QmlGraphicsItem -{ -public: - QmlGraphicsParticlesPainter(QmlGraphicsParticlesPrivate *p, QmlGraphicsItem* parent) - : QmlGraphicsItem(parent), d(p) - { - setFlag(QGraphicsItem::ItemHasNoContents, false); - maxX = minX = maxY = minY = 0; - } - - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); - - void updateSize(); - - qreal maxX; - qreal minX; - qreal maxY; - qreal minY; - QmlGraphicsParticlesPrivate* d; -}; - -//--------------------------------------------------------------------------- -class QmlGraphicsParticlesPrivate : public QmlGraphicsItemPrivate -{ - Q_DECLARE_PUBLIC(QmlGraphicsParticles) -public: - QmlGraphicsParticlesPrivate() - : count(1), lifeSpan(1000), lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300) - , angle(0), angleDev(0), velocity(0), velocityDev(0) - , addParticleTime(0), addParticleCount(0), lastAdvTime(0), stream(false), streamDelay(0) - , emitting(true), motion(0), pendingPixmapCache(false), clock(this) - { - } - - ~QmlGraphicsParticlesPrivate() - { - } - - void init() - { - Q_Q(QmlGraphicsParticles); - paintItem = new QmlGraphicsParticlesPainter(this, q); - } - - void tick(int time); - void createParticle(int time); - void updateOpacity(QmlGraphicsParticle &p, int age); - - QUrl url; - QPixmap image; - int count; - int lifeSpan; - int lifeSpanDev; - int fadeInDur; - int fadeOutDur; - qreal angle; - qreal angleDev; - qreal velocity; - qreal velocityDev; - int addParticleTime; - int addParticleCount; - int lastAdvTime; - bool stream; - int streamDelay; - bool emitting; - QmlGraphicsParticleMotion *motion; - QmlGraphicsParticlesPainter *paintItem; - - bool pendingPixmapCache; - - QList particles; - QTickAnimationProxy clock; - -}; - -void QmlGraphicsParticlesPrivate::tick(int time) -{ - Q_Q(QmlGraphicsParticles); - if (!motion) - motion = new QmlGraphicsParticleMotionLinear(q); - - int oldCount = particles.count(); - int removed = 0; - int interval = time - lastAdvTime; - for (int i = 0; i < particles.count(); ) { - QmlGraphicsParticle &particle = particles[i]; - int age = time - particle.birthTime; - if (age >= particle.lifeSpan) { - QmlGraphicsParticle part = particles.takeAt(i); - motion->destroy(part); - ++removed; - } else { - updateOpacity(particle, age); - motion->advance(particle, interval); - ++i; - } - } - - while(removed-- && particles.count() < count && emitting) - createParticle(time); - - if (!addParticleTime) - addParticleTime = time; - - if (particles.count() < count && emitting) { - qreal perc = (lifeSpanDev <= 0)?(1.):(qreal(time - addParticleTime) / qreal(lifeSpanDev)); - int percCount = addParticleCount + (int)perc * (count - addParticleCount); - int streamWidth = -1; - if (stream){ - if (streamDelay > time){ - streamWidth = 0; - }else{ - int missed = time - streamDelay; - qreal streamWidthReal = qreal(count)/qreal(lifeSpan); - if (streamWidthReal < 1){ - streamDelay = time + (int)(1.0/streamWidthReal); - streamWidth = 1; - streamWidth += missed/streamDelay; - }else{ - streamWidth = qRound(streamWidthReal * (time-lastAdvTime)); - } - } - } - while(particles.count() < count && - (!stream || (particles.count() < percCount && streamWidth--))) - createParticle(time); - } - - lastAdvTime = time; - paintItem->updateSize(); - paintItem->update(); - if (!(oldCount || particles.count()) && (!count || !emitting)) { - lastAdvTime = 0; - clock.stop(); - } -} - -void QmlGraphicsParticlesPrivate::createParticle(int time) -{ -#ifdef Q_ENABLE_PERFORMANCE_LOG - QmlPerfTimer x; -#endif - Q_Q(QmlGraphicsParticles); - QmlGraphicsParticle p(time); - p.x = q->x() + q->width() * qreal(rand()) / RAND_MAX - image.width()/2.0; - p.y = q->y() + q->height() * qreal(rand()) / RAND_MAX - image.height()/2.0; - p.lifeSpan = lifeSpan; - if (lifeSpanDev) - p.lifeSpan += int(lifeSpanDev/2 - lifeSpanDev * qreal(rand()) / RAND_MAX); - p.fadeOutAge = p.lifeSpan - fadeOutDur; - if (fadeInDur == 0.) { - p.state= QmlGraphicsParticle::Solid; - p.opacity = 1.0; - } - qreal a = angle; - if (angleDev) - a += angleDev/2 - angleDev * qreal(rand()) / RAND_MAX; - if (a > M_PI) - a = a - 2 * M_PI; - qreal v = velocity; - if (velocityDev) - v += velocityDev/2 - velocityDev * qreal(rand()) / RAND_MAX; - p.x_velocity = v * fastCos(a); - p.y_velocity = v * fastSin(a); - particles.append(p); - motion->created(particles.last()); -} - -void QmlGraphicsParticlesPrivate::updateOpacity(QmlGraphicsParticle &p, int age) -{ - switch (p.state) { - case QmlGraphicsParticle::FadeIn: - if (age <= fadeInDur) { - p.opacity = qreal(age) / fadeInDur; - break; - } else { - p.opacity = 1.0; - p.state = QmlGraphicsParticle::Solid; - // Fall through - } - case QmlGraphicsParticle::Solid: - if (age <= p.fadeOutAge) { - break; - } else { - p.state = QmlGraphicsParticle::FadeOut; - // Fall through - } - case QmlGraphicsParticle::FadeOut: - p.opacity = qreal(p.lifeSpan - age) / fadeOutDur; - break; - } -} - -QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,Particles,QmlGraphicsParticles) - -/*! - \qmlclass Particles - \brief The Particles object generates and moves particles. - \inherits Item - - The particles created by this object cannot be dealt with directly, they can only be controlled through the parameters of the Particles object. The particles are all the same pixmap, specified by the user. - - The particles are painted relative to the parent of the Particles object. Moving the - Particles object will not move the particles already emitted. - - The below example creates two differently behaving particle sources. - The top one has particles falling from the top like snow, - the lower one has particles expelled up like a fountain. - - \qml -Rectangle { - width: 240 - height: 320 - color: "black" - Particles { - y: 0 - width: parent.width - height: 30 - source: "star.png" - lifeSpan: 5000 - count: 50 - angle: 70 - angleDeviation: 36 - velocity: 30 - velocityDeviation: 10 - ParticleMotionWander { - xvariance: 30 - pace: 100 - } - } - Particles { - y: 300 - x: 120 - width: 1 - height: 1 - source: "star.png" - lifeSpan: 5000 - count: 200 - angle: 270 - angleDeviation: 45 - velocity: 50 - velocityDeviation: 30 - ParticleMotionGravity { - yattractor: 1000 - xattractor: 0 - acceleration: 25 - } - } -} - \endqml - \image particles.gif -*/ - -/*! - \internal - \class QmlGraphicsParticles - \ingroup group_effects - \brief The QmlGraphicsParticles class generates and moves particles. -*/ - -QmlGraphicsParticles::QmlGraphicsParticles(QmlGraphicsItem *parent) - : QmlGraphicsItem(*(new QmlGraphicsParticlesPrivate), parent) -{ - Q_D(QmlGraphicsParticles); - d->init(); -} - -QmlGraphicsParticles::QmlGraphicsParticles(QmlGraphicsParticlesPrivate &dd, QmlGraphicsItem *parent) - : QmlGraphicsItem(dd, parent) -{ - Q_D(QmlGraphicsParticles); - d->init(); -} - -QmlGraphicsParticles::~QmlGraphicsParticles() -{ - Q_D(QmlGraphicsParticles); - if (d->pendingPixmapCache) - QmlPixmapCache::cancelGet(d->url, this); -} - -/*! - \qmlproperty string Particles::src - This property holds the URL of the particle image. -*/ - -/*! - \property QmlGraphicsParticles::source - \brief the URL of the particle image. -*/ -QUrl QmlGraphicsParticles::source() const -{ - Q_D(const QmlGraphicsParticles); - return d->url; -} - -void QmlGraphicsParticles::imageLoaded() -{ - Q_D(QmlGraphicsParticles); - d->pendingPixmapCache = false; - QmlPixmapCache::find(d->url, &d->image); - d->paintItem->updateSize(); - d->paintItem->update(); -} - -void QmlGraphicsParticles::setSource(const QUrl &name) -{ - Q_D(QmlGraphicsParticles); - - if ((d->url.isEmpty() == name.isEmpty()) && name == d->url) - return; - - if (d->pendingPixmapCache) { - QmlPixmapCache::cancelGet(d->url, this); - d->pendingPixmapCache = false; - } - if (name.isEmpty()) { - d->url = name; - d->image = QPixmap(); - d->paintItem->updateSize(); - d->paintItem->update(); - } else { - d->url = name; - Q_ASSERT(!name.isRelative()); - QNetworkReply *reply = QmlPixmapCache::get(qmlEngine(this), d->url, &d->image); - if (reply) { - connect(reply, SIGNAL(finished()), this, SLOT(imageLoaded())); - d->pendingPixmapCache = true; - } else { - //### unify with imageLoaded - d->paintItem->updateSize(); - d->paintItem->update(); - } - } -} - -/*! - \qmlproperty int Particles::count - This property holds the target number of particles -*/ - -/*! - \property QmlGraphicsParticles::count - \brief the target number of particles -*/ -int QmlGraphicsParticles::count() const -{ - Q_D(const QmlGraphicsParticles); - return d->count; -} - -void QmlGraphicsParticles::setCount(int cnt) -{ - Q_D(QmlGraphicsParticles); - if (cnt == d->count) - return; - - int oldCount = d->count; - d->count = cnt; - d->addParticleTime = 0; - d->addParticleCount = d->particles.count(); - if (!oldCount && d->clock.state() != QAbstractAnimation::Running && d->count) { - d->clock.start(); - } - d->paintItem->updateSize(); - d->paintItem->update(); -} - -/*! - \qmlproperty int Particles::lifeSpan - \qmlproperty int Particles::lifeSpanDeviation - - These properties describe the life span of each particle. - - The default lifespan for a particle is 1000ms. - - lifeSpanDeviation randomly varies the lifeSpan up to the specified variation. For - example, the following creates particles whose lifeSpan will vary - from 150ms to 250ms: - - \qml -Particles { - source: "star.png" - lifeSpan: 200 - lifeSpanDeviation: 100 -} - \endqml -*/ - -/*! - \property QmlGraphicsParticles::lifeSpan - \brief the life span of each particle. - - Default value is 1000ms. - - \sa QmlGraphicsParticles::lifeSpanDeviation -*/ -int QmlGraphicsParticles::lifeSpan() const -{ - Q_D(const QmlGraphicsParticles); - return d->lifeSpan; -} - -void QmlGraphicsParticles::setLifeSpan(int ls) -{ - Q_D(QmlGraphicsParticles); - d->lifeSpan = ls; -} - -/*! - \property QmlGraphicsParticles::lifeSpanDeviation - \brief the maximum possible deviation from the set lifeSpan. - - Randomly varies the lifeSpan up to the specified variation. For - example, the following creates particles whose lifeSpan will vary - from 150ms to 250ms: - -\qml -Particles { - source: "star.png" - lifeSpan: 200 - lifeSpanDeviation: 100 -} -\endqml - - \sa QmlGraphicsParticles::lifeSpan -*/ -int QmlGraphicsParticles::lifeSpanDeviation() const -{ - Q_D(const QmlGraphicsParticles); - return d->lifeSpanDev; -} - -void QmlGraphicsParticles::setLifeSpanDeviation(int dev) -{ - Q_D(QmlGraphicsParticles); - d->lifeSpanDev = dev; -} - -/*! - \qmlproperty int Particles::fadeInDuration - \qmlproperty int Particles::fadeOutDuration - These properties hold the time taken to fade the particles in and out. - - By default fade in is 200ms and fade out is 300ms. -*/ - -/*! - \property QmlGraphicsParticles::fadeInDuration - \brief the time taken to fade in the particles. - - Default value is 200ms. -*/ -int QmlGraphicsParticles::fadeInDuration() const -{ - Q_D(const QmlGraphicsParticles); - return d->fadeInDur; -} - -void QmlGraphicsParticles::setFadeInDuration(int dur) -{ - Q_D(QmlGraphicsParticles); - if (dur >= 0.0) - d->fadeInDur = dur; -} - -/*! - \property QmlGraphicsParticles::fadeOutDuration - \brief the time taken to fade out the particles. - - Default value is 300ms. -*/ -int QmlGraphicsParticles::fadeOutDuration() const -{ - Q_D(const QmlGraphicsParticles); - return d->fadeOutDur; -} - -void QmlGraphicsParticles::setFadeOutDuration(int dur) -{ - Q_D(QmlGraphicsParticles); - if (dur >= 0.0) - d->fadeOutDur = dur; -} - -/*! - \qmlproperty real Particles::angle - \qmlproperty real Particles::angleDeviation - - These properties control particle direction. - - angleDeviation randomly varies the direction up to the specified variation. For - example, the following creates particles whose initial direction will - vary from 15 degrees to 105 degrees: - - \qml -Particles { - source: "star.png" - angle: 60 - angleDeviation: 90 -} - \endqml -*/ - -/*! - \property QmlGraphicsParticles::angle - \brief the initial angle of direction. - - \sa QmlGraphicsParticles::angleDeviation -*/ -qreal QmlGraphicsParticles::angle() const -{ - Q_D(const QmlGraphicsParticles); - return d->angle * 180.0 / M_PI; -} - -void QmlGraphicsParticles::setAngle(qreal angle) -{ - Q_D(QmlGraphicsParticles); - d->angle = angle * M_PI / 180.0; -} - -/*! - \property QmlGraphicsParticles::angleDeviation - \brief the maximum possible deviation from the set angle. - - Randomly varies the direction up to the specified variation. For - example, the following creates particles whose initial direction will - vary from 15 degrees to 105 degrees: - -\qml -Particles { - source: "star.png" - angle: 60 - angleDeviation: 90 -} -\endqml - - \sa QmlGraphicsParticles::angle -*/ -qreal QmlGraphicsParticles::angleDeviation() const -{ - Q_D(const QmlGraphicsParticles); - return d->angleDev * 180.0 / M_PI; -} - -void QmlGraphicsParticles::setAngleDeviation(qreal dev) -{ - Q_D(QmlGraphicsParticles); - d->angleDev = dev * M_PI / 180.0;; -} - -/*! - \qmlproperty real Particles::velocity - \qmlproperty real Particles::velocityDeviation - - These properties control the velocity of the particles. - - velocityDeviation randomly varies the velocity up to the specified variation. For - example, the following creates particles whose initial velocity will - vary from 40 to 60. - - \qml -Particles { - source: "star.png" - velocity: 50 - velocityDeviation: 20 -} - \endqml -*/ - -/*! - \property QmlGraphicsParticles::velocity - \brief the initial velocity of the particles. - - \sa QmlGraphicsParticles::velocityDeviation -*/ -qreal QmlGraphicsParticles::velocity() const -{ - Q_D(const QmlGraphicsParticles); - return d->velocity * 1000.0; -} - -void QmlGraphicsParticles::setVelocity(qreal velocity) -{ - Q_D(QmlGraphicsParticles); - d->velocity = velocity / 1000.0; -} - -/*! - \property QmlGraphicsParticles::velocityDeviation - \brief the maximum possible deviation from the set velocity. - - Randomly varies the velocity up to the specified variation. For - example, the following creates particles whose initial velocity will - vary from 40 to 60. - -\qml -Particles { - source: "star.png" - velocity: 50 - velocityDeviation: 20 -} -\endqml - - \sa QmlGraphicsParticles::velocity -*/ -qreal QmlGraphicsParticles::velocityDeviation() const -{ - Q_D(const QmlGraphicsParticles); - return d->velocityDev * 1000.0; -} - -void QmlGraphicsParticles::setVelocityDeviation(qreal velocity) -{ - Q_D(QmlGraphicsParticles); - d->velocityDev = velocity / 1000.0; -} - -/*! - \qmlproperty bool Particles::streamIn - This property determines whether the particles stream in at a constant rate - - When stream is set to true the particles will stream in at a constant rate. - Otherwise the particles will appear as a clump. Note that this only affects the - start of the particle effect, variables such as lifespan deviation can cause the - particles to unclump over time. -*/ -/*! - \property QmlGraphicsParticles::streamIn - \brief determines whether the particles stream in at a constant rate - - When stream is set to true the particles will stream in at a constant rate. - Otherwise the particles will appear as a clump. Note that this only affects the - start of the particle effect, variables such as lifespan deviation can cause the - -*/ -//The name may need a rethink -bool QmlGraphicsParticles::streamIn() const -{ - Q_D(const QmlGraphicsParticles); - return d->stream; -} - -void QmlGraphicsParticles::setStreamIn(bool b) -{ - Q_D(QmlGraphicsParticles); - d->stream = b; -} - -/*! - \qmlproperty bool Particles::emitting - This property determines whether new particles are created - - If emitting is set to false no new particles will be created. This means that - when a particle reaches the end of its lifespan it is not replaced. This - property can be used to turn particles on and off with a more natural look. - - The default setting is true. Note that if it initialized to false no particles - will be produced until it is set to true. -*/ -/*! - \property QmlGraphicsParticles::emitting - If emitting is set to false no new particles will be created. This means that - when a particle reaches the end of its lifespan it is not replaced. This - property can be used to turn particles on and off with a more natural look. - - The default setting is true. Note that if it initialized to false no particles - will be produced until it is set to true. -*/ -bool QmlGraphicsParticles::emitting() const -{ - Q_D(const QmlGraphicsParticles); - return d->emitting; -} - -void QmlGraphicsParticles::setEmitting(bool r) -{ - Q_D(QmlGraphicsParticles); - d->emitting = r; - if (d->count && r) - d->clock.start(); -} -/*! - \qmlproperty ParticleMotion Particles::motion - This property sets the type of motion to apply to the particles. - - When a particle is created it will have an initial direction and velocity. - The motion of the particle during its lifeSpan is then influenced by the - motion property. - - Default motion is ParticleMotionLinear. -*/ - -/*! - \property QmlGraphicsParticles::motion - \brief sets the type of motion to apply to the particles. - - When a particle is created it will have an initial direction and velocity. - The motion of the particle during its lifeSpan is then influenced by the - motion property. - - Default motion is QmlGraphicsParticleMotionLinear. -*/ -QmlGraphicsParticleMotion *QmlGraphicsParticles::motion() const -{ - Q_D(const QmlGraphicsParticles); - return d->motion; -} - -void QmlGraphicsParticles::setMotion(QmlGraphicsParticleMotion *motion) -{ - Q_D(QmlGraphicsParticles); - d->motion = motion; -} - -void QmlGraphicsParticlesPainter::updateSize() -{ - if (!isComponentComplete()) - return; - - const int parentX = parentItem()->x(); - const int parentY = parentItem()->y(); - for (int i = 0; i < d->particles.count(); ++i) { - const QmlGraphicsParticle &particle = d->particles.at(i); - if(particle.x > maxX) - maxX = particle.x; - if(particle.x < minX) - minX = particle.x; - if(particle.y > maxY) - maxY = particle.y; - if(particle.y < minY) - minY = particle.y; - } - - int myWidth = (int)(maxX-minX+0.5)+d->image.width(); - int myX = (int)(minX - parentX); - int myHeight = (int)(maxY-minY+0.5)+d->image.height(); - int myY = (int)(minY - parentY); - setWidth(myWidth); - setHeight(myHeight); - setX(myX); - setY(myY); -} - -void QmlGraphicsParticles::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) -{ - Q_UNUSED(p); - //painting is done by the ParticlesPainter, so it can have the right size -} - -void QmlGraphicsParticlesPainter::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) -{ - if (d->image.isNull() || d->particles.isEmpty()) - return; - - const int myX = x() + parentItem()->x(); - const int myY = y() + parentItem()->y(); - - static QVarLengthArray pixmapData; - if (pixmapData.count() < d->particles.count()) - pixmapData.resize(d->particles.count()); - - const QRectF sourceRect = d->image.rect(); - for (int i = 0; i < d->particles.count(); ++i) { - const QmlGraphicsParticle &particle = d->particles.at(i); - pixmapData[i].point = QPointF(particle.x - myX, particle.y - myY); - pixmapData[i].opacity = particle.opacity; - - //these never change - pixmapData[i].rotation = 0; - pixmapData[i].scaleX = 1; - pixmapData[i].scaleY = 1; - pixmapData[i].source = sourceRect; - } - qDrawPixmaps(p, pixmapData.data(), d->particles.count(), d->image); -} - -void QmlGraphicsParticles::componentComplete() -{ - Q_D(QmlGraphicsParticles); - QmlGraphicsItem::componentComplete(); - if (d->count) { - d->paintItem->updateSize(); - d->clock.start(); - } - if (d->lifeSpanDev > d->lifeSpan) - d->lifeSpanDev = d->lifeSpan; -} - -QT_END_NAMESPACE diff --git a/src/declarative/extra/qmlgraphicsparticles_p.h b/src/declarative/extra/qmlgraphicsparticles_p.h deleted file mode 100644 index 9eca762..0000000 --- a/src/declarative/extra/qmlgraphicsparticles_p.h +++ /dev/null @@ -1,230 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLGRAPHICSPARTICLES_H -#define QMLGRAPHICSPARTICLES_H - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QmlGraphicsParticle; -class QmlGraphicsParticles; -class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotion : public QObject -{ - Q_OBJECT -public: - QmlGraphicsParticleMotion(QObject *parent=0); - - virtual void advance(QmlGraphicsParticle &, int interval); - virtual void created(QmlGraphicsParticle &); - virtual void destroy(QmlGraphicsParticle &); -}; - -class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotionLinear : public QmlGraphicsParticleMotion -{ - Q_OBJECT -public: - QmlGraphicsParticleMotionLinear(QObject *parent=0) - : QmlGraphicsParticleMotion(parent) {} - - virtual void advance(QmlGraphicsParticle &, int interval); -}; - -class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotionGravity : public QmlGraphicsParticleMotion -{ - Q_OBJECT - - Q_PROPERTY(int xattractor READ xAttractor WRITE setXAttractor) - Q_PROPERTY(int yattractor READ yAttractor WRITE setYAttractor) - Q_PROPERTY(int acceleration READ acceleration WRITE setAcceleration) -public: - QmlGraphicsParticleMotionGravity(QObject *parent=0) - : QmlGraphicsParticleMotion(parent), _xAttr(0), _yAttr(0), _accel(0.00005) {} - - int xAttractor() const { return _xAttr; } - void setXAttractor(int x) { _xAttr = x; } - - int yAttractor() const { return _yAttr; } - void setYAttractor(int y) { _yAttr = y; } - - int acceleration() const { return int(_accel * 1000000); } - void setAcceleration(int accel) { _accel = qreal(accel)/1000000.0; } - - virtual void advance(QmlGraphicsParticle &, int interval); - -private: - int _xAttr; - int _yAttr; - qreal _accel; -}; - -class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotionWander : public QmlGraphicsParticleMotion -{ - Q_OBJECT -public: - QmlGraphicsParticleMotionWander() - : QmlGraphicsParticleMotion(), particles(0), _xvariance(0), _yvariance(0) {} - - virtual void advance(QmlGraphicsParticle &, int interval); - virtual void created(QmlGraphicsParticle &); - virtual void destroy(QmlGraphicsParticle &); - - struct Data { - qreal x_targetV; - qreal y_targetV; - qreal x_peak; - qreal y_peak; - qreal x_var; - qreal y_var; - }; - - Q_PROPERTY(int xvariance READ xVariance WRITE setXVariance) - int xVariance() const { return int(_xvariance * 1000); } - void setXVariance(int var) { _xvariance = var / 1000.0; } - - Q_PROPERTY(int yvariance READ yVariance WRITE setYVariance) - int yVariance() const { return int(_yvariance * 1000); } - void setYVariance(int var) { _yvariance = var / 1000.0; } - - Q_PROPERTY(int pace READ pace WRITE setPace) - int pace() const { return int(_pace * 1000); } - void setPace(int pace) { _pace = pace / 1000.0; } - -private: - QmlGraphicsParticles *particles; - qreal _xvariance; - qreal _yvariance; - qreal _pace; -}; - -class QmlGraphicsParticlesPrivate; -class Q_DECLARATIVE_EXPORT QmlGraphicsParticles : public QmlGraphicsItem -{ - Q_OBJECT - - Q_PROPERTY(QUrl source READ source WRITE setSource) - Q_PROPERTY(int count READ count WRITE setCount) - Q_PROPERTY(int lifeSpan READ lifeSpan WRITE setLifeSpan) - Q_PROPERTY(int lifeSpanDeviation READ lifeSpanDeviation WRITE setLifeSpanDeviation) - Q_PROPERTY(int fadeInDuration READ fadeInDuration WRITE setFadeInDuration) - Q_PROPERTY(int fadeOutDuration READ fadeOutDuration WRITE setFadeOutDuration) - Q_PROPERTY(qreal angle READ angle WRITE setAngle) - Q_PROPERTY(qreal angleDeviation READ angleDeviation WRITE setAngleDeviation) - Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity) - Q_PROPERTY(qreal velocityDeviation READ velocityDeviation WRITE setVelocityDeviation) - Q_PROPERTY(bool streamIn READ streamIn WRITE setStreamIn) - Q_PROPERTY(bool emitting READ emitting WRITE setEmitting) - Q_PROPERTY(QmlGraphicsParticleMotion *motion READ motion WRITE setMotion) - Q_CLASSINFO("DefaultProperty", "motion") - -public: - QmlGraphicsParticles(QmlGraphicsItem *parent=0); - ~QmlGraphicsParticles(); - - QUrl source() const; - void setSource(const QUrl &); - - int count() const; - void setCount(int cnt); - - int lifeSpan() const; - void setLifeSpan(int); - - int lifeSpanDeviation() const; - void setLifeSpanDeviation(int); - - int fadeInDuration() const; - void setFadeInDuration(int); - - int fadeOutDuration() const; - void setFadeOutDuration(int); - - qreal angle() const; - void setAngle(qreal); - - qreal angleDeviation() const; - void setAngleDeviation(qreal); - - qreal velocity() const; - void setVelocity(qreal); - - qreal velocityDeviation() const; - void setVelocityDeviation(qreal); - - bool streamIn() const; - void setStreamIn(bool); - - bool emitting() const; - void setEmitting(bool); - - QmlGraphicsParticleMotion *motion() const; - void setMotion(QmlGraphicsParticleMotion *); - - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); - -protected: - virtual void componentComplete(); - QmlGraphicsParticles(QmlGraphicsParticlesPrivate &dd, QmlGraphicsItem *parent); - -private Q_SLOTS: - void imageLoaded(); - -private: - Q_DISABLE_COPY(QmlGraphicsParticles) - Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QmlGraphicsParticles) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QmlGraphicsParticleMotion) -QML_DECLARE_TYPE(QmlGraphicsParticleMotionLinear) -QML_DECLARE_TYPE(QmlGraphicsParticleMotionGravity) -QML_DECLARE_TYPE(QmlGraphicsParticleMotionWander) -QML_DECLARE_TYPE(QmlGraphicsParticles) - -QT_END_HEADER - -#endif diff --git a/src/declarative/graphicsitems/graphicsitems.pri b/src/declarative/graphicsitems/graphicsitems.pri index cf71451..3c4e39a 100644 --- a/src/declarative/graphicsitems/graphicsitems.pri +++ b/src/declarative/graphicsitems/graphicsitems.pri @@ -42,6 +42,7 @@ HEADERS += \ graphicsitems/qmlgraphicsvisualitemmodel_p.h \ graphicsitems/qmlgraphicslistview_p.h \ graphicsitems/qmlgraphicsgraphicsobjectcontainer_p.h \ + graphicsitems/qmlgraphicsparticles_p.h \ graphicsitems/qmlgraphicslayoutitem_p.h \ graphicsitems/qmlgraphicseffects.cpp @@ -72,6 +73,7 @@ SOURCES += \ graphicsitems/qmlgraphicsvisualitemmodel.cpp \ graphicsitems/qmlgraphicslistview.cpp \ graphicsitems/qmlgraphicsgraphicsobjectcontainer.cpp \ + graphicsitems/qmlgraphicsparticles.cpp \ graphicsitems/qmlgraphicslayoutitem.cpp \ contains(QT_CONFIG, webkit) { diff --git a/src/declarative/graphicsitems/qmlgraphicsparticles.cpp b/src/declarative/graphicsitems/qmlgraphicsparticles.cpp new file mode 100644 index 0000000..d906fd3 --- /dev/null +++ b/src/declarative/graphicsitems/qmlgraphicsparticles.cpp @@ -0,0 +1,1278 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qmlgraphicsitem_p.h" + +#include +#include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#define M_PI_2 (M_PI / 2.) +#endif +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif +#include +#include +#include +#include + +#include "qmlgraphicsparticles_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE +#define PI_SQR 9.8696044 +// parabolic approximation +inline qreal fastSin(qreal theta) +{ + const qreal b = 4 / M_PI; + const qreal c = -4 / PI_SQR; + + qreal y = b * theta + c * theta * qAbs(theta); + return y; +} + +inline qreal fastCos(qreal theta) +{ + theta += M_PI_2; + if (theta > M_PI) + theta -= 2 * M_PI; + + return fastSin(theta); +} + +class QmlGraphicsParticle +{ +public: + QmlGraphicsParticle(int time) : lifeSpan(1000), fadeOutAge(800) + , opacity(0), birthTime(time), x_velocity(0), y_velocity(0) + , state(FadeIn), data(0) + { + } + + int lifeSpan; + int fadeOutAge; + qreal x; + qreal y; + qreal opacity; + int birthTime; + qreal x_velocity; + qreal y_velocity; + enum State { FadeIn, Solid, FadeOut }; + State state; + void *data; +}; + +//--------------------------------------------------------------------------- + +QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotion,QmlGraphicsParticleMotion) + +/*! + \class QmlGraphicsParticleMotion + \ingroup group_effects + \brief The QmlGraphicsParticleMotion class is the base class for particle motion. + \internal + + This class causes the particles to remain static. +*/ + +/*! + Constructs a QmlGraphicsParticleMotion with parent object \a parent. +*/ +QmlGraphicsParticleMotion::QmlGraphicsParticleMotion(QObject *parent) : + QObject(parent) +{ +} + +/*! + Move the \a particle to its new position. \a interval is the number of + milliseconds elapsed since it was last moved. +*/ +void QmlGraphicsParticleMotion::advance(QmlGraphicsParticle &particle, int interval) +{ + Q_UNUSED(particle); + Q_UNUSED(interval); +} + +/*! + The \a particle has just been created. Some motion strategies require + additional state information. This can be allocated by this function. +*/ +void QmlGraphicsParticleMotion::created(QmlGraphicsParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + The \a particle is about to be destroyed. Any additional memory + that has been allocated for the particle should be freed. +*/ +void QmlGraphicsParticleMotion::destroy(QmlGraphicsParticle &particle) +{ + Q_UNUSED(particle); +} + +/*! + \qmlclass ParticleMotionLinear + \brief The ParticleMotionLinear object moves particles linearly. + + \sa Particles +*/ + +/*! + \internal + \class QmlGraphicsParticleMotionLinear + \ingroup group_effects + \brief The QmlGraphicsParticleMotionLinear class moves the particles linearly. +*/ + +QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotionLinear,QmlGraphicsParticleMotionLinear) + +void QmlGraphicsParticleMotionLinear::advance(QmlGraphicsParticle &p, int interval) +{ + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionGravity + \brief The ParticleMotionGravity object moves particles towards a point. + + \sa Particles +*/ + +/*! + \internal + \class QmlGraphicsParticleMotionGravity + \ingroup group_effects + \brief The QmlGraphicsParticleMotionGravity class moves the particles towards a point. +*/ + +QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotionGravity,QmlGraphicsParticleMotionGravity) + +/*! + \qmlproperty int ParticleMotionGravity::xattractor + \qmlproperty int ParticleMotionGravity::yattractor + These properties hold the x and y coordinates of the point attracting the particles. +*/ + +/*! + \qmlproperty int ParticleMotionGravity::acceleration + This property holds the acceleration to apply to the particles. +*/ + +/*! + \property QmlGraphicsParticleMotionGravity::xattractor + \brief the x coordinate of the point attracting the particles. +*/ + +/*! + \property QmlGraphicsParticleMotionGravity::yattractor + \brief the y coordinate of the point attracting the particles. +*/ + +/*! + \property QmlGraphicsParticleMotionGravity::acceleration + \brief the acceleration to apply to the particles. +*/ + +void QmlGraphicsParticleMotionGravity::advance(QmlGraphicsParticle &p, int interval) +{ + qreal xdiff = p.x - _xAttr; + qreal ydiff = p.y - _yAttr; + + qreal xcomp = xdiff / (xdiff + ydiff); + qreal ycomp = ydiff / (xdiff + ydiff); + + p.x_velocity += xcomp * _accel * interval; + p.y_velocity += ycomp * _accel * interval; + + p.x += interval * p.x_velocity; + p.y += interval * p.y_velocity; +} + +/*! + \qmlclass ParticleMotionWander + \brief The ParticleMotionWander object moves particles in a somewhat random fashion. + + The particles will continue roughly in the original direction, however will randomly + drift to each side. + + The code below produces an effect similar to falling snow. + + \qml +Rectangle { + width: 240 + height: 320 + color: "black" + + Particles { + y: 0 + width: parent.width + height: 30 + source: "star.png" + lifeSpan: 5000 + count: 50 + angle: 70 + angleDeviation: 36 + velocity: 30 + velocityDeviation: 10 + ParticleMotionWander { + xvariance: 30 + pace: 100 + } + } +} + \endqml + + \sa Particles +*/ + +/*! + \internal + \class QmlGraphicsParticleMotionWander + \ingroup group_effects + \brief The QmlGraphicsParticleMotionWander class moves particles in a somewhat random fashion. + + The particles will continue roughly in the original direction, however will randomly + drift to each side. +*/ + +/*! + \qmlproperty int QmlGraphicsParticleMotionWander::xvariance + \qmlproperty int QmlGraphicsParticleMotionWander::yvariance + + These properties set the amount to wander in the x and y directions. +*/ + +/*! + \qmlproperty int QmlGraphicsParticleMotionWander::pace + This property holds how quickly the paricles will move from side to side. +*/ + +QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ParticleMotionWander,QmlGraphicsParticleMotionWander) + +void QmlGraphicsParticleMotionWander::advance(QmlGraphicsParticle &p, int interval) +{ + if (!particles) + particles = qobject_cast(parent()); + if (particles) { + Data *d = (Data*)p.data; + if (_xvariance != 0.) { + qreal xdiff = p.x_velocity - d->x_targetV; + if ((xdiff > d->x_peak && d->x_var > 0.0) || (xdiff < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = _xvariance + _xvariance * qreal(rand()) / RAND_MAX; + } + p.x_velocity += d->x_var * interval; + } + p.x += interval * p.x_velocity; + + if (_yvariance != 0.) { + qreal ydiff = p.y_velocity - d->y_targetV; + if ((ydiff > d->y_peak && d->y_var > 0.0) || (ydiff < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = _yvariance + _yvariance * qreal(rand()) / RAND_MAX; + } + p.y_velocity += d->y_var * interval; + } + p.y += interval * p.y_velocity; + } +} + +void QmlGraphicsParticleMotionWander::created(QmlGraphicsParticle &p) +{ + if (!p.data) { + Data *d = new Data; + p.data = (void*)d; + d->x_targetV = p.x_velocity; + d->y_targetV = p.y_velocity; + d->x_peak = _xvariance; + d->y_peak = _yvariance; + d->x_var = _pace * qreal(rand()) / RAND_MAX / 1000.0; + d->y_var = _pace * qreal(rand()) / RAND_MAX / 1000.0; + } +} + +void QmlGraphicsParticleMotionWander::destroy(QmlGraphicsParticle &p) +{ + if (p.data) + delete (Data*)p.data; +} + +//--------------------------------------------------------------------------- +class QmlGraphicsParticlesPainter : public QmlGraphicsItem +{ +public: + QmlGraphicsParticlesPainter(QmlGraphicsParticlesPrivate *p, QmlGraphicsItem* parent) + : QmlGraphicsItem(parent), d(p) + { + setFlag(QGraphicsItem::ItemHasNoContents, false); + maxX = minX = maxY = minY = 0; + } + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + void updateSize(); + + qreal maxX; + qreal minX; + qreal maxY; + qreal minY; + QmlGraphicsParticlesPrivate* d; +}; + +//--------------------------------------------------------------------------- +class QmlGraphicsParticlesPrivate : public QmlGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QmlGraphicsParticles) +public: + QmlGraphicsParticlesPrivate() + : count(1), emissionRate(-1), emissionVariance(0.5), lifeSpan(1000) + , lifeSpanDev(1000), fadeInDur(200), fadeOutDur(300) + , angle(0), angleDev(0), velocity(0), velocityDev(0), emissionCarry(0.) + , addParticleTime(0), addParticleCount(0), lastAdvTime(0) + , emitting(true), motion(0), pendingPixmapCache(false), clock(this) + { + } + + ~QmlGraphicsParticlesPrivate() + { + } + + void init() + { + Q_Q(QmlGraphicsParticles); + paintItem = new QmlGraphicsParticlesPainter(this, q); + } + + void tick(int time); + void createParticle(int time); + void updateOpacity(QmlGraphicsParticle &p, int age); + + QUrl url; + QPixmap image; + int count; + int emissionRate; + qreal emissionVariance; + int lifeSpan; + int lifeSpanDev; + int fadeInDur; + int fadeOutDur; + qreal angle; + qreal angleDev; + qreal velocity; + qreal velocityDev; + qreal emissionCarry; + int addParticleTime; + int addParticleCount; + int lastAdvTime; + bool emitting; + QmlGraphicsParticleMotion *motion; + QmlGraphicsParticlesPainter *paintItem; + + bool pendingPixmapCache; + + QList > bursts;//countLeft, emissionRate pairs + QList particles; + QTickAnimationProxy clock; + +}; + +void QmlGraphicsParticlesPrivate::tick(int time) +{ + Q_Q(QmlGraphicsParticles); + if (!motion) + motion = new QmlGraphicsParticleMotionLinear(q); + + int oldCount = particles.count(); + int removed = 0; + int interval = time - lastAdvTime; + for (int i = 0; i < particles.count(); ) { + QmlGraphicsParticle &particle = particles[i]; + int age = time - particle.birthTime; + if (age >= particle.lifeSpan) { + QmlGraphicsParticle part = particles.takeAt(i); + motion->destroy(part); + ++removed; + } else { + updateOpacity(particle, age); + motion->advance(particle, interval); + ++i; + } + } + + if(emissionRate == -1)//Otherwise leave emission to the emission rate + while(removed-- && ((count == -1) || particles.count() < count) && emitting) + createParticle(time); + + if (!addParticleTime) + addParticleTime = time; + + //Possibly emit new particles + if (((count == -1) || particles.count() < count) && emitting + && !(count==-1 && emissionRate==-1)) { + int emissionCount = -1; + if (emissionRate != -1){ + qreal variance = 1.; + if (emissionVariance > 0.){ + variance += (qreal(rand())/RAND_MAX) * emissionVariance * (rand()%2?-1.:1.); + } + qreal emission = emissionRate * (qreal(interval)/1000.); + emission = emission * variance + emissionCarry; + double tmpDbl; + emissionCarry = modf(emission, &tmpDbl); + emissionCount = (int)tmpDbl; + emissionCount = qMax(0,emissionCount); + } + while(((count == -1) || particles.count() < count) && + (emissionRate==-1 || emissionCount--)) + createParticle(time); + } + + //Deal with emissions from requested bursts + for(int i=0; i 0.){ + variance += (qreal(rand())/RAND_MAX) * emissionVariance * (rand()%2?-1.:1.); + } + qreal workingEmission = bursts[i].second * (qreal(interval)/1000.); + workingEmission *= variance; + emission = (int)workingEmission; + emission = qMax(emission, 0); + } + emission = qMin(emission, bursts[i].first); + bursts[i].first -= emission; + while(emission--) + createParticle(time); + } + for(int i=bursts.size()-1; i>=0; i--) + if(bursts[i].first <= 0) + bursts.removeAt(i); + + lastAdvTime = time; + paintItem->updateSize(); + paintItem->update(); + if (!(oldCount || particles.count()) && (!count || !emitting) && bursts.isEmpty()) { + lastAdvTime = 0; + clock.stop(); + } +} + +void QmlGraphicsParticlesPrivate::createParticle(int time) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QmlPerfTimer x; +#endif + Q_Q(QmlGraphicsParticles); + QmlGraphicsParticle p(time); + p.x = q->x() + q->width() * qreal(rand()) / RAND_MAX - image.width()/2.0; + p.y = q->y() + q->height() * qreal(rand()) / RAND_MAX - image.height()/2.0; + p.lifeSpan = lifeSpan; + if (lifeSpanDev) + p.lifeSpan += int(lifeSpanDev/2 - lifeSpanDev * qreal(rand()) / RAND_MAX); + p.fadeOutAge = p.lifeSpan - fadeOutDur; + if (fadeInDur == 0.) { + p.state= QmlGraphicsParticle::Solid; + p.opacity = 1.0; + } + qreal a = angle; + if (angleDev) + a += angleDev/2 - angleDev * qreal(rand()) / RAND_MAX; + if (a > M_PI) + a = a - 2 * M_PI; + qreal v = velocity; + if (velocityDev) + v += velocityDev/2 - velocityDev * qreal(rand()) / RAND_MAX; + p.x_velocity = v * fastCos(a); + p.y_velocity = v * fastSin(a); + particles.append(p); + motion->created(particles.last()); +} + +void QmlGraphicsParticlesPrivate::updateOpacity(QmlGraphicsParticle &p, int age) +{ + switch (p.state) { + case QmlGraphicsParticle::FadeIn: + if (age <= fadeInDur) { + p.opacity = qreal(age) / fadeInDur; + break; + } else { + p.opacity = 1.0; + p.state = QmlGraphicsParticle::Solid; + // Fall through + } + case QmlGraphicsParticle::Solid: + if (age <= p.fadeOutAge) { + break; + } else { + p.state = QmlGraphicsParticle::FadeOut; + // Fall through + } + case QmlGraphicsParticle::FadeOut: + p.opacity = qreal(p.lifeSpan - age) / fadeOutDur; + break; + } +} + +QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,Particles,QmlGraphicsParticles) + +/*! + \qmlclass Particles + \brief The Particles object generates and moves particles. + \inherits Item + + This element provides preliminary support for particles in QML, and may be heavily changed or removed in later versions. + + The particles created by this object cannot be dealt with directly, they can only be controlled through the parameters of the Particles object. The particles are all the same pixmap, specified by the user. + + The particles are painted relative to the parent of the Particles object. Moving the + Particles object will not move the particles already emitted. + + The below example creates two differently behaving particle sources. + The top one has particles falling from the top like snow, + the lower one has particles expelled up like a fountain. + + \qml +Rectangle { + width: 240 + height: 320 + color: "black" + Particles { + y: 0 + width: parent.width + height: 30 + source: "star.png" + lifeSpan: 5000 + count: 50 + angle: 70 + angleDeviation: 36 + velocity: 30 + velocityDeviation: 10 + ParticleMotionWander { + xvariance: 30 + pace: 100 + } + } + Particles { + y: 300 + x: 120 + width: 1 + height: 1 + source: "star.png" + lifeSpan: 5000 + count: 200 + angle: 270 + angleDeviation: 45 + velocity: 50 + velocityDeviation: 30 + ParticleMotionGravity { + yattractor: 1000 + xattractor: 0 + acceleration: 25 + } + } +} + \endqml + \image particles.gif +*/ + +/*! + \internal + \class QmlGraphicsParticles + \ingroup group_effects + \brief The QmlGraphicsParticles class generates and moves particles. +*/ + +QmlGraphicsParticles::QmlGraphicsParticles(QmlGraphicsItem *parent) + : QmlGraphicsItem(*(new QmlGraphicsParticlesPrivate), parent) +{ + Q_D(QmlGraphicsParticles); + d->init(); +} + +QmlGraphicsParticles::QmlGraphicsParticles(QmlGraphicsParticlesPrivate &dd, QmlGraphicsItem *parent) + : QmlGraphicsItem(dd, parent) +{ + Q_D(QmlGraphicsParticles); + d->init(); +} + +QmlGraphicsParticles::~QmlGraphicsParticles() +{ + Q_D(QmlGraphicsParticles); + if (d->pendingPixmapCache) + QmlPixmapCache::cancelGet(d->url, this); +} + +/*! + \qmlproperty string Particles::src + This property holds the URL of the particle image. +*/ + +/*! + \property QmlGraphicsParticles::source + \brief the URL of the particle image. +*/ +QUrl QmlGraphicsParticles::source() const +{ + Q_D(const QmlGraphicsParticles); + return d->url; +} + +void QmlGraphicsParticles::imageLoaded() +{ + Q_D(QmlGraphicsParticles); + d->pendingPixmapCache = false; + QmlPixmapCache::find(d->url, &d->image); + d->paintItem->updateSize(); + d->paintItem->update(); +} + +void QmlGraphicsParticles::setSource(const QUrl &name) +{ + Q_D(QmlGraphicsParticles); + + if ((d->url.isEmpty() == name.isEmpty()) && name == d->url) + return; + + if (d->pendingPixmapCache) { + QmlPixmapCache::cancelGet(d->url, this); + d->pendingPixmapCache = false; + } + if (name.isEmpty()) { + d->url = name; + d->image = QPixmap(); + d->paintItem->updateSize(); + d->paintItem->update(); + } else { + d->url = name; + Q_ASSERT(!name.isRelative()); + QNetworkReply *reply = QmlPixmapCache::get(qmlEngine(this), d->url, &d->image); + if (reply) { + connect(reply, SIGNAL(finished()), this, SLOT(imageLoaded())); + d->pendingPixmapCache = true; + } else { + //### unify with imageLoaded + d->paintItem->updateSize(); + d->paintItem->update(); + } + } + emit sourceChanged(); +} + +/*! + \qmlproperty int Particles::count + This property holds the maximum number of particles + + The particles element emits particles until it has count active + particles. When this number is reached, new particles are not emitted until + some of the current particles reach theend of their lifespan. + + If count is -1 then there is no maximum number of active particles, and + particles will be constantly emitted at the rate specified by emissionRate. + + If both count and emissionRate are set to -1, nothing will be emitted. + +*/ + +/*! + \property QmlGraphicsParticles::count + \brief the maximum number of particles +*/ +int QmlGraphicsParticles::count() const +{ + Q_D(const QmlGraphicsParticles); + return d->count; +} + +void QmlGraphicsParticles::setCount(int cnt) +{ + Q_D(QmlGraphicsParticles); + if (cnt == d->count) + return; + + int oldCount = d->count; + d->count = cnt; + d->addParticleTime = 0; + d->addParticleCount = d->particles.count(); + if (!oldCount && d->clock.state() != QAbstractAnimation::Running && d->count && d->emitting) { + d->clock.start(); + } + d->paintItem->updateSize(); + d->paintItem->update(); + emit countChanged(); +} + + +/*! + \qmlproperty int Particles::emissionRate + This property holds the target number of particles to emit every second. + + The particles element will emit up to emissionRate particles every + second. Fewer particles may be emitted per second if the maximum number of + particles has been reached. + + If emissionRate is set to -1 there is no limit to the number of + particles emitted per second, and particles will be instantly emitted to + reach the maximum number of particles specified by count. + + The default value for emissionRate is -1. + + If both count and emissionRate are set to -1, nothing will be emitted. +*/ + +/*! + \property QmlGraphicsParticles::emissionRate + \brief the emission rate of particles +*/ +int QmlGraphicsParticles::emissionRate() const +{ + Q_D(const QmlGraphicsParticles); + return d->emissionRate; +} +void QmlGraphicsParticles::setEmissionRate(int er) +{ + Q_D(QmlGraphicsParticles); + if(er == d->emissionRate) + return; + d->emissionRate = er; + emit emissionRateChanged(); +} + +/*! + \qmlproperty qreal Particles::emissionVariance + This property holds how inconsistent the rate of particle emissions are. + It is a number between 0 (no variance) and 1 (some variance). + + The expected number of particles emitted per second is emissionRate. If + emissionVariance is 0 then particles will be emitted consistently throughout + each second to reach that number. If emissionVariance is greater than 0 the + rate of particle emission will vary randomly throughout the second, with the + consequence that the actual number of particles emitted in one second will + vary randomly as well. + + emissionVariance is the maximum deviation from emitting + emissionRate particles per second. An emissionVariance of 0 means you should + get exactly emissionRate particles emitted per second, + and an emissionVariance of 1 means you will get between zero and two times + emissionRate particles per second, but you should get emissionRate particles + per second on average. + + Note that even with an emissionVariance of 0 there may be some variance due + to performance and hardware constraints. + + The default value of emissionVariance is 0.5 +*/ + +/*! + \property QmlGraphicsParticles::emissionVariance + \brief how much the particle emission amounts vary per tick +*/ + +qreal QmlGraphicsParticles::emissionVariance() const +{ + Q_D(const QmlGraphicsParticles); + return d->emissionVariance; +} + +void QmlGraphicsParticles::setEmissionVariance(qreal ev) +{ + Q_D(QmlGraphicsParticles); + if(d->emissionVariance == ev) + return; + d->emissionVariance = ev; + emit emissionVarianceChanged(); +} + +/*! + \qmlproperty int Particles::lifeSpan + \qmlproperty int Particles::lifeSpanDeviation + + These properties describe the life span of each particle. + + The default lifespan for a particle is 1000ms. + + lifeSpanDeviation randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + + \qml +Particles { + source: "star.png" + lifeSpan: 200 + lifeSpanDeviation: 100 +} + \endqml +*/ + +/*! + \property QmlGraphicsParticles::lifeSpan + \brief the life span of each particle. + + Default value is 1000ms. + + \sa QmlGraphicsParticles::lifeSpanDeviation +*/ +int QmlGraphicsParticles::lifeSpan() const +{ + Q_D(const QmlGraphicsParticles); + return d->lifeSpan; +} + +void QmlGraphicsParticles::setLifeSpan(int ls) +{ + Q_D(QmlGraphicsParticles); + if(d->lifeSpan == ls) + return; + d->lifeSpan = ls; + emit lifeSpanChanged(); +} + +/*! + \property QmlGraphicsParticles::lifeSpanDeviation + \brief the maximum possible deviation from the set lifeSpan. + + Randomly varies the lifeSpan up to the specified variation. For + example, the following creates particles whose lifeSpan will vary + from 150ms to 250ms: + +\qml +Particles { + source: "star.png" + lifeSpan: 200 + lifeSpanDeviation: 100 +} +\endqml + + \sa QmlGraphicsParticles::lifeSpan +*/ +int QmlGraphicsParticles::lifeSpanDeviation() const +{ + Q_D(const QmlGraphicsParticles); + return d->lifeSpanDev; +} + +void QmlGraphicsParticles::setLifeSpanDeviation(int dev) +{ + Q_D(QmlGraphicsParticles); + if(d->lifeSpanDev == dev) + return; + d->lifeSpanDev = dev; + emit lifeSpanDeviationChanged(); +} + +/*! + \qmlproperty int Particles::fadeInDuration + \qmlproperty int Particles::fadeOutDuration + These properties hold the time taken to fade the particles in and out. + + By default fade in is 200ms and fade out is 300ms. +*/ + +/*! + \property QmlGraphicsParticles::fadeInDuration + \brief the time taken to fade in the particles. + + Default value is 200ms. +*/ +int QmlGraphicsParticles::fadeInDuration() const +{ + Q_D(const QmlGraphicsParticles); + return d->fadeInDur; +} + +void QmlGraphicsParticles::setFadeInDuration(int dur) +{ + Q_D(QmlGraphicsParticles); + if (dur < 0.0 || dur == d->fadeInDur) + return; + d->fadeInDur = dur; + emit fadeInDurationChanged(); +} + +/*! + \property QmlGraphicsParticles::fadeOutDuration + \brief the time taken to fade out the particles. + + Default value is 300ms. +*/ +int QmlGraphicsParticles::fadeOutDuration() const +{ + Q_D(const QmlGraphicsParticles); + return d->fadeOutDur; +} + +void QmlGraphicsParticles::setFadeOutDuration(int dur) +{ + Q_D(QmlGraphicsParticles); + if (dur < 0.0 || d->fadeOutDur == dur) + return; + d->fadeOutDur = dur; + emit fadeOutDurationChanged(); +} + +/*! + \qmlproperty real Particles::angle + \qmlproperty real Particles::angleDeviation + + These properties control particle direction. + + angleDeviation randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + + \qml +Particles { + source: "star.png" + angle: 60 + angleDeviation: 90 +} + \endqml +*/ + +/*! + \property QmlGraphicsParticles::angle + \brief the initial angle of direction. + + \sa QmlGraphicsParticles::angleDeviation +*/ +qreal QmlGraphicsParticles::angle() const +{ + Q_D(const QmlGraphicsParticles); + return d->angle * 180.0 / M_PI; +} + +void QmlGraphicsParticles::setAngle(qreal angle) +{ + Q_D(QmlGraphicsParticles); + qreal radAngle = angle * M_PI / 180.0; + if(radAngle == d->angle) + return; + d->angle = radAngle; + emit angleChanged(); +} + +/*! + \property QmlGraphicsParticles::angleDeviation + \brief the maximum possible deviation from the set angle. + + Randomly varies the direction up to the specified variation. For + example, the following creates particles whose initial direction will + vary from 15 degrees to 105 degrees: + +\qml +Particles { + source: "star.png" + angle: 60 + angleDeviation: 90 +} +\endqml + + \sa QmlGraphicsParticles::angle +*/ +qreal QmlGraphicsParticles::angleDeviation() const +{ + Q_D(const QmlGraphicsParticles); + return d->angleDev * 180.0 / M_PI; +} + +void QmlGraphicsParticles::setAngleDeviation(qreal dev) +{ + Q_D(QmlGraphicsParticles); + qreal radDev = dev * M_PI / 180.0; + if(radDev == d->angleDev) + return; + d->angleDev = radDev; + emit angleDeviationChanged(); +} + +/*! + \qmlproperty real Particles::velocity + \qmlproperty real Particles::velocityDeviation + + These properties control the velocity of the particles. + + velocityDeviation randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + + \qml +Particles { + source: "star.png" + velocity: 50 + velocityDeviation: 20 +} + \endqml +*/ + +/*! + \property QmlGraphicsParticles::velocity + \brief the initial velocity of the particles. + + \sa QmlGraphicsParticles::velocityDeviation +*/ +qreal QmlGraphicsParticles::velocity() const +{ + Q_D(const QmlGraphicsParticles); + return d->velocity * 1000.0; +} + +void QmlGraphicsParticles::setVelocity(qreal velocity) +{ + Q_D(QmlGraphicsParticles); + qreal realVel = velocity / 1000.0; + if(realVel == d->velocity) + return; + d->velocity = realVel; + emit velocityChanged(); +} + +/*! + \property QmlGraphicsParticles::velocityDeviation + \brief the maximum possible deviation from the set velocity. + + Randomly varies the velocity up to the specified variation. For + example, the following creates particles whose initial velocity will + vary from 40 to 60. + +\qml +Particles { + source: "star.png" + velocity: 50 + velocityDeviation: 20 +} +\endqml + + \sa QmlGraphicsParticles::velocity +*/ +qreal QmlGraphicsParticles::velocityDeviation() const +{ + Q_D(const QmlGraphicsParticles); + return d->velocityDev * 1000.0; +} + +void QmlGraphicsParticles::setVelocityDeviation(qreal velocity) +{ + Q_D(QmlGraphicsParticles); + qreal realDev = velocity / 1000.0; + if(realDev == d->velocityDev) + return; + d->velocityDev = realDev; + emit velocityDeviationChanged(); +} + +/*! + \qmlproperty bool Particles::emitting + This property determines whether new particles are created + + If emitting is set to false no new particles will be created. This means that + when a particle reaches the end of its lifespan it is not replaced. This + property can be used to turn particles on and off with a more natural look. + + The default setting is true. Note that if it initialized to false no particles + will be produced until it is set to true. +*/ +/*! + \property QmlGraphicsParticles::emitting + If emitting is set to false no new particles will be created. This means that + when a particle reaches the end of its lifespan it is not replaced. This + property can be used to turn particles on and off with a more natural look. + + The default setting is true. Note that if it initialized to false no particles + will be produced until it is set to true. +*/ +bool QmlGraphicsParticles::emitting() const +{ + Q_D(const QmlGraphicsParticles); + return d->emitting; +} + +void QmlGraphicsParticles::setEmitting(bool r) +{ + Q_D(QmlGraphicsParticles); + if(d->emitting == r) + return; + d->emitting = r; + if (d->count && r) + d->clock.start(); + emit emittingChanged(); +} +/*! + \qmlproperty ParticleMotion Particles::motion + This property sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is ParticleMotionLinear. +*/ + +/*! + \property QmlGraphicsParticles::motion + \brief sets the type of motion to apply to the particles. + + When a particle is created it will have an initial direction and velocity. + The motion of the particle during its lifeSpan is then influenced by the + motion property. + + Default motion is QmlGraphicsParticleMotionLinear. +*/ +QmlGraphicsParticleMotion *QmlGraphicsParticles::motion() const +{ + Q_D(const QmlGraphicsParticles); + return d->motion; +} + +void QmlGraphicsParticles::setMotion(QmlGraphicsParticleMotion *motion) +{ + Q_D(QmlGraphicsParticles); + d->motion = motion; +} + +/*! + \qmlmethod Particles::burst + + Initiates a burst of particles. + + This method takes two arguments. The first argument is the number + of particles to emit and the second argument is the emissionRate for the + burst. If the second argument is omitted, it is treated as -1. The burst + of particles has a separate emissionRate and count to the normal emission of + particles. The burst uses the same values as normal emission for all other + properties, including emissionVariance and emitting. + + The normal emission of particles will continue during the burst, however + the particles created by the burst count towards the maximum number used by + normal emission. To avoid this behavior, use two Particles elements. + +*/ +void QmlGraphicsParticles::burst(int count, int emissionRate) +{ + Q_D(QmlGraphicsParticles); + d->bursts << qMakePair(count, emissionRate); + if (d->clock.state() != QAbstractAnimation::Running && d->emitting) + d->clock.start(); +} + +void QmlGraphicsParticlesPainter::updateSize() +{ + if (!isComponentComplete()) + return; + + const int parentX = parentItem()->x(); + const int parentY = parentItem()->y(); + for (int i = 0; i < d->particles.count(); ++i) { + const QmlGraphicsParticle &particle = d->particles.at(i); + if(particle.x > maxX) + maxX = particle.x; + if(particle.x < minX) + minX = particle.x; + if(particle.y > maxY) + maxY = particle.y; + if(particle.y < minY) + minY = particle.y; + } + + int myWidth = (int)(maxX-minX+0.5)+d->image.width(); + int myX = (int)(minX - parentX); + int myHeight = (int)(maxY-minY+0.5)+d->image.height(); + int myY = (int)(minY - parentY); + setWidth(myWidth); + setHeight(myHeight); + setX(myX); + setY(myY); +} + +void QmlGraphicsParticles::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_UNUSED(p); + //painting is done by the ParticlesPainter, so it can have the right size +} + +void QmlGraphicsParticlesPainter::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (d->image.isNull() || d->particles.isEmpty()) + return; + + const int myX = x() + parentItem()->x(); + const int myY = y() + parentItem()->y(); + + QVarLengthArray pixmapData; + pixmapData.resize(d->particles.count()); + + const QRectF sourceRect = d->image.rect(); + qreal halfPWidth = sourceRect.width()/2.; + qreal halfPHeight = sourceRect.height()/2.; + for (int i = 0; i < d->particles.count(); ++i) { + const QmlGraphicsParticle &particle = d->particles.at(i); + pixmapData[i].point = QPointF(particle.x - myX + halfPWidth, particle.y - myY + halfPHeight); + pixmapData[i].opacity = particle.opacity; + + //these never change + pixmapData[i].rotation = 0; + pixmapData[i].scaleX = 1; + pixmapData[i].scaleY = 1; + pixmapData[i].source = sourceRect; + } + qDrawPixmaps(p, pixmapData.data(), d->particles.count(), d->image); +} + +void QmlGraphicsParticles::componentComplete() +{ + Q_D(QmlGraphicsParticles); + QmlGraphicsItem::componentComplete(); + if (d->count) { + d->paintItem->updateSize(); + d->clock.start(); + } + if (d->lifeSpanDev > d->lifeSpan) + d->lifeSpanDev = d->lifeSpan; +} + +QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qmlgraphicsparticles_p.h b/src/declarative/graphicsitems/qmlgraphicsparticles_p.h new file mode 100644 index 0000000..851edd7 --- /dev/null +++ b/src/declarative/graphicsitems/qmlgraphicsparticles_p.h @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLGRAPHICSPARTICLES_H +#define QMLGRAPHICSPARTICLES_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QmlGraphicsParticle; +class QmlGraphicsParticles; +class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotion : public QObject +{ + Q_OBJECT +public: + QmlGraphicsParticleMotion(QObject *parent=0); + + virtual void advance(QmlGraphicsParticle &, int interval); + virtual void created(QmlGraphicsParticle &); + virtual void destroy(QmlGraphicsParticle &); +}; + +class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotionLinear : public QmlGraphicsParticleMotion +{ + Q_OBJECT +public: + QmlGraphicsParticleMotionLinear(QObject *parent=0) + : QmlGraphicsParticleMotion(parent) {} + + virtual void advance(QmlGraphicsParticle &, int interval); +}; + +class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotionGravity : public QmlGraphicsParticleMotion +{ + Q_OBJECT + + Q_PROPERTY(int xattractor READ xAttractor WRITE setXAttractor) + Q_PROPERTY(int yattractor READ yAttractor WRITE setYAttractor) + Q_PROPERTY(int acceleration READ acceleration WRITE setAcceleration) +public: + QmlGraphicsParticleMotionGravity(QObject *parent=0) + : QmlGraphicsParticleMotion(parent), _xAttr(0), _yAttr(0), _accel(0.00005) {} + + int xAttractor() const { return _xAttr; } + void setXAttractor(int x) { _xAttr = x; } + + int yAttractor() const { return _yAttr; } + void setYAttractor(int y) { _yAttr = y; } + + int acceleration() const { return int(_accel * 1000000); } + void setAcceleration(int accel) { _accel = qreal(accel)/1000000.0; } + + virtual void advance(QmlGraphicsParticle &, int interval); + +private: + int _xAttr; + int _yAttr; + qreal _accel; +}; + +class Q_DECLARATIVE_EXPORT QmlGraphicsParticleMotionWander : public QmlGraphicsParticleMotion +{ + Q_OBJECT +public: + QmlGraphicsParticleMotionWander() + : QmlGraphicsParticleMotion(), particles(0), _xvariance(0), _yvariance(0) {} + + virtual void advance(QmlGraphicsParticle &, int interval); + virtual void created(QmlGraphicsParticle &); + virtual void destroy(QmlGraphicsParticle &); + + struct Data { + qreal x_targetV; + qreal y_targetV; + qreal x_peak; + qreal y_peak; + qreal x_var; + qreal y_var; + }; + + Q_PROPERTY(int xvariance READ xVariance WRITE setXVariance) + int xVariance() const { return int(_xvariance * 1000); } + void setXVariance(int var) { _xvariance = var / 1000.0; } + + Q_PROPERTY(int yvariance READ yVariance WRITE setYVariance) + int yVariance() const { return int(_yvariance * 1000); } + void setYVariance(int var) { _yvariance = var / 1000.0; } + + Q_PROPERTY(int pace READ pace WRITE setPace) + int pace() const { return int(_pace * 1000); } + void setPace(int pace) { _pace = pace / 1000.0; } + +private: + QmlGraphicsParticles *particles; + qreal _xvariance; + qreal _yvariance; + qreal _pace; +}; + +class QmlGraphicsParticlesPrivate; +class Q_DECLARATIVE_EXPORT QmlGraphicsParticles : public QmlGraphicsItem +{ + Q_OBJECT + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) + Q_PROPERTY(int emissionRate READ emissionRate WRITE setEmissionRate NOTIFY emissionRateChanged) + Q_PROPERTY(qreal emissionVariance READ emissionVariance WRITE setEmissionVariance NOTIFY emissionVarianceChanged) + Q_PROPERTY(int lifeSpan READ lifeSpan WRITE setLifeSpan NOTIFY lifeSpanChanged) + Q_PROPERTY(int lifeSpanDeviation READ lifeSpanDeviation WRITE setLifeSpanDeviation NOTIFY lifeSpanDeviationChanged) + Q_PROPERTY(int fadeInDuration READ fadeInDuration WRITE setFadeInDuration NOTIFY fadeInDurationChanged) + Q_PROPERTY(int fadeOutDuration READ fadeOutDuration WRITE setFadeOutDuration NOTIFY fadeOutDurationChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(qreal angleDeviation READ angleDeviation WRITE setAngleDeviation NOTIFY angleDeviationChanged) + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(qreal velocityDeviation READ velocityDeviation WRITE setVelocityDeviation NOTIFY velocityDeviationChanged) + Q_PROPERTY(bool emitting READ emitting WRITE setEmitting NOTIFY emittingChanged) + Q_PROPERTY(QmlGraphicsParticleMotion *motion READ motion WRITE setMotion) + Q_CLASSINFO("DefaultProperty", "motion") + +public: + QmlGraphicsParticles(QmlGraphicsItem *parent=0); + ~QmlGraphicsParticles(); + + QUrl source() const; + void setSource(const QUrl &); + + int count() const; + void setCount(int cnt); + + int emissionRate() const; + void setEmissionRate(int); + + qreal emissionVariance() const; + void setEmissionVariance(qreal); + + int lifeSpan() const; + void setLifeSpan(int); + + int lifeSpanDeviation() const; + void setLifeSpanDeviation(int); + + int fadeInDuration() const; + void setFadeInDuration(int); + + int fadeOutDuration() const; + void setFadeOutDuration(int); + + qreal angle() const; + void setAngle(qreal); + + qreal angleDeviation() const; + void setAngleDeviation(qreal); + + qreal velocity() const; + void setVelocity(qreal); + + qreal velocityDeviation() const; + void setVelocityDeviation(qreal); + + bool emitting() const; + void setEmitting(bool); + + QmlGraphicsParticleMotion *motion() const; + void setMotion(QmlGraphicsParticleMotion *); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +public Q_SLOTS: + void burst(int count, int emissionRate=-1); + +protected: + virtual void componentComplete(); + QmlGraphicsParticles(QmlGraphicsParticlesPrivate &dd, QmlGraphicsItem *parent); + +Q_SIGNALS: + void sourceChanged(); + void countChanged(); + void emissionRateChanged(); + void emissionVarianceChanged(); + void lifeSpanChanged(); + void lifeSpanDeviationChanged(); + void fadeInDurationChanged(); + void fadeOutDurationChanged(); + void angleChanged(); + void angleDeviationChanged(); + void velocityChanged(); + void velocityDeviationChanged(); + void emittingChanged(); + +private Q_SLOTS: + void imageLoaded(); + +private: + Q_DISABLE_COPY(QmlGraphicsParticles) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QmlGraphicsParticles) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QmlGraphicsParticleMotion) +QML_DECLARE_TYPE(QmlGraphicsParticleMotionLinear) +QML_DECLARE_TYPE(QmlGraphicsParticleMotionGravity) +QML_DECLARE_TYPE(QmlGraphicsParticleMotionWander) +QML_DECLARE_TYPE(QmlGraphicsParticles) + +QT_END_HEADER + +#endif diff --git a/tests/auto/declarative/qmlgraphicsparticles/data/particles.qml b/tests/auto/declarative/qmlgraphicsparticles/data/particles.qml index dccd2c7..c58927e 100644 --- a/tests/auto/declarative/qmlgraphicsparticles/data/particles.qml +++ b/tests/auto/declarative/qmlgraphicsparticles/data/particles.qml @@ -8,7 +8,7 @@ Rectangle{ objectName: "particles" width:1; height:1; anchors.centerIn: parent; opacity: 1 lifeSpan: 100; lifeSpanDeviation: 20; count:1000; - fadeInDuration: 20; fadeOutDuration: 20; + fadeInDuration: 20; fadeOutDuration: 20; count: -1; emissionRate: 1000 angle: 0; angleDeviation: 360; velocity: 500; velocityDeviation:30 source: "particle.png" } diff --git a/tests/auto/declarative/qmlgraphicsparticles/tst_qmlgraphicsparticles.cpp b/tests/auto/declarative/qmlgraphicsparticles/tst_qmlgraphicsparticles.cpp index e50437a..ed68eaf 100644 --- a/tests/auto/declarative/qmlgraphicsparticles/tst_qmlgraphicsparticles.cpp +++ b/tests/auto/declarative/qmlgraphicsparticles/tst_qmlgraphicsparticles.cpp @@ -91,8 +91,11 @@ void tst_QmlGraphicsParticles::properties() particles->setVelocityDeviation(100.0); QCOMPARE(particles->velocityDeviation(), 100.0); - particles->setEmitting(false); - QCOMPARE(particles->emitting(), false); + particles->setEmissionVariance(0.5); + QCOMPARE(particles->emissionVariance(),0.5); + + particles->setEmissionRate(12); + QCOMPARE(particles->emissionRate(), 12); } void tst_QmlGraphicsParticles::runs() -- cgit v0.12