aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/particles
diff options
context:
space:
mode:
authorKent Hansen <kent.hansen@nokia.com>2011-11-23 15:14:07 +0100
committerQt by Nokia <qt-info@nokia.com>2011-12-02 14:18:20 +0100
commit6c8378eaf1edbbefe6aaa3672b0127816a004fd7 (patch)
tree8ee08fb447e052f7a7a685fbeaaa04f04ea60126 /src/quick/particles
parente01219b77b1e889e70437635905d7ff820568e23 (diff)
Say hello to QtQuick module
This change moves the QtQuick 2 types and C++ API (including SceneGraph) to a new module (AKA library), QtQuick. 99% of this change is moving files from src/declarative to src/quick, and from tests/auto/declarative to tests/auto/qtquick2. The loading of QtQuick 2 ("import QtQuick 2.0") is now delegated to a plugin, src/imports/qtquick2, just like it's done for QtQuick 1. All tools, examples, and tests that use QtQuick C++ API have gotten "QT += quick" or "QT += quick-private" added to their .pro file. A few additional internal QtDeclarative classes had to be exported (via Q_DECLARATIVE_PRIVATE_EXPORT) since they're needed by the QtQuick 2 implementation. The old header locations (e.g. QtDeclarative/qquickitem.h) will still be supported for some time, but will produce compile-time warnings. (To avoid the QtQuick implementation using the compatibility headers (since QtDeclarative's includepath comes first), a few include statements were modified, e.g. from "#include <qsgnode.h>" to "#include <QtQuick/qsgnode.h>".) There's a change in qtbase that automatically adds QtQuick to the module list if QtDeclarative is used. Together with the compatibility headers, this should help reduce the migration pain for existing projects. In theory, simply getting an existing QtDeclarative-based project to compile and link shouldn't require any changes for now -- but porting to the new scheme is of course recommended, and will eventually become mandatory. Task-number: QTBUG-22889 Reviewed-by: Lars Knoll <lars.knoll@nokia.com> Change-Id: Ia52be9373172ba2f37e7623231ecb060316c96a7 Reviewed-by: Kent Hansen <kent.hansen@nokia.com> Reviewed-by: Sergio Ahumada <sergio.ahumada@nokia.com>
Diffstat (limited to 'src/quick/particles')
-rw-r--r--src/quick/particles/particleresources/noise.pngbin0 -> 19477 bytes
-rw-r--r--src/quick/particles/particles.pri69
-rw-r--r--src/quick/particles/particles.qrc5
-rw-r--r--src/quick/particles/qquickage.cpp112
-rw-r--r--src/quick/particles/qquickage_p.h99
-rw-r--r--src/quick/particles/qquickangledirection.cpp116
-rw-r--r--src/quick/particles/qquickangledirection_p.h131
-rw-r--r--src/quick/particles/qquickcumulativedirection.cpp70
-rw-r--r--src/quick/particles/qquickcumulativedirection_p.h67
-rw-r--r--src/quick/particles/qquickcustomaffector.cpp211
-rw-r--r--src/quick/particles/qquickcustomaffector_p.h162
-rw-r--r--src/quick/particles/qquickcustomparticle.cpp596
-rw-r--r--src/quick/particles/qquickcustomparticle_p.h119
-rw-r--r--src/quick/particles/qquickdirection.cpp64
-rw-r--r--src/quick/particles/qquickdirection_p.h68
-rw-r--r--src/quick/particles/qquickellipseextruder.cpp80
-rw-r--r--src/quick/particles/qquickellipseextruder_p.h83
-rw-r--r--src/quick/particles/qquickfriction.cpp111
-rw-r--r--src/quick/particles/qquickfriction_p.h101
-rw-r--r--src/quick/particles/qquickgravity.cpp97
-rw-r--r--src/quick/particles/qquickgravity_p.h115
-rw-r--r--src/quick/particles/qquickgroupgoal.cpp112
-rw-r--r--src/quick/particles/qquickgroupgoal_p.h100
-rw-r--r--src/quick/particles/qquickimageparticle.cpp1804
-rw-r--r--src/quick/particles/qquickimageparticle_p.h427
-rw-r--r--src/quick/particles/qquickitemparticle.cpp269
-rw-r--r--src/quick/particles/qquickitemparticle_p.h137
-rw-r--r--src/quick/particles/qquicklineextruder.cpp84
-rw-r--r--src/quick/particles/qquicklineextruder_p.h77
-rw-r--r--src/quick/particles/qquickmaskextruder.cpp113
-rw-r--r--src/quick/particles/qquickmaskextruder_p.h95
-rw-r--r--src/quick/particles/qquickparticleaffector.cpp279
-rw-r--r--src/quick/particles/qquickparticleaffector_p.h199
-rw-r--r--src/quick/particles/qquickparticleemitter.cpp501
-rw-r--r--src/quick/particles/qquickparticleemitter_p.h349
-rw-r--r--src/quick/particles/qquickparticleextruder.cpp70
-rw-r--r--src/quick/particles/qquickparticleextruder_p.h71
-rw-r--r--src/quick/particles/qquickparticlegroup.cpp143
-rw-r--r--src/quick/particles/qquickparticlegroup_p.h112
-rw-r--r--src/quick/particles/qquickparticlepainter.cpp150
-rw-r--r--src/quick/particles/qquickparticlepainter_p.h129
-rw-r--r--src/quick/particles/qquickparticlesmodule.cpp120
-rw-r--r--src/quick/particles/qquickparticlesmodule_p.h61
-rw-r--r--src/quick/particles/qquickparticlesystem.cpp1106
-rw-r--r--src/quick/particles/qquickparticlesystem_p.h378
-rw-r--r--src/quick/particles/qquickpointattractor.cpp162
-rw-r--r--src/quick/particles/qquickpointattractor_p.h167
-rw-r--r--src/quick/particles/qquickpointdirection.cpp85
-rw-r--r--src/quick/particles/qquickpointdirection_p.h133
-rw-r--r--src/quick/particles/qquickrectangleextruder.cpp86
-rw-r--r--src/quick/particles/qquickrectangleextruder_p.h86
-rw-r--r--src/quick/particles/qquickspritegoal.cpp153
-rw-r--r--src/quick/particles/qquickspritegoal_p.h123
-rw-r--r--src/quick/particles/qquicktargetdirection.cpp131
-rw-r--r--src/quick/particles/qquicktargetdirection_p.h189
-rw-r--r--src/quick/particles/qquicktrailemitter.cpp285
-rw-r--r--src/quick/particles/qquicktrailemitter_p.h168
-rw-r--r--src/quick/particles/qquickturbulence.cpp205
-rw-r--r--src/quick/particles/qquickturbulence_p.h116
-rw-r--r--src/quick/particles/qquickv8particledata.cpp503
-rw-r--r--src/quick/particles/qquickv8particledata_p.h67
-rw-r--r--src/quick/particles/qquickwander.cpp180
-rw-r--r--src/quick/particles/qquickwander_p.h158
63 files changed, 12359 insertions, 0 deletions
diff --git a/src/quick/particles/particleresources/noise.png b/src/quick/particles/particleresources/noise.png
new file mode 100644
index 0000000000..3c723e1a5a
--- /dev/null
+++ b/src/quick/particles/particleresources/noise.png
Binary files differ
diff --git a/src/quick/particles/particles.pri b/src/quick/particles/particles.pri
new file mode 100644
index 0000000000..3a40a3b911
--- /dev/null
+++ b/src/quick/particles/particles.pri
@@ -0,0 +1,69 @@
+HEADERS += \
+ $$PWD/qquickangledirection_p.h \
+ $$PWD/qquickcustomparticle_p.h \
+ $$PWD/qquickcustomaffector_p.h \
+ $$PWD/qquickellipseextruder_p.h \
+ $$PWD/qquicktrailemitter_p.h \
+ $$PWD/qquickfriction_p.h \
+ $$PWD/qquickgravity_p.h \
+ $$PWD/qquickimageparticle_p.h \
+ $$PWD/qquickitemparticle_p.h \
+ $$PWD/qquickage_p.h \
+ $$PWD/qquicklineextruder_p.h \
+ $$PWD/qquickmaskextruder_p.h \
+ $$PWD/qquickparticleaffector_p.h \
+ $$PWD/qquickparticleemitter_p.h \
+ $$PWD/qquickparticleextruder_p.h \
+ $$PWD/qquickparticlepainter_p.h \
+ $$PWD/qquickparticlesmodule_p.h \
+ $$PWD/qquickparticlesystem_p.h \
+ $$PWD/qquickpointattractor_p.h \
+ $$PWD/qquickpointdirection_p.h \
+ $$PWD/qquickspritegoal_p.h \
+ $$PWD/qquickdirection_p.h \
+ $$PWD/qquicktargetdirection_p.h \
+ $$PWD/qquickturbulence_p.h \
+ $$PWD/qquickwander_p.h \
+ $$PWD/qquickcumulativedirection_p.h \
+ $$PWD/qquickv8particledata_p.h \
+ $$PWD/qquickrectangleextruder_p.h \
+ $$PWD/qquickparticlegroup_p.h \
+ $$PWD/qquickgroupgoal_p.h
+
+SOURCES += \
+ $$PWD/qquickangledirection.cpp \
+ $$PWD/qquickcustomparticle.cpp \
+ $$PWD/qquickcustomaffector.cpp \
+ $$PWD/qquickellipseextruder.cpp \
+ $$PWD/qquicktrailemitter.cpp \
+ $$PWD/qquickfriction.cpp \
+ $$PWD/qquickgravity.cpp \
+ $$PWD/qquickimageparticle.cpp \
+ $$PWD/qquickitemparticle.cpp \
+ $$PWD/qquickage.cpp \
+ $$PWD/qquicklineextruder.cpp \
+ $$PWD/qquickmaskextruder.cpp \
+ $$PWD/qquickparticleaffector.cpp \
+ $$PWD/qquickparticleemitter.cpp \
+ $$PWD/qquickparticleextruder.cpp \
+ $$PWD/qquickparticlepainter.cpp \
+ $$PWD/qquickparticlesmodule.cpp \
+ $$PWD/qquickparticlesystem.cpp \
+ $$PWD/qquickpointattractor.cpp \
+ $$PWD/qquickpointdirection.cpp \
+ $$PWD/qquickspritegoal.cpp \
+ $$PWD/qquickdirection.cpp \
+ $$PWD/qquicktargetdirection.cpp \
+ $$PWD/qquickturbulence.cpp \
+ $$PWD/qquickwander.cpp \
+ $$PWD/qquickcumulativedirection.cpp \
+ $$PWD/qquickv8particledata.cpp \
+ $$PWD/qquickrectangleextruder.cpp \
+ $$PWD/qquickparticlegroup.cpp \
+ $$PWD/qquickgroupgoal.cpp
+
+RESOURCES += \
+ $$PWD/particles.qrc
+
+
+
diff --git a/src/quick/particles/particles.qrc b/src/quick/particles/particles.qrc
new file mode 100644
index 0000000000..344f9489a4
--- /dev/null
+++ b/src/quick/particles/particles.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>particleresources/noise.png</file>
+ </qresource>
+</RCC>
diff --git a/src/quick/particles/qquickage.cpp b/src/quick/particles/qquickage.cpp
new file mode 100644
index 0000000000..a71b9e2088
--- /dev/null
+++ b/src/quick/particles/qquickage.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickage_p.h"
+#include "qquickparticleemitter_p.h"
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Age QQuickAgeAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Age affector allows you to prematurely age particles
+
+ The Age affector allows you to alter where the particle is in its lifecycle. Common uses
+ are to expire particles prematurely, possibly giving them time to animate out.
+
+ The Age affector only applies to particles which are still alive.
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Age::lifeLeft
+
+ The amount of life to set the particle to have. Affected particles
+ will advance to a point in their life where they will have this many
+ milliseconds left to live.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::Age::advancePosition
+
+ advancePosition determines whether position, veclocity and acceleration are included in
+ the simulated aging done by the affector. If advancePosition is false,
+ then the position, velocity and acceleration will remain the same and only
+ other attributes (such as opacity) will advance in the simulation to where
+ it would normally be for that point in the particle's life. With advancePosition set to
+ true the position, velocity and acceleration will also advance to where it would
+ normally be by that point in the particle's life, making it advance its position
+ on screen.
+
+ Default value is true.
+*/
+
+QQuickAgeAffector::QQuickAgeAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_lifeLeft(0), m_advancePosition(true)
+{
+}
+
+
+bool QQuickAgeAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ if (d->stillAlive()){
+ qreal curT = (qreal)m_system->timeInt/1000.0;
+ qreal ttl = (qreal)m_lifeLeft/1000.0;
+ if (!m_advancePosition && ttl > 0){
+ qreal x = d->curX();
+ qreal vx = d->curVX();
+ qreal ax = d->curAX();
+ qreal y = d->curY();
+ qreal vy = d->curVY();
+ qreal ay = d->curAY();
+ d->t = curT - (d->lifeSpan - ttl);
+ d->setInstantaneousX(x);
+ d->setInstantaneousVX(vx);
+ d->setInstantaneousAX(ax);
+ d->setInstantaneousY(y);
+ d->setInstantaneousVY(vy);
+ d->setInstantaneousAY(ay);
+ } else {
+ d->t = curT - (d->lifeSpan - ttl);
+ }
+ return true;
+ }
+ return false;
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickage_p.h b/src/quick/particles/qquickage_p.h
new file mode 100644
index 0000000000..b3fd3c65f4
--- /dev/null
+++ b/src/quick/particles/qquickage_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef KILLAFFECTOR_H
+#define KILLAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAgeAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(int lifeLeft READ lifeLeft WRITE setLifeLeft NOTIFY lifeLeftChanged)
+ Q_PROPERTY(bool advancePosition READ advancePosition WRITE setAdvancePosition NOTIFY advancePositionChanged)
+
+public:
+ explicit QQuickAgeAffector(QQuickItem *parent = 0);
+
+ int lifeLeft() const
+ {
+ return m_lifeLeft;
+ }
+
+ bool advancePosition() const
+ {
+ return m_advancePosition;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+ void lifeLeftChanged(int arg);
+ void advancePositionChanged(bool arg);
+
+public slots:
+ void setLifeLeft(int arg)
+ {
+ if (m_lifeLeft != arg) {
+ m_lifeLeft = arg;
+ emit lifeLeftChanged(arg);
+ }
+ }
+
+ void setAdvancePosition(bool arg)
+ {
+ if (m_advancePosition != arg) {
+ m_advancePosition = arg;
+ emit advancePositionChanged(arg);
+ }
+ }
+
+private:
+ int m_lifeLeft;
+ bool m_advancePosition;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // KILLAFFECTOR_H
diff --git a/src/quick/particles/qquickangledirection.cpp b/src/quick/particles/qquickangledirection.cpp
new file mode 100644
index 0000000000..9ce422e8d1
--- /dev/null
+++ b/src/quick/particles/qquickangledirection.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickangledirection_p.h"
+#include <cmath>
+QT_BEGIN_NAMESPACE
+const qreal CONV = 0.017453292519943295;
+/*!
+ \qmlclass AngleDirection QQuickAngleDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The AngleDirection element allows you to specify a direction that varies in angle
+
+ The AngledDirection element allows both the specification of a direction by angle and magnitude,
+ as well as varying the parameters by angle or magnitude.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::angle
+ This property specifies the base angle for the direction.
+ The angle of this direction will vary by no more than angleVariation
+ from this angle.
+
+ Angle is specified by degrees clockwise from straight right.
+
+ The default value is zero.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::magnitude
+ This property specifies the base magnitude for the direction.
+ The magnitude of this direction will vary by no more than magnitudeVariation
+ from this magnitude.
+
+ Magnitude is specified in units of pixels per second.
+
+ The default value is zero.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::angleVariation
+ This property specifies the maximum angle variation for the direction.
+ The angle of the direction will vary by up to angleVariation clockwise
+ and anticlockwise from the value specified in angle.
+
+ Angle is specified by degrees clockwise from straight right.
+
+ The default value is zero.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::magnitudeVariation
+ This property specifies the base magnitude for the direction.
+ The magnitude of this direction will vary by no more than magnitudeVariation
+ from the base magnitude.
+
+ Magnitude is specified in units of pixels per second.
+
+ The default value is zero.
+*/
+QQuickAngleDirection::QQuickAngleDirection(QObject *parent) :
+ QQuickDirection(parent)
+ , m_angle(0)
+ , m_magnitude(0)
+ , m_angleVariation(0)
+ , m_magnitudeVariation(0)
+{
+
+}
+
+const QPointF QQuickAngleDirection::sample(const QPointF &from)
+{
+ Q_UNUSED(from);
+ QPointF ret;
+ qreal theta = m_angle*CONV - m_angleVariation*CONV + rand()/float(RAND_MAX) * m_angleVariation*CONV * 2;
+ qreal mag = m_magnitude- m_magnitudeVariation + rand()/float(RAND_MAX) * m_magnitudeVariation * 2;
+ ret.setX(mag * cos(theta));
+ ret.setY(mag * sin(theta));
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickangledirection_p.h b/src/quick/particles/qquickangledirection_p.h
new file mode 100644
index 0000000000..c0de933c00
--- /dev/null
+++ b/src/quick/particles/qquickangledirection_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickANGLEDDIRECTION_H
+#define QQuickANGLEDDIRECTION_H
+#include "qquickdirection_p.h"
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAngleDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
+ Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal angleVariation READ angleVariation WRITE setAngleVariation NOTIFY angleVariationChanged)
+ Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged)
+public:
+ explicit QQuickAngleDirection(QObject *parent = 0);
+ const QPointF sample(const QPointF &from);
+ qreal angle() const
+ {
+ return m_angle;
+ }
+
+ qreal magnitude() const
+ {
+ return m_magnitude;
+ }
+
+ qreal angleVariation() const
+ {
+ return m_angleVariation;
+ }
+
+ qreal magnitudeVariation() const
+ {
+ return m_magnitudeVariation;
+ }
+
+signals:
+
+ void angleChanged(qreal arg);
+
+ void magnitudeChanged(qreal arg);
+
+ void angleVariationChanged(qreal arg);
+
+ void magnitudeVariationChanged(qreal arg);
+
+public slots:
+void setAngle(qreal arg)
+{
+ if (m_angle != arg) {
+ m_angle = arg;
+ emit angleChanged(arg);
+ }
+}
+
+void setMagnitude(qreal arg)
+{
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ emit magnitudeChanged(arg);
+ }
+}
+
+void setAngleVariation(qreal arg)
+{
+ if (m_angleVariation != arg) {
+ m_angleVariation = arg;
+ emit angleVariationChanged(arg);
+ }
+}
+
+void setMagnitudeVariation(qreal arg)
+{
+ if (m_magnitudeVariation != arg) {
+ m_magnitudeVariation = arg;
+ emit magnitudeVariationChanged(arg);
+ }
+}
+
+private:
+qreal m_angle;
+qreal m_magnitude;
+qreal m_angleVariation;
+qreal m_magnitudeVariation;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // QQuickANGLEDDIRECTION_H
diff --git a/src/quick/particles/qquickcumulativedirection.cpp b/src/quick/particles/qquickcumulativedirection.cpp
new file mode 100644
index 0000000000..2f09ea4505
--- /dev/null
+++ b/src/quick/particles/qquickcumulativedirection.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcumulativedirection_p.h"
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass CumulativeDirection QQuickCumulativeDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The CumulativeDirection element allows you to specify a direction made of other directions
+
+ The CumulativeDirection element will act as a direction that sums the directions within it.
+*/
+QQuickCumulativeDirection::QQuickCumulativeDirection(QObject *parent):QQuickDirection(parent)
+{
+}
+
+QDeclarativeListProperty<QQuickDirection> QQuickCumulativeDirection::directions()
+{
+ return QDeclarativeListProperty<QQuickDirection>(this, m_directions);//TODO: Proper list property
+}
+
+const QPointF QQuickCumulativeDirection::sample(const QPointF &from)
+{
+ QPointF ret;
+ foreach (QQuickDirection* dir, m_directions)
+ ret += dir->sample(from);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickcumulativedirection_p.h b/src/quick/particles/qquickcumulativedirection_p.h
new file mode 100644
index 0000000000..cae491cafe
--- /dev/null
+++ b/src/quick/particles/qquickcumulativedirection_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickCUMULATIVEDIRECTION_P_H
+#define QQuickCUMULATIVEDIRECTION_P_H
+#include "qquickdirection_p.h"
+#include <QDeclarativeListProperty>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCumulativeDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(QDeclarativeListProperty<QQuickDirection> directions READ directions)
+ Q_CLASSINFO("DefaultProperty", "directions")
+public:
+ explicit QQuickCumulativeDirection(QObject *parent = 0);
+ QDeclarativeListProperty<QQuickDirection> directions();
+ const QPointF sample(const QPointF &from);
+private:
+ QList<QQuickDirection*> m_directions;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQuickCUMULATIVEDIRECTION_P_H
diff --git a/src/quick/particles/qquickcustomaffector.cpp b/src/quick/particles/qquickcustomaffector.cpp
new file mode 100644
index 0000000000..80e2eaf268
--- /dev/null
+++ b/src/quick/particles/qquickcustomaffector.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcustomaffector_p.h"
+#include <private/qv8engine_p.h>
+#include <private/qdeclarativeengine_p.h>
+#include <QDeclarativeEngine>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+
+//TODO: Move docs (and inheritence) to real base when docs can propagate. Currently this pretends to be the base class!
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::affectParticles(Array particles, real dt)
+
+ This handler is called when particles are selected to be affected. particles contains
+ an array of particle objects which can be directly manipulated.
+
+ dt is the time since the last time it was affected. Use dt to normalize
+ trajectory manipulations to real time.
+
+ Note that JS is slower to execute, so it is not recommended to use this in
+ high-volume particle systems.
+*/
+
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Affector::position
+
+ Affected particles will have their position set to this direction,
+ relative to the ParticleSystem. When interpreting directions as points,
+ imagine it as an arrow with the base at the 0,0 of the ParticleSystem and the
+ tip at where the specified position will be.
+*/
+
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Affector::speed
+
+ Affected particles will have their speed set to this direction.
+*/
+
+
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Affector::acceleration
+
+ Affected particles will have their acceleration set to this direction.
+*/
+
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::Affector::relative
+
+ Whether the affected particles have their existing position/speed/acceleration added
+ to the new one.
+
+ Default is true.
+*/
+QQuickCustomAffector::QQuickCustomAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent)
+ , m_position(&m_nullVector)
+ , m_speed(&m_nullVector)
+ , m_acceleration(&m_nullVector)
+ , m_relative(true)
+{
+}
+
+bool QQuickCustomAffector::isAffectConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("affectParticles(QDeclarativeV8Handle,qreal)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+void QQuickCustomAffector::affectSystem(qreal dt)
+{
+ if (!isAffectConnected()) {
+ QQuickParticleAffector::affectSystem(dt);
+ return;
+ }
+ if (!m_enabled)
+ return;
+ updateOffsets();
+
+ QList<QQuickParticleData*> toAffect;
+ foreach (QQuickParticleGroupData* gd, m_system->groupData)
+ if (activeGroup(m_system->groupData.key(gd)))
+ foreach (QQuickParticleData* d, gd->data)
+ if (shouldAffect(d))
+ toAffect << d;
+
+ if (toAffect.isEmpty())
+ return;
+
+ if (m_onceOff)
+ dt = 1.0;
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
+ v8::Handle<v8::Array> array = v8::Array::New(toAffect.size());
+ for (int i=0; i<toAffect.size(); i++)
+ array->Set(i, toAffect[i]->v8Value().toHandle());
+
+ if (dt >= simulationCutoff || dt <= simulationDelta) {
+ affectProperties(toAffect, dt);
+ emit affectParticles(QDeclarativeV8Handle::fromHandle(array), dt);
+ } else {
+ int realTime = m_system->timeInt;
+ m_system->timeInt -= dt * 1000.0;
+ while (dt > simulationDelta) {
+ m_system->timeInt += simulationDelta * 1000.0;
+ dt -= simulationDelta;
+ affectProperties(toAffect, simulationDelta);
+ emit affectParticles(QDeclarativeV8Handle::fromHandle(array), simulationDelta);
+ }
+ m_system->timeInt = realTime;
+ if (dt > 0.0) {
+ affectProperties(toAffect, dt);
+ emit affectParticles(QDeclarativeV8Handle::fromHandle(array), dt);
+ }
+ }
+
+ foreach (QQuickParticleData* d, toAffect)
+ if (d->update == 1.0)
+ postAffect(d);
+}
+
+bool QQuickCustomAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ //This does the property based affecting, called by superclass if signal isn't hooked up.
+ bool changed = false;
+ QPointF curPos(d->curX(), d->curY());
+
+ if (m_acceleration != &m_nullVector){
+ QPointF pos = m_acceleration->sample(curPos);
+ if (m_relative) {
+ pos *= dt;
+ pos += QPointF(d->curAX(), d->curAY());
+ }
+ d->setInstantaneousAX(pos.x());
+ d->setInstantaneousAY(pos.y());
+ changed = true;
+ }
+
+ if (m_speed != &m_nullVector){
+ QPointF pos = m_speed->sample(curPos);
+ if (m_relative) {
+ pos *= dt;
+ pos += QPointF(d->curVX(), d->curVY());
+ }
+ d->setInstantaneousVX(pos.x());
+ d->setInstantaneousVY(pos.y());
+ changed = true;
+ }
+
+ if (m_position != &m_nullVector){
+ QPointF pos = m_position->sample(curPos);
+ if (m_relative) {
+ pos *= dt;
+ pos += curPos;
+ }
+ d->setInstantaneousX(pos.x());
+ d->setInstantaneousY(pos.y());
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QQuickCustomAffector::affectProperties(const QList<QQuickParticleData*> particles, qreal dt)
+{
+ foreach (QQuickParticleData* d, particles)
+ if ( affectParticle(d, dt) )
+ d->update = 1.0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickcustomaffector_p.h b/src/quick/particles/qquickcustomaffector_p.h
new file mode 100644
index 0000000000..740b0a6773
--- /dev/null
+++ b/src/quick/particles/qquickcustomaffector_p.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUSTOMAFFECTOR_H
+#define CUSTOMAFFECTOR_H
+
+#include <QObject>
+#include "qquickparticlesystem_p.h"
+#include "qquickparticleextruder_p.h"
+#include "qquickparticleaffector_p.h"
+#include "qquickdirection_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCustomAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(bool relative READ relative WRITE setRelative NOTIFY relativeChanged)
+ Q_PROPERTY(QQuickDirection *position READ position WRITE setPosition NOTIFY positionChanged RESET positionReset)
+ Q_PROPERTY(QQuickDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged RESET speedReset)
+ Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged RESET accelerationReset)
+
+public:
+ explicit QQuickCustomAffector(QQuickItem *parent = 0);
+ virtual void affectSystem(qreal dt);
+
+ QQuickDirection * position() const
+ {
+ return m_position;
+ }
+
+ QQuickDirection * speed() const
+ {
+ return m_speed;
+ }
+
+ QQuickDirection * acceleration() const
+ {
+ return m_acceleration;
+ }
+
+ void positionReset()
+ {
+ m_position = &m_nullVector;
+ }
+
+ void speedReset()
+ {
+ m_speed = &m_nullVector;
+ }
+
+ void accelerationReset()
+ {
+ m_acceleration = &m_nullVector;
+ }
+
+ bool relative() const
+ {
+ return m_relative;
+ }
+
+
+signals:
+ void affectParticles(QDeclarativeV8Handle particles, qreal dt);
+
+ void positionChanged(QQuickDirection * arg);
+
+ void speedChanged(QQuickDirection * arg);
+
+ void accelerationChanged(QQuickDirection * arg);
+
+ void relativeChanged(bool arg);
+
+public slots:
+ void setPosition(QQuickDirection * arg)
+ {
+ if (m_position != arg) {
+ m_position = arg;
+ emit positionChanged(arg);
+ }
+ }
+
+ void setSpeed(QQuickDirection * arg)
+ {
+ if (m_speed != arg) {
+ m_speed = arg;
+ emit speedChanged(arg);
+ }
+ }
+
+ void setAcceleration(QQuickDirection * arg)
+ {
+ if (m_acceleration != arg) {
+ m_acceleration = arg;
+ emit accelerationChanged(arg);
+ }
+ }
+
+ void setRelative(bool arg)
+ {
+ if (m_relative != arg) {
+ m_relative = arg;
+ emit relativeChanged(arg);
+ }
+ }
+
+protected:
+ bool isAffectConnected();
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+private:
+ void affectProperties(const QList<QQuickParticleData*> particles, qreal dt);
+ QQuickDirection * m_position;
+ QQuickDirection * m_speed;
+ QQuickDirection * m_acceleration;
+
+ QQuickDirection m_nullVector;
+ bool m_relative;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // CUSTOMAFFECTOR_H
diff --git a/src/quick/particles/qquickcustomparticle.cpp b/src/quick/particles/qquickcustomparticle.cpp
new file mode 100644
index 0000000000..e6f50c0ff6
--- /dev/null
+++ b/src/quick/particles/qquickcustomparticle.cpp
@@ -0,0 +1,596 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcustomparticle_p.h"
+#include <QtQuick/private/qquickshadereffectmesh_p.h>
+#include <cstdlib>
+
+QT_BEGIN_NAMESPACE
+
+//Includes comments because the code isn't self explanatory
+static const char qt_particles_template_vertex_code[] =
+ "attribute highp vec2 qt_ParticlePos;\n"
+ "attribute highp vec2 qt_ParticleTex;\n"
+ "attribute highp vec4 qt_ParticleData; // x = time, y = lifeSpan, z = size, w = endSize\n"
+ "attribute highp vec4 qt_ParticleVec; // x,y = constant speed, z,w = acceleration\n"
+ "attribute highp float qt_ParticleR;\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp float qt_Timestamp;\n"
+ "varying highp vec2 qt_TexCoord0;\n"
+ "void defaultMain() {\n"
+ " qt_TexCoord0 = qt_ParticleTex;\n"
+ " highp float size = qt_ParticleData.z;\n"
+ " highp float endSize = qt_ParticleData.w;\n"
+ " highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;\n"
+ " highp float currentSize = mix(size, endSize, t * t);\n"
+ " if (t < 0. || t > 1.)\n"
+ " currentSize = 0.;\n"
+ " highp vec2 pos = qt_ParticlePos\n"
+ " - currentSize / 2. + currentSize * qt_ParticleTex // adjust size\n"
+ " + qt_ParticleVec.xy * t * qt_ParticleData.y // apply speed vector..\n"
+ " + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);\n"
+ " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
+ "}";
+static const char qt_particles_default_vertex_code[] =
+ "void main() { \n"
+ " defaultMain(); \n"
+ "}";
+
+static const char qt_particles_default_fragment_code[] =
+ "uniform sampler2D source; \n"
+ "varying highp vec2 qt_TexCoord0; \n"
+ "uniform lowp float qt_Opacity; \n"
+ "void main() { \n"
+ " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
+ "}";
+
+static QSGGeometry::Attribute PlainParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // TexCoord
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(3, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(4, 1, GL_FLOAT) // r
+};
+
+static QSGGeometry::AttributeSet PlainParticle_AttributeSet =
+{
+ 5, // Attribute Count
+ (2 + 2 + 4 + 4 + 1) * sizeof(float),
+ PlainParticle_Attributes
+};
+
+struct PlainVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ float r;
+};
+
+struct PlainVertices {
+ PlainVertex v1;
+ PlainVertex v2;
+ PlainVertex v3;
+ PlainVertex v4;
+};
+
+/*!
+ \qmlclass CustomParticle QQuickCustomParticle
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief The CustomParticle element allows you to specify your own shader to paint particles.
+
+*/
+
+QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
+ : QQuickParticlePainter(parent)
+ , m_dirtyData(true)
+ , m_material(0)
+ , m_rootNode(0)
+{
+ setFlag(QQuickItem::ItemHasContents);
+}
+
+class QQuickShaderEffectMaterialObject : public QObject, public QQuickShaderEffectMaterial { };
+
+QQuickCustomParticle::~QQuickCustomParticle()
+{
+ if (m_material)
+ m_material->deleteLater();
+}
+
+void QQuickCustomParticle::componentComplete()
+{
+ reset();
+ QQuickParticlePainter::componentComplete();
+}
+
+
+//Trying to keep the shader conventions the same as in qsgshadereffectitem
+/*!
+ \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
+
+ This property holds the fragment shader's GLSL source code.
+ The default shader expects the texture coordinate to be passed from the
+ vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
+ sampler2D named "source".
+*/
+
+void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
+{
+ if (m_source.fragmentCode.constData() == code.constData())
+ return;
+ m_source.fragmentCode = code;
+ if (isComponentComplete()) {
+ reset();
+ }
+ emit fragmentShaderChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
+
+ This property holds the vertex shader's GLSL source code.
+
+ The default shader passes the texture coordinate along to the fragment
+ shader as "varying highp vec2 qt_TexCoord0".
+
+ To aid writing a particle vertex shader, the following GLSL code is prepended
+ to your vertex shader:
+ \code
+ attribute highp vec2 qt_ParticlePos;
+ attribute highp vec2 qt_ParticleTex;
+ attribute highp vec4 qt_ParticleData; // x = time, y = lifeSpan, z = size, w = endSize
+ attribute highp vec4 qt_ParticleVec; // x,y = constant speed, z,w = acceleration
+ attribute highp float qt_ParticleR;
+ uniform highp mat4 qt_Matrix;
+ uniform highp float qt_Timestamp;
+ varying highp vec2 qt_TexCoord0;
+ void defaultMain() {
+ qt_TexCoord0 = qt_ParticleTex;
+ highp float size = qt_ParticleData.z;
+ highp float endSize = qt_ParticleData.w;
+ highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;
+ highp float currentSize = mix(size, endSize, t * t);
+ if (t < 0. || t > 1.)
+ currentSize = 0.;
+ highp vec2 pos = qt_ParticlePos
+ - currentSize / 2. + currentSize * qt_ParticleTex // adjust size
+ + qt_ParticleVec.xy * t * qt_ParticleData.y // apply speed vector..
+ + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);
+ gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);
+ }
+ \endcode
+
+ defaultMain() is the same code as in the default shader, you can call this for basic
+ particle functions and then add additional variables for custom effects. Note that
+ the vertex shader for particles is responsible for simulating the movement of particles
+ over time, the particle data itself only has the starting position and spawn time.
+*/
+
+void QQuickCustomParticle::setVertexShader(const QByteArray &code)
+{
+ if (m_source.vertexCode.constData() == code.constData())
+ return;
+ m_source.vertexCode = code;
+ if (isComponentComplete()) {
+ reset();
+ }
+ emit vertexShaderChanged();
+}
+
+void QQuickCustomParticle::reset()
+{
+ disconnectPropertySignals();
+
+ m_source.attributeNames.clear();
+ m_source.uniformNames.clear();
+ m_source.respectsOpacity = false;
+ m_source.respectsMatrix = false;
+ m_source.className = metaObject()->className();
+
+ for (int i = 0; i < m_sources.size(); ++i) {
+ const SourceData &source = m_sources.at(i);
+ delete source.mapper;
+ if (source.item && source.item->parentItem() == this)
+ source.item->setParentItem(0);
+ }
+ m_sources.clear();
+
+ QQuickParticlePainter::reset();
+ m_pleaseReset = true;
+ update();
+}
+
+
+void QQuickCustomParticle::changeSource(int index)
+{
+ Q_ASSERT(index >= 0 && index < m_sources.size());
+ QVariant v = property(m_sources.at(index).name.constData());
+ setSource(v, index);
+}
+
+void QQuickCustomParticle::updateData()
+{
+ m_dirtyData = true;
+ update();
+}
+
+void QQuickCustomParticle::setSource(const QVariant &var, int index)
+{
+ Q_ASSERT(index >= 0 && index < m_sources.size());
+
+ SourceData &source = m_sources[index];
+
+ source.item = 0;
+ if (var.isNull()) {
+ return;
+ } else if (!qVariantCanConvert<QObject *>(var)) {
+ qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
+ return;
+ }
+
+ QObject *obj = qVariantValue<QObject *>(var);
+ source.item = qobject_cast<QQuickItem *>(obj);
+ if (!source.item || !source.item->isTextureProvider()) {
+ qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
+ source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
+ return;
+ }
+
+ // TODO: Copy better solution in QQuickShaderEffect when they find it.
+ // 'source.item' needs a canvas to get a scenegraph node.
+ // The easiest way to make sure it gets a canvas is to
+ // make it a part of the same item tree as 'this'.
+ if (source.item && source.item->parentItem() == 0) {
+ source.item->setParentItem(this);
+ source.item->setVisible(false);
+ }
+}
+
+void QQuickCustomParticle::disconnectPropertySignals()
+{
+ disconnect(this, 0, this, SLOT(updateData()));
+ for (int i = 0; i < m_sources.size(); ++i) {
+ SourceData &source = m_sources[i];
+ disconnect(this, 0, source.mapper, 0);
+ disconnect(source.mapper, 0, this, 0);
+ }
+}
+
+void QQuickCustomParticle::connectPropertySignals()
+{
+ QSet<QByteArray>::const_iterator it;
+ for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
+ int pi = metaObject()->indexOfProperty(it->constData());
+ if (pi >= 0) {
+ QMetaProperty mp = metaObject()->property(pi);
+ if (!mp.hasNotifySignal())
+ qWarning("QQuickCustomParticle: property '%s' does not have notification method!", it->constData());
+ QByteArray signalName("2");
+ signalName.append(mp.notifySignal().signature());
+ connect(this, signalName, this, SLOT(updateData()));
+ } else {
+ qWarning("QQuickCustomParticle: '%s' does not have a matching property!", it->constData());
+ }
+ }
+ for (int i = 0; i < m_sources.size(); ++i) {
+ SourceData &source = m_sources[i];
+ int pi = metaObject()->indexOfProperty(source.name.constData());
+ if (pi >= 0) {
+ QMetaProperty mp = metaObject()->property(pi);
+ QByteArray signalName("2");
+ signalName.append(mp.notifySignal().signature());
+ connect(this, signalName, source.mapper, SLOT(map()));
+ source.mapper->setMapping(this, i);
+ connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
+ } else {
+ qWarning("QQuickCustomParticle: '%s' does not have a matching source!", source.name.constData());
+ }
+ }
+}
+
+void QQuickCustomParticle::updateProperties()
+{
+ QByteArray vertexCode = m_source.vertexCode;
+ QByteArray fragmentCode = m_source.fragmentCode;
+ if (vertexCode.isEmpty())
+ vertexCode = qt_particles_default_vertex_code;
+ if (fragmentCode.isEmpty())
+ fragmentCode = qt_particles_default_fragment_code;
+ vertexCode = qt_particles_template_vertex_code + vertexCode;
+
+ m_source.attributeNames.clear();
+ m_source.attributeNames << "qt_ParticlePos"
+ << "qt_ParticleTex"
+ << "qt_ParticleData"
+ << "qt_ParticleVec"
+ << "qt_ParticleR";
+
+ lookThroughShaderCode(vertexCode);
+ lookThroughShaderCode(fragmentCode);
+
+ if (!m_source.respectsMatrix)
+ qWarning("QQuickCustomParticle: Missing reference to \'qt_Matrix\'.");
+ if (!m_source.respectsOpacity)
+ qWarning("QQuickCustomParticle: Missing reference to \'qt_Opacity\'.");
+
+ for (int i = 0; i < m_sources.size(); ++i) {
+ QVariant v = property(m_sources.at(i).name);
+ setSource(v, i);
+ }
+
+ connectPropertySignals();
+}
+
+void QQuickCustomParticle::lookThroughShaderCode(const QByteArray &code)
+{
+ // Regexp for matching attributes and uniforms.
+ // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name>
+ static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)"));
+ Q_ASSERT(re.isValid());
+
+ int pos = -1;
+
+ QString wideCode = QString::fromLatin1(code.constData(), code.size());
+
+ while ((pos = re.indexIn(wideCode, pos + 1)) != -1) {
+ QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute
+ QByteArray type = re.cap(2).toLatin1(); // type
+ QByteArray name = re.cap(3).toLatin1(); // variable name
+
+ if (decl == "attribute") {
+ if (!m_source.attributeNames.contains(name))
+ qWarning() << "Custom Particle: Unknown attribute " << name;
+ } else {
+ Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert
+
+ if (name == "qt_Matrix") {
+ m_source.respectsMatrix = true;
+ } else if (name == "qt_Opacity") {
+ m_source.respectsOpacity = true;
+ } else if (name == "qt_Timestamp") {
+ //Not strictly necessary
+ } else {
+ m_source.uniformNames.insert(name);
+ if (type == "sampler2D") {
+ SourceData d;
+ d.mapper = new QSignalMapper;
+ d.name = name;
+ d.item = 0;
+ m_sources.append(d);
+ }
+ }
+ }
+ }
+}
+
+QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ Q_UNUSED(oldNode);
+ if (m_pleaseReset){
+
+ //delete m_material;//Shader effect item doesn't regen material?
+
+ delete m_rootNode;//Automatically deletes children
+ m_rootNode = 0;
+ m_nodes.clear();
+ m_pleaseReset = false;
+ m_dirtyData = false;
+ }
+
+ if (m_system && m_system->isRunning() && !m_system->isPaused()){
+ prepareNextFrame();
+ if (m_rootNode) {
+ update();
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyGeometry);//done in buildData?
+ }
+ }
+
+ return m_rootNode;
+}
+
+void QQuickCustomParticle::prepareNextFrame(){
+ if (!m_rootNode)
+ m_rootNode = buildCustomNodes();
+ if (!m_rootNode)
+ return;
+
+ m_lastTime = m_system->systemSync(this) / 1000.;
+ if (m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive.
+ buildData();
+}
+
+QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
+{
+#ifdef QT_OPENGL_ES_2
+ if (m_count * 4 > 0xffff) {
+ printf("CustomParticle: Too many particles... \n");
+ return 0;
+ }
+#endif
+
+ if (m_count <= 0) {
+ printf("CustomParticle: Too few particles... \n");
+ return 0;
+ }
+
+ updateProperties();
+
+ QQuickShaderEffectProgram s = m_source;
+ if (s.fragmentCode.isEmpty())
+ s.fragmentCode = qt_particles_default_fragment_code;
+ if (s.vertexCode.isEmpty())
+ s.vertexCode = qt_particles_default_vertex_code;
+
+ if (!m_material) {
+ m_material = new QQuickShaderEffectMaterialObject;
+ }
+
+ s.vertexCode = qt_particles_template_vertex_code + s.vertexCode;
+ m_material->setProgramSource(s);
+ foreach (const QString &str, m_groups){
+ int gIdx = m_system->groupIds[str];
+ int count = m_system->groupData[gIdx]->size();
+
+ QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
+ m_nodes.insert(gIdx, node);
+
+ node->setMaterial(m_material);
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ //Create Particle Geometry
+ int vCount = count * 4;
+ int iCount = count * 6;
+ QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
+ g->setDrawingMode(GL_TRIANGLES);
+ node->setGeometry(g);
+ PlainVertex *vertices = (PlainVertex *) g->vertexData();
+ for (int p=0; p < count; ++p) {
+ commit(gIdx, p);
+ vertices[0].tx = 0;
+ vertices[0].ty = 0;
+
+ vertices[1].tx = 1;
+ vertices[1].ty = 0;
+
+ vertices[2].tx = 0;
+ vertices[2].ty = 1;
+
+ vertices[3].tx = 1;
+ vertices[3].ty = 1;
+ vertices += 4;
+ }
+ quint16 *indices = g->indexDataAsUShort();
+ for (int i=0; i < count; ++i) {
+ int o = i * 4;
+ indices[0] = o;
+ indices[1] = o + 1;
+ indices[2] = o + 2;
+ indices[3] = o + 1;
+ indices[4] = o + 3;
+ indices[5] = o + 2;
+ indices += 6;
+ }
+ }
+ foreach (QQuickShaderEffectNode* node, m_nodes){
+ if (node == *(m_nodes.begin()))
+ continue;
+ (*(m_nodes.begin()))->appendChildNode(node);
+ }
+
+ return *(m_nodes.begin());
+}
+
+
+void QQuickCustomParticle::buildData()
+{
+ if (!m_rootNode)
+ return;
+ const QByteArray timestampName("qt_Timestamp");
+ QVector<QPair<QByteArray, QVariant> > values;
+ QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
+ const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = m_material->textureProviders();
+ for (int i = 0; i < oldTextures.size(); ++i) {
+ QSGTextureProvider *t = oldTextures.at(i).second;
+ if (t)
+ foreach (QQuickShaderEffectNode* node, m_nodes)
+ disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ }
+ for (int i = 0; i < m_sources.size(); ++i) {
+ const SourceData &source = m_sources.at(i);
+ QSGTextureProvider *t = source.item->textureProvider();
+ textures.append(qMakePair(source.name, t));
+ if (t)
+ foreach (QQuickShaderEffectNode* node, m_nodes)
+ connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
+ }
+ for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
+ it != m_source.uniformNames.end(); ++it) {
+ values.append(qMakePair(*it, property(*it)));
+ }
+ values.append(qMakePair(timestampName, QVariant(m_lastTime)));
+ m_material->setUniforms(values);
+ m_material->setTextureProviders(textures);
+ m_dirtyData = false;
+ foreach (QQuickShaderEffectNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+}
+
+void QQuickCustomParticle::initialize(int gIdx, int pIdx)
+{
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ datum->r = rand()/(qreal)RAND_MAX;
+}
+
+void QQuickCustomParticle::commit(int gIdx, int pIdx)
+{
+ if (m_nodes[gIdx] == 0)
+ return;
+
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ PlainVertices *particles = (PlainVertices *) m_nodes[gIdx]->geometry()->vertexData();
+ PlainVertex *vertices = (PlainVertex *)&particles[pIdx];
+ for (int i=0; i<4; ++i) {
+ vertices[i].x = datum->x - m_systemOffset.x();
+ vertices[i].y = datum->y - m_systemOffset.y();
+ vertices[i].t = datum->t;
+ vertices[i].lifeSpan = datum->lifeSpan;
+ vertices[i].size = datum->size;
+ vertices[i].endSize = datum->endSize;
+ vertices[i].vx = datum->vx;
+ vertices[i].vy = datum->vy;
+ vertices[i].ax = datum->ax;
+ vertices[i].ay = datum->ay;
+ vertices[i].r = datum->r;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickcustomparticle_p.h b/src/quick/particles/qquickcustomparticle_p.h
new file mode 100644
index 0000000000..583e61fa39
--- /dev/null
+++ b/src/quick/particles/qquickcustomparticle_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUSTOM_PARTICLE_H
+#define CUSTOM_PARTICLE_H
+#include "qquickparticlepainter_p.h"
+#include <private/qquickshadereffectnode_p.h>
+#include <QSignalMapper>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QSGNode;
+struct PlainVertices;
+
+class QQuickShaderEffectMaterialObject;
+
+//Genealogy: Hybrid of UltraParticle and ShaderEffect
+class QQuickCustomParticle : public QQuickParticlePainter
+{
+ Q_OBJECT
+ Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged)
+ Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged)
+
+public:
+ explicit QQuickCustomParticle(QQuickItem* parent=0);
+ ~QQuickCustomParticle();
+
+ QByteArray fragmentShader() const { return m_source.fragmentCode; }
+ void setFragmentShader(const QByteArray &code);
+
+ QByteArray vertexShader() const { return m_source.vertexCode; }
+ void setVertexShader(const QByteArray &code);
+public Q_SLOTS:
+ void updateData();
+ void changeSource(int);
+Q_SIGNALS:
+ void fragmentShaderChanged();
+ void vertexShaderChanged();
+protected:
+ virtual void initialize(int gIdx, int pIdx);
+ virtual void commit(int gIdx, int pIdx);
+
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ void prepareNextFrame();
+ void setSource(const QVariant &var, int index);
+ void disconnectPropertySignals();
+ void connectPropertySignals();
+ void reset();
+ void resize(int oldCount, int newCount);
+ void updateProperties();
+ void lookThroughShaderCode(const QByteArray &code);
+ virtual void componentComplete();
+ QQuickShaderEffectNode *buildCustomNodes();
+ void performPendingResize();
+
+private:
+ void buildData();
+
+ bool m_dirtyData;
+ QQuickShaderEffectProgram m_source;
+ struct SourceData
+ {
+ QSignalMapper *mapper;
+ QPointer<QQuickItem> item;
+ QByteArray name;
+ };
+ QVector<SourceData> m_sources;
+ QQuickShaderEffectMaterialObject *m_material;
+ QQuickShaderEffectNode* m_rootNode;
+ QHash<int, QQuickShaderEffectNode*> m_nodes;
+ qreal m_lastTime;
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //HEADER_GUARD
diff --git a/src/quick/particles/qquickdirection.cpp b/src/quick/particles/qquickdirection.cpp
new file mode 100644
index 0000000000..babd35de9f
--- /dev/null
+++ b/src/quick/particles/qquickdirection.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickdirection_p.h"
+
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Direction QQuickDirection
+ \inqmlmodule QtQuick.Particles 2
+ \brief The Direction elements allow you to specify a vector space.
+
+*/
+
+
+QQuickDirection::QQuickDirection(QObject *parent) :
+ QObject(parent)
+{
+}
+
+const QPointF QQuickDirection::sample(const QPointF &from)
+{
+ Q_UNUSED(from);
+ return QPointF();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickdirection_p.h b/src/quick/particles/qquickdirection_p.h
new file mode 100644
index 0000000000..ecfb84d90f
--- /dev/null
+++ b/src/quick/particles/qquickdirection_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VARYINGVECTOR_H
+#define VARYINGVECTOR_H
+
+#include <QObject>
+#include <QPointF>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDirection : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QQuickDirection(QObject *parent = 0);
+
+ virtual const QPointF sample(const QPointF &from);
+signals:
+
+public slots:
+
+protected:
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // VARYINGVECTOR_H
diff --git a/src/quick/particles/qquickellipseextruder.cpp b/src/quick/particles/qquickellipseextruder.cpp
new file mode 100644
index 0000000000..479bbb4aa7
--- /dev/null
+++ b/src/quick/particles/qquickellipseextruder.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickellipseextruder_p.h"
+#include <cmath>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass EllipseShape QQuickEllipseExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Shape
+ \brief The EllipseShape represents an ellipse to other particle system elements
+
+ This shape can be used by Emitter subclasses and Affector subclasses to have
+ them act upon an ellipse shaped area.
+*/
+QQuickEllipseExtruder::QQuickEllipseExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent)
+ , m_fill(true)
+{
+}
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::EllipseShape::fill
+ If fill is true the ellipse is filled; otherwise it is just a border.
+
+ Default is true.
+*/
+
+QPointF QQuickEllipseExtruder::extrude(const QRectF & r)
+{
+ qreal theta = ((qreal)rand()/RAND_MAX) * 6.2831853071795862;
+ qreal mag = m_fill ? ((qreal)rand()/RAND_MAX) : 1;
+ return QPointF(r.x() + r.width()/2 + mag * (r.width()/2) * cos(theta),
+ r.y() + r.height()/2 + mag * (r.height()/2) * sin(theta));
+}
+
+bool QQuickEllipseExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ return bounds.contains(point);//TODO: Ellipse
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickellipseextruder_p.h b/src/quick/particles/qquickellipseextruder_p.h
new file mode 100644
index 0000000000..77c117a3d5
--- /dev/null
+++ b/src/quick/particles/qquickellipseextruder_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ELLIPSEEXTRUDER_H
+#define ELLIPSEEXTRUDER_H
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickEllipseExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box
+public:
+ explicit QQuickEllipseExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+
+ bool fill() const
+ {
+ return m_fill;
+ }
+
+signals:
+
+ void fillChanged(bool arg);
+
+public slots:
+
+ void setFill(bool arg)
+ {
+ if (m_fill != arg) {
+ m_fill = arg;
+ emit fillChanged(arg);
+ }
+ }
+private:
+ bool m_fill;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ELLIPSEEXTRUDER_H
diff --git a/src/quick/particles/qquickfriction.cpp b/src/quick/particles/qquickfriction.cpp
new file mode 100644
index 0000000000..c060deb95f
--- /dev/null
+++ b/src/quick/particles/qquickfriction.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfriction_p.h"
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Friction QQuickFrictionAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Friction affector slows down movement proportional to the particle's current speed.
+
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Friction::factor
+
+ A drag will be applied to moving objects which is this factor of their current velocity.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Friction::threshold
+
+ The drag will only be applied to objects with a velocity above the threshold velocity. The
+ drag applied will bring objects down to the threshold velocity, but no further.
+
+ The default threshold is 0
+*/
+static qreal sign(qreal a)
+{
+ return a >= 0 ? 1 : -1;
+}
+
+static const qreal epsilon = 0.00001;
+
+QQuickFrictionAffector::QQuickFrictionAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_factor(0.0), m_threshold(0.0)
+{
+}
+
+bool QQuickFrictionAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ if (!m_factor)
+ return false;
+ qreal curVX = d->curVX();
+ qreal curVY = d->curVY();
+ if (!curVX && !curVY)
+ return false;
+ qreal newVX = curVX + (curVX * m_factor * -1 * dt);
+ qreal newVY = curVY + (curVY * m_factor * -1 * dt);
+
+ if (!m_threshold) {
+ if (sign(curVX) != sign(newVX))
+ newVX = 0;
+ if (sign(curVY) != sign(newVY))
+ newVY = 0;
+ } else {
+ qreal curMag = sqrt(curVX*curVX + curVY*curVY);
+ if (curMag <= m_threshold + epsilon)
+ return false;
+ qreal newMag = sqrt(newVX*newVX + newVY*newVY);
+ if (newMag <= m_threshold + epsilon || //went past the threshold, stop there instead
+ sign(curVX) != sign(newVX) || //went so far past maybe it came out the other side!
+ sign(curVY) != sign(newVY)) {
+ qreal theta = atan2(curVY, curVX);
+ newVX = m_threshold * cos(theta);
+ newVY = m_threshold * sin(theta);
+ }
+ }
+
+ d->setInstantaneousVX(newVX);
+ d->setInstantaneousVY(newVY);
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickfriction_p.h b/src/quick/particles/qquickfriction_p.h
new file mode 100644
index 0000000000..0eaed34783
--- /dev/null
+++ b/src/quick/particles/qquickfriction_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FRICTIONAFFECTOR_H
+#define FRICTIONAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFrictionAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged)
+ Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged)
+public:
+ explicit QQuickFrictionAffector(QQuickItem *parent = 0);
+
+ qreal factor() const
+ {
+ return m_factor;
+ }
+
+ qreal threshold() const
+ {
+ return m_threshold;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+
+signals:
+
+ void factorChanged(qreal arg);
+ void thresholdChanged(qreal arg);
+
+public slots:
+
+ void setFactor(qreal arg)
+ {
+ if (m_factor != arg) {
+ m_factor = arg;
+ emit factorChanged(arg);
+ }
+ }
+
+ void setThreshold(qreal arg)
+ {
+ if (m_threshold != arg) {
+ m_threshold = arg;
+ emit thresholdChanged(arg);
+ }
+ }
+
+private:
+ qreal m_factor;
+ qreal m_threshold;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // FRICTIONAFFECTOR_H
diff --git a/src/quick/particles/qquickgravity.cpp b/src/quick/particles/qquickgravity.cpp
new file mode 100644
index 0000000000..fd2fb92a53
--- /dev/null
+++ b/src/quick/particles/qquickgravity.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickgravity_p.h"
+#include <cmath>
+QT_BEGIN_NAMESPACE
+const qreal CONV = 0.017453292520444443;
+/*!
+ \qmlclass Gravity QQuickGravityAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Gravity element allows you to set an accleration in an angle
+
+ This element will accelerate all affected particles to a vector of
+ the specified magnitude in the specified angle. If the angle and acceleration do
+ not vary, it is more efficient to set the specified acceleration on the Emitter.
+
+ This element models the gravity of a massive object whose center of
+ gravity is far away (and thus the gravitational pull is effectively constant
+ across the scene). To model the gravity of an object near or inside the scene,
+ use PointAttractor.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Gravity::magnitude
+
+ Pixels per second that objects will be accelerated by.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Gravity::acceleration
+
+ Name changed to magnitude, will be removed soon.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Gravity::angle
+
+ Angle of acceleration.
+*/
+
+QQuickGravityAffector::QQuickGravityAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_magnitude(-10), m_angle(90), m_needRecalc(true)
+{
+}
+
+bool QQuickGravityAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ if (!m_magnitude)
+ return false;
+ if (m_needRecalc) {
+ m_needRecalc = false;
+ m_dx = m_magnitude * cos(m_angle * CONV);
+ m_dy = m_magnitude * sin(m_angle * CONV);
+ }
+
+ d->setInstantaneousVX(d->curVX() + m_dx*dt);
+ d->setInstantaneousVY(d->curVY() + m_dy*dt);
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickgravity_p.h b/src/quick/particles/qquickgravity_p.h
new file mode 100644
index 0000000000..a9faf43349
--- /dev/null
+++ b/src/quick/particles/qquickgravity_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GRAVITYAFFECTOR_H
+#define GRAVITYAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickGravityAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal acceleration READ magnitude WRITE setAcceleration NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
+public:
+ explicit QQuickGravityAffector(QQuickItem *parent = 0);
+ qreal magnitude() const
+ {
+ return m_magnitude;
+ }
+
+ qreal angle() const
+ {
+ return m_angle;
+ }
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+
+ void magnitudeChanged(qreal arg);
+
+ void angleChanged(qreal arg);
+
+public slots:
+void setAcceleration(qreal arg)
+{
+ qWarning() << "Gravity::acceleration has been renamed Gravity::magnitude";
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ m_needRecalc = true;
+ emit magnitudeChanged(arg);
+ }
+}
+
+void setMagnitude(qreal arg)
+{
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ m_needRecalc = true;
+ emit magnitudeChanged(arg);
+ }
+}
+
+void setAngle(qreal arg)
+{
+ if (m_angle != arg) {
+ m_angle = arg;
+ m_needRecalc = true;
+ emit angleChanged(arg);
+ }
+}
+
+private:
+ qreal m_magnitude;
+ qreal m_angle;
+
+ bool m_needRecalc;
+ qreal m_dx;
+ qreal m_dy;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // GRAVITYAFFECTOR_H
diff --git a/src/quick/particles/qquickgroupgoal.cpp b/src/quick/particles/qquickgroupgoal.cpp
new file mode 100644
index 0000000000..1d8a3d980c
--- /dev/null
+++ b/src/quick/particles/qquickgroupgoal.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickgroupgoal_p.h"
+#include <private/qquickspriteengine_p.h>
+#include <private/qquicksprite_p.h>
+#include "qquickimageparticle_p.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass GroupGoal QQuickGroupGoalAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The GroupGoal Affector allows you to change the state of a group of a particle.
+
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::GroupGoal::goalState
+
+ The name of the group which the affected particles should move to.
+
+ Groups can have defined durations and transitions between them, setting goalState
+ will cause it to disregard any path weightings (including 0) and head down the path
+ which will reach the goalState quickest. It will pass through intermediate groups
+ on that path for their respective durations.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::GroupGoal::jump
+
+ If true, affected particles will jump directly to the target group instead of taking the
+ the shortest valid path to get there. They will also not finish their current state,
+ but immediately move to the beginning of the goal state.
+
+ Default is false.
+*/
+
+QQuickGroupGoalAffector::QQuickGroupGoalAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_jump(false)
+{
+ m_ignoresTime = true;
+}
+
+void QQuickGroupGoalAffector::setGoalState(QString arg)
+{
+ if (m_goalState != arg) {
+ m_goalState = arg;
+ emit goalStateChanged(arg);
+ }
+}
+
+bool QQuickGroupGoalAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ QQuickStochasticEngine *engine = m_system->stateEngine;
+ bool notUsingEngine = false;
+ if (!engine)
+ notUsingEngine = true;
+
+ int index = d->systemIndex;
+ int goalIdx = m_system->groupIds[m_goalState];
+ if (notUsingEngine){//no stochastic states defined. So cut out the engine
+ //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group
+ m_system->moveGroups(d, goalIdx);
+ return true;
+ }else if (engine->curState(index) != goalIdx){
+ engine->setGoal(goalIdx, index, m_jump);
+ return true;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickgroupgoal_p.h b/src/quick/particles/qquickgroupgoal_p.h
new file mode 100644
index 0000000000..a24d89dfb5
--- /dev/null
+++ b/src/quick/particles/qquickgroupgoal_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GROUPGOALAFFECTOR_H
+#define GROUPGOALAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStochasticEngine;
+
+class QQuickGroupGoalAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
+ Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged)
+public:
+ explicit QQuickGroupGoalAffector(QQuickItem *parent = 0);
+
+ QString goalState() const
+ {
+ return m_goalState;
+ }
+
+ bool jump() const
+ {
+ return m_jump;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+
+signals:
+
+ void goalStateChanged(QString arg);
+
+ void jumpChanged(bool arg);
+
+public slots:
+
+ void setGoalState(QString arg);
+
+ void setJump(bool arg)
+ {
+ if (m_jump != arg) {
+ m_jump = arg;
+ emit jumpChanged(arg);
+ }
+ }
+
+private:
+ QString m_goalState;
+ bool m_jump;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // GROUPGOALAFFECTOR_H
diff --git a/src/quick/particles/qquickimageparticle.cpp b/src/quick/particles/qquickimageparticle.cpp
new file mode 100644
index 0000000000..3453dd6ed3
--- /dev/null
+++ b/src/quick/particles/qquickimageparticle.cpp
@@ -0,0 +1,1804 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgtexturematerial.h>
+#include <QtQuick/qsgtexture.h>
+#include <QFile>
+#include "qquickimageparticle_p.h"
+#include "qquickparticleemitter_p.h"
+#include <private/qquicksprite_p.h>
+#include <private/qquickspriteengine_p.h>
+#include <QOpenGLFunctions>
+#include <QtQuick/qsgengine.h>
+#include <QtQuick/private/qsgtexture_p.h>
+#include <private/qdeclarativeglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+//###Switch to define later, for now user-friendly (no compilation) debugging is worth it
+DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
+
+#ifndef QT_OPENGL_ES_2
+#define SHADER_DEFINES "#version 120\n"
+#else
+#define SHADER_DEFINES ""
+#endif
+
+//TODO: Make it larger on desktop? Requires fixing up shader code with the same define
+#define UNIFORM_ARRAY_SIZE 64
+
+static const char vertexShaderCode[] =
+ "attribute highp vec2 vPos;\n"
+ "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize\n"
+ "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration\n"
+ "uniform highp float entry;\n"
+ "#ifdef COLOR\n"
+ "attribute lowp vec4 vColor;\n"
+ "#endif\n"
+ "#ifdef DEFORM\n"
+ "attribute highp vec2 vTex;\n"
+ "attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector\n"
+ "attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n"
+ "#endif\n"
+ "#ifdef SPRITE\n"
+ "attribute highp vec4 vAnimData;// interpolate(bool), duration, frameCount (this anim), timestamp (this anim)\n"
+ "attribute highp vec4 vAnimPos;//sheet x,y, width/height of this anim\n"
+ "uniform highp vec2 animSheetSize; //width/height of whole sheet\n"
+ "#endif\n"
+ "\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp float timestamp;\n"
+ "#ifdef TABLE\n"
+ "varying lowp vec2 tt;//y is progress if Sprite mode\n"
+ "uniform highp float sizetable[64];\n"
+ "uniform highp float opacitytable[64];\n"
+ "#endif\n"
+ "#ifdef SPRITE\n"
+ "varying highp vec4 fTexS;\n"
+ "#else\n"
+ "#ifdef DEFORM\n"
+ "varying highp vec2 fTex;\n"
+ "#endif\n"
+ "#endif\n"
+ "#ifdef COLOR\n"
+ "varying lowp vec4 fColor;\n"
+ "#else\n"
+ "varying lowp float fFade;\n"
+ "#endif\n"
+ "\n"
+ "\n"
+ "void main() {\n"
+ "\n"
+ " highp float t = (timestamp - vData.x) / vData.y;\n"
+ " if (t < 0. || t > 1.){\n"
+ "#ifdef DEFORM //Not point sprites\n"
+ " gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0., 1.);\n"
+ "#else\n"
+ " gl_PointSize = 0.;\n"
+ "#endif\n"
+ " return;\n"
+ " }\n"
+ "#ifdef SPRITE\n"
+ " //Calculate frame location in texture\n"
+ " highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z);\n"
+ " tt.y = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y;\n"
+ "\n"
+ " frameIndex = floor(frameIndex);\n"
+ " fTexS.xy = vec2(((frameIndex + vTex.x) * vAnimPos.z / animSheetSize.x), ((vAnimPos.y + vTex.y * vAnimPos.w) / animSheetSize.y));\n"
+ "\n"
+ " //Next frame is also passed, for interpolation\n"
+ " //### Should the next anim be precalculated to allow for interpolation there?\n"
+ " if (vAnimData.x == 1.0 && frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop\n"
+ " frameIndex = mod(frameIndex+1., vAnimData.z);\n"
+ " fTexS.zw = vec2(((frameIndex + vTex.x) * vAnimPos.z / animSheetSize.x), ((vAnimPos.y + vTex.y * vAnimPos.w) / animSheetSize.y));\n"
+ "#else\n"
+ "#ifdef DEFORM\n"
+ " fTex = vTex;\n"
+ "#endif\n"
+ "#endif\n"
+ " highp float currentSize = mix(vData.z, vData.w, t * t);\n"
+ " lowp float fade = 1.;\n"
+ " highp float fadeIn = min(t * 10., 1.);\n"
+ " highp float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);\n"
+ "\n"
+ "#ifdef TABLE\n"
+ " currentSize = currentSize * sizetable[int(floor(t*64.))];\n"
+ " fade = fade * opacitytable[int(floor(t*64.))];\n"
+ "#endif\n"
+ "\n"
+ " if (entry == 1.)\n"
+ " fade = fade * fadeIn * fadeOut;\n"
+ " else if (entry == 2.)\n"
+ " currentSize = currentSize * fadeIn * fadeOut;\n"
+ "\n"
+ " if (currentSize <= 0.){\n"
+ "#ifdef DEFORM //Not point sprites\n"
+ " gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0., 1.);\n"
+ "#else\n"
+ " gl_PointSize = 0.;\n"
+ "#endif\n"
+ " return;\n"
+ " }\n"
+ " if (currentSize < 3.)//Sizes too small look jittery as they move\n"
+ " currentSize = 3.;\n"
+ "\n"
+ " highp vec2 pos;\n"
+ "#ifdef DEFORM\n"
+ " highp float rotation = vRotation.x + vRotation.y * t * vData.y;\n"
+ " if (vRotation.z == 1.0){\n"
+ " highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;\n"
+ " rotation += atan(curVel.y, curVel.x);\n"
+ " }\n"
+ " highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));\n"
+ " highp vec4 deform = vDeformVec * currentSize * (vTex.xxyy - 0.5);\n"
+ " highp vec4 rotatedDeform = deform.xxzz * trigCalcs.xyxy;\n"
+ " rotatedDeform = rotatedDeform + (deform.yyww * trigCalcs.yxyx * vec4(-1.,1.,-1.,1.));\n"
+ " /* The readable version:\n"
+ " highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);\n"
+ " highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);\n"
+ " highp vec2 xRotatedDeform;\n"
+ " xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;\n"
+ " xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;\n"
+ " highp vec2 yRotatedDeform;\n"
+ " yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;\n"
+ " yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;\n"
+ " */\n"
+ " pos = vPos\n"
+ " + rotatedDeform.xy\n"
+ " + rotatedDeform.zw\n"
+ " + vVec.xy * t * vData.y // apply speed\n"
+ " + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration\n"
+ "#else\n"
+ " pos = vPos\n"
+ " + vVec.xy * t * vData.y // apply speed vector..\n"
+ " + 0.5 * vVec.zw * pow(t * vData.y, 2.);\n"
+ " gl_PointSize = currentSize;\n"
+ "#endif\n"
+ " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
+ "\n"
+ "#ifdef COLOR\n"
+ " fColor = vColor * fade;\n"
+ "#else\n"
+ " fFade = fade;\n"
+ "#endif\n"
+ "#ifdef TABLE\n"
+ " tt.x = t;\n"
+ "#endif\n"
+ "}\n";
+
+static const char fragmentShaderCode[] =
+ "uniform sampler2D texture;\n"
+ "uniform lowp float qt_Opacity;\n"
+ "\n"
+ "#ifdef SPRITE\n"
+ "varying highp vec4 fTexS;\n"
+ "#else\n"
+ "#ifdef DEFORM //First non-pointsprite\n"
+ "varying highp vec2 fTex;\n"
+ "#endif\n"
+ "#endif\n"
+ "#ifdef COLOR\n"
+ "varying lowp vec4 fColor;\n"
+ "#else\n"
+ "varying lowp float fFade;\n"
+ "#endif\n"
+ "#ifdef TABLE\n"
+ "varying lowp vec2 tt;\n"
+ "uniform sampler2D colortable;\n"
+ "#endif\n"
+ "\n"
+ "void main() {\n"
+ "#ifdef SPRITE\n"
+ " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), tt.y)\n"
+ " * fColor\n"
+ " * texture2D(colortable, tt)\n"
+ " * qt_Opacity;\n"
+ "#else\n"
+ "#ifdef TABLE\n"
+ " gl_FragColor = texture2D(texture, fTex)\n"
+ " * fColor\n"
+ " * texture2D(colortable, tt)\n"
+ " * qt_Opacity;\n"
+ "#else\n"
+ "#ifdef DEFORM\n"
+ " gl_FragColor = (texture2D(texture, fTex)) * fColor * qt_Opacity;\n"
+ "#else\n"
+ "#ifdef COLOR\n"
+ " gl_FragColor = (texture2D(texture, gl_PointCoord)) * fColor * qt_Opacity;\n"
+ "#else\n"
+ " gl_FragColor = texture2D(texture, gl_PointCoord) * (fFade * qt_Opacity);\n"
+ "#endif //COLOR\n"
+ "#endif //DEFORM\n"
+ "#endif //TABLE\n"
+ "#endif //SPRITE\n"
+ "}\n";
+
+const qreal CONV = 0.017453292519943295;
+class ImageMaterialData
+{
+ public:
+ ImageMaterialData()
+ : texture(0), colorTable(0)
+ {}
+
+ ~ImageMaterialData(){
+ delete texture;
+ delete colorTable;
+ }
+
+ QSGTexture *texture;
+ QSGTexture *colorTable;
+ float sizeTable[UNIFORM_ARRAY_SIZE];
+ float opacityTable[UNIFORM_ARRAY_SIZE];
+
+ qreal timestamp;
+ qreal entry;
+ QSizeF animSheetSize;
+};
+
+class TabledMaterialData : public ImageMaterialData {};
+class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
+
+public:
+ TabledMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
+ << "vColor" << "vDeformVec" << "vRotation";
+ };
+
+ void initialize() {
+ QSGSimpleMaterialShader<TabledMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ program()->setUniformValue("colortable", 1);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ m_sizetable_id = program()->uniformLocation("sizetable");
+ m_opacitytable_id = program()->uniformLocation("opacitytable");
+ }
+
+ void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE1);
+ d->colorTable->bind();
+
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
+ program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
+ }
+
+ int m_entry_id;
+ int m_timestamp_id;
+ int m_sizetable_id;
+ int m_opacitytable_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class DeformableMaterialData : public ImageMaterialData {};
+class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
+
+public:
+ DeformableMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define DEFORM\n#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define DEFORM\n#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
+ << "vColor" << "vDeformVec" << "vRotation";
+ };
+
+ void initialize() {
+ QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
+ void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ }
+
+ int m_entry_id;
+ int m_timestamp_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class SpriteMaterialData : public ImageMaterialData {};
+class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
+
+public:
+ SpriteMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
+ << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
+ };
+
+ void initialize() {
+ QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ program()->setUniformValue("colortable", 1);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_animsize_id = program()->uniformLocation("animSheetSize");
+ m_entry_id = program()->uniformLocation("entry");
+ m_sizetable_id = program()->uniformLocation("sizetable");
+ m_opacitytable_id = program()->uniformLocation("opacitytable");
+ }
+
+ void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE1);
+ d->colorTable->bind();
+
+ // make sure we end by setting GL_TEXTURE0 as active texture
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_animsize_id, d->animSheetSize);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
+ program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
+ }
+
+ int m_timestamp_id;
+ int m_animsize_id;
+ int m_entry_id;
+ int m_sizetable_id;
+ int m_opacitytable_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class ColoredMaterialData : public ImageMaterialData {};
+class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
+
+public:
+ ColoredMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ void activate() {
+ QSGSimpleMaterialShader<ColoredMaterialData>::activate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ void deactivate() {
+ QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
+ }
+
+ void initialize() {
+ QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
+ void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ }
+
+ int m_timestamp_id;
+ int m_entry_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class SimpleMaterialData : public ImageMaterialData {};
+class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
+
+public:
+ SimpleMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ void activate() {
+ QSGSimpleMaterialShader<SimpleMaterialData>::activate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ void deactivate() {
+ QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vData" << "vVec";
+ }
+
+ void initialize() {
+ QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
+ void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ }
+
+ int m_timestamp_id;
+ int m_entry_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+void fillUniformArrayFromImage(float* array, const QImage& img, int size)
+{
+ if (img.isNull()){
+ for (int i=0; i<size; i++)
+ array[i] = 1.0;
+ return;
+ }
+ QImage scaled = img.scaled(size,1);
+ for (int i=0; i<size; i++)
+ array[i] = qAlpha(scaled.pixel(i,0))/255.0;
+}
+
+/*!
+ \qmlclass ImageParticle QQuickImageParticle
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief The ImageParticle element visualizes logical particles using an image
+
+ This element renders a logical particle as an image. The image can be
+ \list
+ \o colorized
+ \o rotated
+ \o deformed
+ \o a sprite-based animation
+ \endlist
+
+ ImageParticles implictly share data on particles if multiple ImageParticles are painting
+ the same logical particle group. This is broken down along the four capabilities listed
+ above. So if one ImageParticle defines data for rendering the particles in one of those
+ capabilities, and the other does not, then both will draw the particles the same in that
+ aspect automatically. This is primarily useful when there is some random variation on
+ the particle which is supposed to stay with it when switching painters. If both ImageParticles
+ define how they should appear for that aspect, they diverge and each appears as it is defined.
+
+ This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
+ set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
+ So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
+ to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
+ other ImageParticles.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::source
+
+ The source image to be used.
+
+ If the image is a sprite animation, use the sprite property instead.
+*/
+/*!
+ \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites
+
+ The sprite or sprites used to draw this particle.
+
+ Note that the sprite image will be scaled to a square based on the size of
+ the particle being rendered.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable
+
+ An image whose color will be used as a 1D texture to determine color over life. E.g. when
+ the particle is halfway through its lifetime, it will have the color specified halfway
+ across the image.
+
+ This color is blended with the color property and the color of the source image.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
+
+ An image whose opacity will be used as a 1D texture to determine size over life.
+
+ This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
+
+ An image whose opacity will be used as a 1D texture to determine size over life.
+
+ This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
+*/
+/*!
+ \qmlproperty color QtQuick.Particles2::ImageParticle::color
+
+ If a color is specified, the provided image will be colorized with it.
+
+ Default is white (no change).
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation
+
+ This number represents the color variation applied to individual particles.
+ Setting colorVariation is the same as setting redVariation, greenVariation,
+ and blueVariation to the same number.
+
+ Each channel can vary between particle by up to colorVariation from its usual color.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation
+ The variation in the red color channel between particles.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation
+ The variation in the green color channel between particles.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation
+ The variation in the blue color channel between particles.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::alpha
+ An alpha to be applied to the image. This value is multiplied by the value in
+ the image, and the value in the color property.
+
+ Particles have additive blending, so lower alpha on single particles leads
+ to stronger effects when multiple particles overlap.
+
+ Alpha is measured from 0.0 to 1.0.
+
+ Default is 1.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation
+ The variation in the alpha channel between particles.
+
+ Alpha is measured from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotation
+
+ If set the image will be rotated by this many degrees before it is drawn.
+
+ The particle coordinates are not transformed.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation
+
+ If set the rotation of individual particles will vary by up to this much
+ between particles.
+
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed
+
+ If set particles will rotate at this speed in degrees/second.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation
+
+ If set the rotationSpeed of individual particles will vary by up to this much
+ between particles.
+
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation
+
+ If set to true then a rotation will be applied on top of the particles rotation, so
+ that it faces the direction of travel. So to face away from the direction of travel,
+ set autoRotation to true and rotation to 180.
+
+ Default is false
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector
+
+ Allows you to deform the particle image when drawn. The rectangular image will
+ be deformed so that the horizontal sides are in the shape of this vector instead
+ of (1,0).
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector
+
+ Allows you to deform the particle image when drawn. The rectangular image will
+ be deformed so that the vertical sides are in the shape of this vector instead
+ of (0,1).
+*/
+/*!
+ \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect
+
+ This property provides basic and cheap entrance and exit effects for the particles.
+ For fine-grained control, see sizeTable and opacityTable.
+
+ Acceptable values are
+ \list
+ \o None: Particles just appear and disappear.
+ \o Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
+ \o Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
+ \endlist
+
+ Default value is Fade.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate
+
+ If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
+ the sprites look smoother.
+
+ Default is true.
+*/
+
+
+QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
+ : QQuickParticlePainter(parent)
+ , m_color_variation(0.0)
+ , m_rootNode(0)
+ , m_material(0)
+ , m_alphaVariation(0.0)
+ , m_alpha(1.0)
+ , m_redVariation(0.0)
+ , m_greenVariation(0.0)
+ , m_blueVariation(0.0)
+ , m_rotation(0)
+ , m_rotationVariation(0)
+ , m_rotationSpeed(0)
+ , m_rotationSpeedVariation(0)
+ , m_autoRotation(false)
+ , m_xVector(0)
+ , m_yVector(0)
+ , m_spriteEngine(0)
+ , m_spritesInterpolate(true)
+ , m_explicitColor(false)
+ , m_explicitRotation(false)
+ , m_explicitDeformation(false)
+ , m_explicitAnimation(false)
+ , m_bloat(false)
+ , perfLevel(Unknown)
+ , m_lastLevel(Unknown)
+ , m_debugMode(false)
+ , m_entryEffect(Fade)
+{
+ setFlag(ItemHasContents);
+ m_debugMode = qmlParticlesDebug();
+}
+
+QQuickImageParticle::~QQuickImageParticle()
+{
+}
+
+QDeclarativeListProperty<QQuickSprite> QQuickImageParticle::sprites()
+{
+ return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
+
+void QQuickImageParticle::setImage(const QUrl &image)
+{
+ if (image == m_image_name)
+ return;
+ m_image_name = image;
+ emit imageChanged();
+ reset();
+}
+
+
+void QQuickImageParticle::setColortable(const QUrl &table)
+{
+ if (table == m_colortable_name)
+ return;
+ m_colortable_name = table;
+ emit colortableChanged();
+ reset();
+}
+
+void QQuickImageParticle::setSizetable(const QUrl &table)
+{
+ if (table == m_sizetable_name)
+ return;
+ m_sizetable_name = table;
+ emit sizetableChanged();
+ reset();
+}
+
+void QQuickImageParticle::setOpacitytable(const QUrl &table)
+{
+ if (table == m_opacitytable_name)
+ return;
+ m_opacitytable_name = table;
+ emit opacitytableChanged();
+ reset();
+}
+
+void QQuickImageParticle::setColor(const QColor &color)
+{
+ if (color == m_color)
+ return;
+ m_color = color;
+ emit colorChanged();
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setColorVariation(qreal var)
+{
+ if (var == m_color_variation)
+ return;
+ m_color_variation = var;
+ emit colorVariationChanged();
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setAlphaVariation(qreal arg)
+{
+ if (m_alphaVariation != arg) {
+ m_alphaVariation = arg;
+ emit alphaVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setAlpha(qreal arg)
+{
+ if (m_alpha != arg) {
+ m_alpha = arg;
+ emit alphaChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setRedVariation(qreal arg)
+{
+ if (m_redVariation != arg) {
+ m_redVariation = arg;
+ emit redVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setGreenVariation(qreal arg)
+{
+ if (m_greenVariation != arg) {
+ m_greenVariation = arg;
+ emit greenVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setBlueVariation(qreal arg)
+{
+ if (m_blueVariation != arg) {
+ m_blueVariation = arg;
+ emit blueVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setRotation(qreal arg)
+{
+ if (m_rotation != arg) {
+ m_rotation = arg;
+ emit rotationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setRotationVariation(qreal arg)
+{
+ if (m_rotationVariation != arg) {
+ m_rotationVariation = arg;
+ emit rotationVariationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setRotationSpeed(qreal arg)
+{
+ if (m_rotationSpeed != arg) {
+ m_rotationSpeed = arg;
+ emit rotationSpeedChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setRotationSpeedVariation(qreal arg)
+{
+ if (m_rotationSpeedVariation != arg) {
+ m_rotationSpeedVariation = arg;
+ emit rotationSpeedVariationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setAutoRotation(bool arg)
+{
+ if (m_autoRotation != arg) {
+ m_autoRotation = arg;
+ emit autoRotationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setXVector(QQuickDirection* arg)
+{
+ if (m_xVector != arg) {
+ m_xVector = arg;
+ emit xVectorChanged(arg);
+ }
+ m_explicitDeformation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setYVector(QQuickDirection* arg)
+{
+ if (m_yVector != arg) {
+ m_yVector = arg;
+ emit yVectorChanged(arg);
+ }
+ m_explicitDeformation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setSpritesInterpolate(bool arg)
+{
+ if (m_spritesInterpolate != arg) {
+ m_spritesInterpolate = arg;
+ emit spritesInterpolateChanged(arg);
+ }
+}
+
+void QQuickImageParticle::setBloat(bool arg)
+{
+ if (m_bloat != arg) {
+ m_bloat = arg;
+ emit bloatChanged(arg);
+ }
+ if (perfLevel < 9999)
+ reset();
+}
+
+void QQuickImageParticle::setEntryEffect(EntryEffect arg)
+{
+ if (m_entryEffect != arg) {
+ m_entryEffect = arg;
+ if (m_material)
+ getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
+ emit entryEffectChanged(arg);
+ }
+}
+
+void QQuickImageParticle::resetColor()
+{
+ m_explicitColor = false;
+ foreach (const QString &str, m_groups)
+ foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
+ if (d->colorOwner == this)
+ d->colorOwner = 0;
+ m_color = QColor();
+ m_color_variation = 0.0f;
+ m_redVariation = 0.0f;
+ m_blueVariation = 0.0f;
+ m_greenVariation = 0.0f;
+ m_alpha = 1.0f;
+ m_alphaVariation = 0.0f;
+}
+
+void QQuickImageParticle::resetRotation()
+{
+ m_explicitRotation = false;
+ foreach (const QString &str, m_groups)
+ foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
+ if (d->rotationOwner == this)
+ d->rotationOwner = 0;
+ m_rotation = 0;
+ m_rotationVariation = 0;
+ m_rotationSpeed = 0;
+ m_rotationSpeedVariation = 0;
+ m_autoRotation = false;
+}
+
+void QQuickImageParticle::resetDeformation()
+{
+ m_explicitDeformation = false;
+ foreach (const QString &str, m_groups)
+ foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
+ if (d->deformationOwner == this)
+ d->deformationOwner = 0;
+ if (m_xVector)
+ delete m_xVector;
+ if (m_yVector)
+ delete m_yVector;
+ m_xVector = 0;
+ m_yVector = 0;
+}
+
+void QQuickImageParticle::reset()
+{
+ QQuickParticlePainter::reset();
+ m_pleaseReset = true;
+ update();
+}
+
+void QQuickImageParticle::createEngine()
+{
+ if (m_spriteEngine)
+ delete m_spriteEngine;
+ if (m_sprites.count()) {
+ m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
+ connect(m_spriteEngine, SIGNAL(stateChanged(int)),
+ this, SLOT(spriteAdvance(int)));
+ m_explicitAnimation = true;
+ } else {
+ m_spriteEngine = 0;
+ m_explicitAnimation = false;
+ }
+ reset();
+}
+
+static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors
+};
+
+static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
+{
+ 3, // Attribute Count
+ ( 2 + 4 + 4 ) * sizeof(float),
+ SimpleParticle_Attributes
+};
+
+static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
+};
+
+static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
+{
+ 4, // Attribute Count
+ ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
+ ColoredParticle_Attributes
+};
+
+static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // TexCoord
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(3, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(4, 4, GL_UNSIGNED_BYTE), // Colors
+ QSGGeometry::Attribute::create(5, 4, GL_FLOAT), // DeformationVectors
+ QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Rotation
+};
+
+static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
+{
+ 7, // Attribute Count
+ (2 + 2 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
+ DeformableParticle_Attributes
+};
+
+static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // TexCoord
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(3, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(4, 4, GL_UNSIGNED_BYTE), // Colors
+ QSGGeometry::Attribute::create(5, 4, GL_FLOAT), // DeformationVectors
+ QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Rotation
+ QSGGeometry::Attribute::create(7, 4, GL_FLOAT), // Anim Data
+ QSGGeometry::Attribute::create(8, 4, GL_FLOAT) // Anim Pos
+};
+
+static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
+{
+ 9, // Attribute Count
+ (2 + 2 + 4 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
+ SpriteParticle_Attributes
+};
+
+void QQuickImageParticle::clearShadows()
+{
+ foreach (const QVector<QQuickParticleData*> data, m_shadowData)
+ qDeleteAll(data);
+ m_shadowData.clear();
+}
+
+//Only call if you need to, may initialize the whole array first time
+QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
+{
+ QQuickParticleGroupData* gd = m_system->groupData[datum->group];
+ if (!m_shadowData.contains(datum->group)) {
+ QVector<QQuickParticleData*> data;
+ for (int i=0; i<gd->size(); i++){
+ QQuickParticleData* datum = new QQuickParticleData(m_system);
+ *datum = *(gd->data[i]);
+ data << datum;
+ }
+ m_shadowData.insert(datum->group, data);
+ }
+ //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
+
+ return m_shadowData[datum->group][datum->index];
+}
+
+QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
+{
+#ifdef QT_OPENGL_ES_2
+ if (m_count * 4 > 0xffff) {
+ printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
+ return 0;
+ }
+#endif
+
+ if (count() <= 0)
+ return 0;
+
+ if (m_sprites.count() || m_bloat) {
+ perfLevel = Sprites;
+ } else if (!m_colortable_name.isEmpty() || !m_sizetable_name.isEmpty()
+ || !m_opacitytable_name.isEmpty()) {
+ perfLevel = Tabled;
+ } else if (m_autoRotation || m_rotation || m_rotationVariation
+ || m_rotationSpeed || m_rotationSpeedVariation
+ || m_xVector || m_yVector) {
+ perfLevel = Deformable;
+ } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
+ || m_redVariation || m_blueVariation || m_greenVariation) {
+ perfLevel = Colored;
+ } else {
+ perfLevel = Simple;
+ }
+
+ foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
+ int gIdx = m_system->groupIds[str];
+ foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
+ QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
+ if (other){
+ if (other->perfLevel > perfLevel) {
+ if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
+ if (perfLevel < Deformable)
+ perfLevel = Deformable;
+ } else {
+ perfLevel = other->perfLevel;
+ }
+ } else if (other->perfLevel < perfLevel) {
+ other->reset();
+ }
+ }
+ }
+ }
+
+ if (perfLevel >= Colored && !m_color.isValid())
+ m_color = QColor(Qt::white);//Hidden default, but different from unset
+
+ QImage image;
+ if (perfLevel >= Sprites){
+ if (!m_spriteEngine) {
+ qWarning() << "ImageParticle: No sprite engine...";
+ return 0;
+ }
+ image = m_spriteEngine->assembledImage();
+ if (image.isNull())//Warning is printed in engine
+ return 0;
+ } else {
+ image = QImage(m_image_name.toLocalFile());
+ if (image.isNull()) {
+ printf("ImageParticle: loading image failed '%s'\n", qPrintable(m_image_name.toLocalFile()));
+ return 0;
+ }
+ }
+
+ clearShadows();
+ if (m_material)
+ m_material = 0;
+
+ //Setup material
+ QImage colortable;
+ QImage sizetable;
+ QImage opacitytable;
+ switch (perfLevel) {//Fallthrough intended
+ case Sprites:
+ m_material = SpriteMaterial::createMaterial();
+ getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
+ m_spriteEngine->setCount(m_count);
+ case Tabled:
+ if (!m_material)
+ m_material = TabledMaterial::createMaterial();
+ colortable = QImage(m_colortable_name.toLocalFile());
+ sizetable = QImage(m_sizetable_name.toLocalFile());
+ opacitytable = QImage(m_opacitytable_name.toLocalFile());
+ if (colortable.isNull()){
+ colortable = QImage(1,1,QImage::Format_ARGB32);
+ colortable.fill(Qt::white);
+ }
+ Q_ASSERT(!colortable.isNull());
+ getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
+ fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
+ fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
+ case Deformable:
+ if (!m_material)
+ m_material = DeformableMaterial::createMaterial();
+ case Colored:
+ if (!m_material)
+ m_material = ColoredMaterial::createMaterial();
+ default://Also Simple
+ if (!m_material)
+ m_material = SimpleMaterial::createMaterial();
+ getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
+ getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
+ getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
+ m_material->setFlag(QSGMaterial::Blending);
+ }
+
+ foreach (const QString &str, m_groups){
+ int gIdx = m_system->groupIds[str];
+ int count = m_system->groupData[gIdx]->size();
+ QSGGeometryNode* node = new QSGGeometryNode();
+ node->setMaterial(m_material);
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ m_nodes.insert(gIdx, node);
+ m_idxStarts.insert(gIdx, m_lastIdxStart);
+ m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
+ m_lastIdxStart += count;
+
+ //Create Particle Geometry
+ int vCount = count * 4;
+ int iCount = count * 6;
+
+ QSGGeometry *g;
+ if (perfLevel == Sprites)
+ g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
+ else if (perfLevel == Tabled)
+ g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
+ else if (perfLevel == Deformable)
+ g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
+ else if (perfLevel == Colored)
+ g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
+ else //Simple
+ g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
+
+ node->setGeometry(g);
+ if (perfLevel <= Colored){
+ g->setDrawingMode(GL_POINTS);
+ if (m_debugMode){
+ GLfloat pointSizeRange[2];
+ glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
+ qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
+ }
+ }else
+ g->setDrawingMode(GL_TRIANGLES);
+
+ for (int p=0; p < count; ++p)
+ commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
+
+ if (perfLevel == Sprites)
+ initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
+ else if (perfLevel == Tabled)
+ initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
+ else if (perfLevel == Deformable)
+ initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
+
+ if (perfLevel > Colored){
+ quint16 *indices = g->indexDataAsUShort();
+ for (int i=0; i < count; ++i) {
+ int o = i * 4;
+ indices[0] = o;
+ indices[1] = o + 1;
+ indices[2] = o + 2;
+ indices[3] = o + 1;
+ indices[4] = o + 3;
+ indices[5] = o + 2;
+ indices += 6;
+ }
+ }
+
+ }
+
+ foreach (QSGGeometryNode* node, m_nodes){
+ if (node == *(m_nodes.begin()))
+ node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
+ else
+ (*(m_nodes.begin()))->appendChildNode(node);
+ }
+
+ return *(m_nodes.begin());
+}
+
+QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+ if (m_pleaseReset){
+ m_lastLevel = perfLevel;
+
+ delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
+ m_rootNode = 0;
+ m_nodes.clear();
+
+ m_idxStarts.clear();
+ m_startsIdx.clear();
+ m_lastIdxStart = 0;
+
+ m_material = 0;
+
+ m_pleaseReset = false;
+ }
+
+ if (m_system && m_system->isRunning() && !m_system->isPaused()){
+ prepareNextFrame();
+ if (m_rootNode) {
+ update();
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyGeometry);
+ }
+ }
+
+ return m_rootNode;
+}
+
+void QQuickImageParticle::prepareNextFrame()
+{
+ if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
+ m_rootNode = buildParticleNodes();
+ if (m_rootNode == 0)
+ return;
+ if (m_debugMode) {
+ qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
+ qDebug() << "QQuickImageParticle Nodes: ";
+ int count = 0;
+ foreach (int i, m_nodes.keys()) {
+ qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
+ count += m_system->groupData[i]->size();
+ }
+ qDebug() << "Total count: " << count;
+ }
+ }
+ qint64 timeStamp = m_system->systemSync(this);
+
+ qreal time = timeStamp / 1000.;
+
+ switch (perfLevel){//Fall-through intended
+ case Sprites:
+ //Advance State
+ m_spriteEngine->updateSprites(timeStamp);
+ case Tabled:
+ case Deformable:
+ case Colored:
+ case Simple:
+ default: //Also Simple
+ getState<ImageMaterialData>(m_material)->timestamp = time;
+ break;
+ }
+
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+}
+
+void QQuickImageParticle::spriteAdvance(int spriteIdx)
+{
+ if (!m_startsIdx.count())//Probably overly defensive
+ return;
+
+ int gIdx = -1;
+ int i;
+ for (i = 0; i<m_startsIdx.count(); i++) {
+ if (spriteIdx < m_startsIdx[i].first) {
+ gIdx = m_startsIdx[i-1].second;
+ break;
+ }
+ }
+ if (gIdx == -1)
+ gIdx = m_startsIdx[i-1].second;
+ int pIdx = spriteIdx - m_startsIdx[i-1].first;
+
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ QQuickParticleData* d = (datum->animationOwner == this ? datum : getShadowDatum(datum));
+
+ d->animIdx = m_spriteEngine->spriteState(spriteIdx);
+ Vertices<SpriteVertex>* particles = (Vertices<SpriteVertex> *) m_nodes[gIdx]->geometry()->vertexData();
+ Vertices<SpriteVertex> &p = particles[pIdx];
+ d->animT = p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
+ d->frameCount = p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ d->frameDuration = p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
+ d->animX = p.v1.animX = p.v2.animX = p.v3.animX = p.v4.animX = m_spriteEngine->spriteX(spriteIdx);
+ d->animY = p.v1.animY = p.v2.animY = p.v3.animY = p.v4.animY = m_spriteEngine->spriteY(spriteIdx);
+ d->animWidth = p.v1.animWidth = p.v2.animWidth = p.v3.animWidth = p.v4.animWidth = m_spriteEngine->spriteWidth(spriteIdx);
+ d->animHeight = p.v1.animHeight = p.v2.animHeight = p.v3.animHeight = p.v4.animHeight = m_spriteEngine->spriteHeight(spriteIdx);
+}
+
+void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
+{
+ d->color = c;
+ //TODO: get index for reload - or make function take an index
+}
+
+void QQuickImageParticle::initialize(int gIdx, int pIdx)
+{
+ Color4ub color;
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ qreal redVariation = m_color_variation + m_redVariation;
+ qreal greenVariation = m_color_variation + m_greenVariation;
+ qreal blueVariation = m_color_variation + m_blueVariation;
+ int spriteIdx = 0;
+ if (m_spriteEngine) {
+ spriteIdx = m_idxStarts[gIdx] + datum->index;
+ if (spriteIdx >= m_spriteEngine->count())
+ m_spriteEngine->setCount(spriteIdx+1);
+ }
+
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;
+ switch (perfLevel){//Fall-through is intended on all of them
+ case Sprites:
+ // Initial Sprite State
+ if (m_explicitAnimation){
+ if (!datum->animationOwner)
+ datum->animationOwner = this;
+ QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
+ writeTo->animT = writeTo->t;
+ //writeTo->animInterpolate = m_spritesInterpolate;
+ if (m_spriteEngine){
+ m_spriteEngine->start(spriteIdx);
+ writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
+ writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
+ writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
+ writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
+ writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
+ }else{
+ writeTo->frameCount = 1;
+ writeTo->frameDuration = 9999;
+ writeTo->animX = writeTo->animY = 0;
+ writeTo->animWidth = writeTo->animHeight = 1;
+ }
+ }
+ case Tabled:
+ case Deformable:
+ //Initial Rotation
+ if (m_explicitDeformation){
+ if (!datum->deformationOwner)
+ datum->deformationOwner = this;
+ if (m_xVector){
+ const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
+ if (datum->deformationOwner == this) {
+ datum->xx = ret.x();
+ datum->xy = ret.y();
+ } else {
+ getShadowDatum(datum)->xx = ret.x();
+ getShadowDatum(datum)->xy = ret.y();
+ }
+ }
+ if (m_yVector){
+ const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
+ if (datum->deformationOwner == this) {
+ datum->yx = ret.x();
+ datum->yy = ret.y();
+ } else {
+ getShadowDatum(datum)->yx = ret.x();
+ getShadowDatum(datum)->yy = ret.y();
+ }
+ }
+ }
+
+ if (m_explicitRotation){
+ if (!datum->rotationOwner)
+ datum->rotationOwner = this;
+ rotation =
+ (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
+ rotationSpeed =
+ (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
+ autoRotate = m_autoRotation?1.0:0.0;
+ if (datum->rotationOwner == this) {
+ datum->rotation = rotation;
+ datum->rotationSpeed = rotationSpeed;
+ datum->autoRotate = autoRotate;
+ } else {
+ getShadowDatum(datum)->rotation = rotation;
+ getShadowDatum(datum)->rotationSpeed = rotationSpeed;
+ getShadowDatum(datum)->autoRotate = autoRotate;
+ }
+ }
+ case Colored:
+ //Color initialization
+ // Particle color
+ if (m_explicitColor) {
+ if (!datum->colorOwner)
+ datum->colorOwner = this;
+ color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
+ color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
+ color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
+ color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
+ if (datum->colorOwner == this)
+ datum->color = color;
+ else
+ getShadowDatum(datum)->color = color;
+ }
+ default:
+ break;
+ }
+}
+
+void QQuickImageParticle::commit(int gIdx, int pIdx)
+{
+ if (m_pleaseReset)
+ return;
+ QSGGeometryNode *node = m_nodes[gIdx];
+ if (!node)
+ return;
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
+ DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
+ ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
+ SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
+ switch (perfLevel){//No automatic fall through intended on this one
+ case Sprites:
+ spriteVertices += pIdx*4;
+ for (int i=0; i<4; i++){
+ spriteVertices[i].x = datum->x - m_systemOffset.x();
+ spriteVertices[i].y = datum->y - m_systemOffset.y();
+ spriteVertices[i].t = datum->t;
+ spriteVertices[i].lifeSpan = datum->lifeSpan;
+ spriteVertices[i].size = datum->size;
+ spriteVertices[i].endSize = datum->endSize;
+ spriteVertices[i].vx = datum->vx;
+ spriteVertices[i].vy = datum->vy;
+ spriteVertices[i].ax = datum->ax;
+ spriteVertices[i].ay = datum->ay;
+ if (m_explicitDeformation && datum->deformationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].xx = shadow->xx;
+ spriteVertices[i].xy = shadow->xy;
+ spriteVertices[i].yx = shadow->yx;
+ spriteVertices[i].yy = shadow->yy;
+ } else {
+ spriteVertices[i].xx = datum->xx;
+ spriteVertices[i].xy = datum->xy;
+ spriteVertices[i].yx = datum->yx;
+ spriteVertices[i].yy = datum->yy;
+ }
+ if (m_explicitRotation && datum->rotationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].rotation = shadow->rotation;
+ spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
+ spriteVertices[i].autoRotate = shadow->autoRotate;
+ } else {
+ spriteVertices[i].rotation = datum->rotation;
+ spriteVertices[i].rotationSpeed = datum->rotationSpeed;
+ spriteVertices[i].autoRotate = datum->autoRotate;
+ }
+ spriteVertices[i].animInterpolate = m_spritesInterpolate ? 1.0 : 0.0;//### Shadow? In particleData? Or uniform?
+ if (m_explicitAnimation && datum->animationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].frameDuration = shadow->frameDuration;
+ spriteVertices[i].frameCount = shadow->frameCount;
+ spriteVertices[i].animT = shadow->animT;
+ spriteVertices[i].animX = shadow->animX;
+ spriteVertices[i].animY = shadow->animY;
+ spriteVertices[i].animWidth = shadow->animWidth;
+ spriteVertices[i].animHeight = shadow->animHeight;
+ } else {
+ spriteVertices[i].frameDuration = datum->frameDuration;
+ spriteVertices[i].frameCount = datum->frameCount;
+ spriteVertices[i].animT = datum->animT;
+ spriteVertices[i].animX = datum->animX;
+ spriteVertices[i].animY = datum->animY;
+ spriteVertices[i].animWidth = datum->animWidth;
+ spriteVertices[i].animHeight = datum->animHeight;
+ }
+ if (m_explicitColor && datum->colorOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].color.r = shadow->color.r;
+ spriteVertices[i].color.g = shadow->color.g;
+ spriteVertices[i].color.b = shadow->color.b;
+ spriteVertices[i].color.a = shadow->color.a;
+ } else {
+ spriteVertices[i].color.r = datum->color.r;
+ spriteVertices[i].color.g = datum->color.g;
+ spriteVertices[i].color.b = datum->color.b;
+ spriteVertices[i].color.a = datum->color.a;
+ }
+ }
+ break;
+ case Tabled: //Fall through until it has its own vertex class
+ case Deformable:
+ deformableVertices += pIdx*4;
+ for (int i=0; i<4; i++){
+ deformableVertices[i].x = datum->x - m_systemOffset.x();
+ deformableVertices[i].y = datum->y - m_systemOffset.y();
+ deformableVertices[i].t = datum->t;
+ deformableVertices[i].lifeSpan = datum->lifeSpan;
+ deformableVertices[i].size = datum->size;
+ deformableVertices[i].endSize = datum->endSize;
+ deformableVertices[i].vx = datum->vx;
+ deformableVertices[i].vy = datum->vy;
+ deformableVertices[i].ax = datum->ax;
+ deformableVertices[i].ay = datum->ay;
+ if (m_explicitDeformation && datum->deformationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ deformableVertices[i].xx = shadow->xx;
+ deformableVertices[i].xy = shadow->xy;
+ deformableVertices[i].yx = shadow->yx;
+ deformableVertices[i].yy = shadow->yy;
+ } else {
+ deformableVertices[i].xx = datum->xx;
+ deformableVertices[i].xy = datum->xy;
+ deformableVertices[i].yx = datum->yx;
+ deformableVertices[i].yy = datum->yy;
+ }
+ if (m_explicitRotation && datum->rotationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ deformableVertices[i].rotation = shadow->rotation;
+ deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
+ deformableVertices[i].autoRotate = shadow->autoRotate;
+ } else {
+ deformableVertices[i].rotation = datum->rotation;
+ deformableVertices[i].rotationSpeed = datum->rotationSpeed;
+ deformableVertices[i].autoRotate = datum->autoRotate;
+ }
+ if (m_explicitColor && datum->colorOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ deformableVertices[i].color.r = shadow->color.r;
+ deformableVertices[i].color.g = shadow->color.g;
+ deformableVertices[i].color.b = shadow->color.b;
+ deformableVertices[i].color.a = shadow->color.a;
+ } else {
+ deformableVertices[i].color.r = datum->color.r;
+ deformableVertices[i].color.g = datum->color.g;
+ deformableVertices[i].color.b = datum->color.b;
+ deformableVertices[i].color.a = datum->color.a;
+ }
+ }
+ break;
+ case Colored:
+ coloredVertices += pIdx*1;
+ for (int i=0; i<1; i++){
+ coloredVertices[i].x = datum->x - m_systemOffset.x();
+ coloredVertices[i].y = datum->y - m_systemOffset.y();
+ coloredVertices[i].t = datum->t;
+ coloredVertices[i].lifeSpan = datum->lifeSpan;
+ coloredVertices[i].size = datum->size;
+ coloredVertices[i].endSize = datum->endSize;
+ coloredVertices[i].vx = datum->vx;
+ coloredVertices[i].vy = datum->vy;
+ coloredVertices[i].ax = datum->ax;
+ coloredVertices[i].ay = datum->ay;
+ if (m_explicitColor && datum->colorOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ coloredVertices[i].color.r = shadow->color.r;
+ coloredVertices[i].color.g = shadow->color.g;
+ coloredVertices[i].color.b = shadow->color.b;
+ coloredVertices[i].color.a = shadow->color.a;
+ } else {
+ coloredVertices[i].color.r = datum->color.r;
+ coloredVertices[i].color.g = datum->color.g;
+ coloredVertices[i].color.b = datum->color.b;
+ coloredVertices[i].color.a = datum->color.a;
+ }
+ }
+ break;
+ case Simple:
+ simpleVertices += pIdx*1;
+ for (int i=0; i<1; i++){
+ simpleVertices[i].x = datum->x - m_systemOffset.x();
+ simpleVertices[i].y = datum->y - m_systemOffset.y();
+ simpleVertices[i].t = datum->t;
+ simpleVertices[i].lifeSpan = datum->lifeSpan;
+ simpleVertices[i].size = datum->size;
+ simpleVertices[i].endSize = datum->endSize;
+ simpleVertices[i].vx = datum->vx;
+ simpleVertices[i].vy = datum->vy;
+ simpleVertices[i].ax = datum->ax;
+ simpleVertices[i].ay = datum->ay;
+ }
+ break;
+ default:
+ break;
+ }
+
+ node->setFlag(QSGNode::OwnsGeometry, true);
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickimageparticle_p.h b/src/quick/particles/qquickimageparticle_p.h
new file mode 100644
index 0000000000..61c24ed836
--- /dev/null
+++ b/src/quick/particles/qquickimageparticle_p.h
@@ -0,0 +1,427 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ULTRAPARTICLE_H
+#define ULTRAPARTICLE_H
+#include "qquickparticlepainter_p.h"
+#include "qquickdirection_p.h"
+#include <QDeclarativeListProperty>
+#include <QtQuick/qsgsimplematerial.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class ImageMaterialData;
+class QSGGeometryNode;
+
+class QQuickSprite;
+class QQuickStochasticEngine;
+
+struct SimpleVertex {
+ float x;
+ float y;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+};
+
+struct ColoredVertex {
+ float x;
+ float y;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ Color4ub color;
+};
+
+struct DeformableVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ Color4ub color;
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;//Assumed that GPUs prefer floats to bools
+};
+
+struct SpriteVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ Color4ub color;
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;//Assumed that GPUs prefer floats to bools
+ float animInterpolate;
+ float frameDuration;
+ float frameCount;
+ float animT;
+ float animX;
+ float animY;
+ float animWidth;
+ float animHeight;
+};
+
+template <typename Vertex>
+struct Vertices {
+ Vertex v1;
+ Vertex v2;
+ Vertex v3;
+ Vertex v4;
+};
+
+class QQuickImageParticle : public QQuickParticlePainter
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged)
+ Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged)
+ Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged)
+ Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged)
+
+ //###Now just colorize - add a flag for 'solid' color particles(where the img is just a mask?)?
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged RESET resetColor)
+ //Stacks (added) with individual colorVariations
+ Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged RESET resetColor)
+ Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged RESET resetColor)
+ Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged RESET resetColor)
+ Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged RESET resetColor)
+ //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha)
+ Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged RESET resetColor)
+ Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged RESET resetColor)
+
+ Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged RESET resetRotation)
+ Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged RESET resetRotation)
+ Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged RESET resetRotation)
+ Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged RESET resetRotation)
+ //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation
+ //to 180 will lead to facing away from the direction of motion
+ Q_PROPERTY(bool autoRotation READ autoRotation WRITE setAutoRotation NOTIFY autoRotationChanged RESET resetRotation)
+
+ //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML?
+ //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size
+ Q_PROPERTY(QQuickDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged RESET resetDeformation)
+ //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram.
+ Q_PROPERTY(QQuickDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged RESET resetDeformation)
+ Q_PROPERTY(QDeclarativeListProperty<QQuickSprite> sprites READ sprites)
+ Q_PROPERTY(bool spritesInterpolate READ spritesInterpolate WRITE setSpritesInterpolate NOTIFY spritesInterpolateChanged)
+
+ Q_PROPERTY(EntryEffect entryEffect READ entryEffect WRITE setEntryEffect NOTIFY entryEffectChanged)
+ Q_PROPERTY(bool bloat READ bloat WRITE setBloat NOTIFY bloatChanged)//Just a debugging property to bypass optimizations
+ Q_ENUMS(EntryEffect)
+public:
+ explicit QQuickImageParticle(QQuickItem *parent = 0);
+ virtual ~QQuickImageParticle();
+
+
+ QDeclarativeListProperty<QQuickSprite> sprites();
+ QQuickStochasticEngine* spriteEngine() {return m_spriteEngine;}
+
+ enum EntryEffect {
+ None = 0,
+ Fade = 1,
+ Scale = 2
+ };
+
+ enum PerformanceLevel{//TODO: Expose?
+ Unknown = 0,
+ Simple,
+ Colored,
+ Deformable,
+ Tabled,
+ Sprites
+ };
+
+ QUrl image() const { return m_image_name; }
+ void setImage(const QUrl &image);
+
+ QUrl colortable() const { return m_colortable_name; }
+ void setColortable(const QUrl &table);
+
+ QUrl sizetable() const { return m_sizetable_name; }
+ void setSizetable (const QUrl &table);
+
+ QUrl opacitytable() const { return m_opacitytable_name; }
+ void setOpacitytable(const QUrl &table);
+
+ QColor color() const { return m_color; }
+ void setColor(const QColor &color);
+
+ qreal colorVariation() const { return m_color_variation; }
+ void setColorVariation(qreal var);
+
+ qreal alphaVariation() const { return m_alphaVariation; }
+
+ qreal alpha() const { return m_alpha; }
+
+ qreal redVariation() const { return m_redVariation; }
+
+ qreal greenVariation() const { return m_greenVariation; }
+
+ qreal blueVariation() const { return m_blueVariation; }
+
+ qreal rotation() const { return m_rotation; }
+
+ qreal rotationVariation() const { return m_rotationVariation; }
+
+ qreal rotationSpeed() const { return m_rotationSpeed; }
+
+ qreal rotationSpeedVariation() const { return m_rotationSpeedVariation; }
+
+ bool autoRotation() const { return m_autoRotation; }
+
+ QQuickDirection* xVector() const { return m_xVector; }
+
+ QQuickDirection* yVector() const { return m_yVector; }
+
+ bool spritesInterpolate() const { return m_spritesInterpolate; }
+
+ bool bloat() const { return m_bloat; }
+
+ EntryEffect entryEffect() const { return m_entryEffect; }
+
+ void resetColor();
+ void resetRotation();
+ void resetDeformation();
+
+signals:
+
+ void imageChanged();
+ void colortableChanged();
+ void sizetableChanged();
+ void opacitytableChanged();
+
+ void colorChanged();
+ void colorVariationChanged();
+
+ void alphaVariationChanged(qreal arg);
+
+ void alphaChanged(qreal arg);
+
+ void redVariationChanged(qreal arg);
+
+ void greenVariationChanged(qreal arg);
+
+ void blueVariationChanged(qreal arg);
+
+ void rotationChanged(qreal arg);
+
+ void rotationVariationChanged(qreal arg);
+
+ void rotationSpeedChanged(qreal arg);
+
+ void rotationSpeedVariationChanged(qreal arg);
+
+ void autoRotationChanged(bool arg);
+
+ void xVectorChanged(QQuickDirection* arg);
+
+ void yVectorChanged(QQuickDirection* arg);
+
+ void spritesInterpolateChanged(bool arg);
+
+ void bloatChanged(bool arg);
+
+ void entryEffectChanged(EntryEffect arg);
+
+public slots:
+ void reloadColor(const Color4ub &c, QQuickParticleData* d);
+ void setAlphaVariation(qreal arg);
+
+ void setAlpha(qreal arg);
+
+ void setRedVariation(qreal arg);
+
+ void setGreenVariation(qreal arg);
+
+ void setBlueVariation(qreal arg);
+
+ void setRotation(qreal arg);
+
+ void setRotationVariation(qreal arg);
+
+ void setRotationSpeed(qreal arg);
+
+ void setRotationSpeedVariation(qreal arg);
+
+ void setAutoRotation(bool arg);
+
+ void setXVector(QQuickDirection* arg);
+
+ void setYVector(QQuickDirection* arg);
+
+ void setSpritesInterpolate(bool arg);
+
+ void setBloat(bool arg);
+
+ void setEntryEffect(EntryEffect arg);
+
+protected:
+ void reset();
+ virtual void initialize(int gIdx, int pIdx);
+ virtual void commit(int gIdx, int pIdx);
+
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ void prepareNextFrame();
+ QSGGeometryNode* buildParticleNodes();
+
+private slots:
+ void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
+
+ void spriteAdvance(int spriteIndex);
+private:
+ QUrl m_image_name;
+ QUrl m_colortable_name;
+ QUrl m_sizetable_name;
+ QUrl m_opacitytable_name;
+
+
+ QColor m_color;
+ qreal m_color_variation;
+
+ QSGGeometryNode *m_rootNode;
+ QHash<int, QSGGeometryNode *> m_nodes;
+ QHash<int, int> m_idxStarts;//TODO: Proper resizing will lead to needing a spriteEngine per particle - do this after sprite engine gains transparent sharing?
+ QList<QPair<int, int> > m_startsIdx;//Same data, optimized for alternate retrieval
+
+ int m_lastIdxStart;
+ QSGMaterial *m_material;
+
+ // derived values...
+
+ qreal m_alphaVariation;
+ qreal m_alpha;
+ qreal m_redVariation;
+ qreal m_greenVariation;
+ qreal m_blueVariation;
+ qreal m_rotation;
+ qreal m_rotationVariation;
+ qreal m_rotationSpeed;
+ qreal m_rotationSpeedVariation;
+ bool m_autoRotation;
+ QQuickDirection* m_xVector;
+ QQuickDirection* m_yVector;
+
+ QList<QQuickSprite*> m_sprites;
+ QQuickSpriteEngine* m_spriteEngine;
+ bool m_spritesInterpolate;
+
+ bool m_explicitColor;
+ bool m_explicitRotation;
+ bool m_explicitDeformation;
+ bool m_explicitAnimation;
+ QHash<int, QVector<QQuickParticleData*> > m_shadowData;
+ void clearShadows();
+ QQuickParticleData* getShadowDatum(QQuickParticleData* datum);
+
+ bool m_bloat;
+ PerformanceLevel perfLevel;
+
+ PerformanceLevel m_lastLevel;
+ bool m_debugMode;
+
+ template<class Vertex>
+ void initTexCoords(Vertex* v, int count){
+ Vertex* end = v + count;
+ while (v < end){
+ v[0].tx = 0;
+ v[0].ty = 0;
+
+ v[1].tx = 1;
+ v[1].ty = 0;
+
+ v[2].tx = 0;
+ v[2].ty = 1;
+
+ v[3].tx = 1;
+ v[3].ty = 1;
+
+ v += 4;
+ }
+ }
+
+ template<class MaterialData>
+ MaterialData* getState(QSGMaterial* m){
+ return static_cast<QSGSimpleMaterial<MaterialData> *>(m)->state();
+ }
+ EntryEffect m_entryEffect;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ULTRAPARTICLE_H
diff --git a/src/quick/particles/qquickitemparticle.cpp b/src/quick/particles/qquickitemparticle.cpp
new file mode 100644
index 0000000000..14ca085469
--- /dev/null
+++ b/src/quick/particles/qquickitemparticle.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitemparticle_p.h"
+#include <private/qquickvisualitemmodel_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QTimer>
+#include <QDeclarativeComponent>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass ItemParticle QQuickItemParticle
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief The ItemParticle element allows you to specify your own delegate to paint particles.
+
+*/
+
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::freeze(Item item)
+
+ Suspends the flow of time for the logical particle which item represents, allowing you to control its movement.
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::unfreeze(Item item)
+
+ Restarts the flow of time for the logical particle which item represents, allowing it to be moved by the particle system again.
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::take(Item item, bool prioritize)
+
+ Asks the ItemParticle to take over control of item. It will be emitted when there is a logical particle available.
+
+ By default items form a queue when waiting for a logical particle, but if prioritize is true then it will go immediately to the
+ head of the queue.
+*/
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::give(Item item)
+
+ Orders the ItemParticle to give you control of the item. It will cease controlling it and the item will lose its association to the logical particle.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ItemParticle::fade
+
+ If true, the item will automatically be faded in and out
+ at the ends of its lifetime. If false, you will have to
+ implement any entry effect yourself.
+
+ Default is true.
+*/
+/*!
+ \qmlproperty Component QtQuick.Particles2::ItemParticle::delegate
+
+ An instance of the delegate will be created for every logical
+ particle, and moved along with it.
+*/
+
+QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) :
+ QQuickParticlePainter(parent), m_fade(true), m_delegate(0)
+{
+ setFlag(QQuickItem::ItemHasContents);
+ QTimer* manageDelegates = new QTimer(this);//TODO: don't leak
+ connect(manageDelegates, SIGNAL(timeout()),
+ this, SLOT(tick()));
+ manageDelegates->setInterval(16);
+ manageDelegates->setSingleShot(false);
+ manageDelegates->start();
+}
+
+
+void QQuickItemParticle::freeze(QQuickItem* item)
+{
+ m_stasis << item;
+}
+
+
+void QQuickItemParticle::unfreeze(QQuickItem* item)
+{
+ m_stasis.remove(item);
+}
+
+void QQuickItemParticle::take(QQuickItem *item, bool prioritize)
+{
+ if (prioritize)
+ m_pendingItems.push_front(item);
+ else
+ m_pendingItems.push_back(item);
+}
+
+void QQuickItemParticle::give(QQuickItem *item)
+{
+ //TODO: This
+ Q_UNUSED(item);
+}
+
+void QQuickItemParticle::initialize(int gIdx, int pIdx)
+{
+ m_loadables << m_system->groupData[gIdx]->data[pIdx];//defer to other thread
+}
+
+void QQuickItemParticle::commit(int, int)
+{
+}
+
+void QQuickItemParticle::tick()
+{
+ foreach (QQuickItem* item, m_deletables){
+ if (m_fade)
+ item->setOpacity(0.);
+ item->setVisible(false);
+ QQuickItemParticleAttached* mpa;
+ if ((mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(item))))
+ mpa->detach();//reparent as well?
+ //TODO: Delete iff we created it
+ m_activeCount--;
+ }
+ m_deletables.clear();
+
+ foreach (QQuickParticleData* d, m_loadables){
+ if (m_stasis.contains(d->delegate))
+ qWarning() << "Current model particles prefers overwrite:false";
+ //remove old item from the particle that is dying to make room for this one
+ if (d->delegate)
+ m_deletables << d->delegate;
+ d->delegate = 0;
+ if (!m_pendingItems.isEmpty()){
+ d->delegate = m_pendingItems.front();
+ m_pendingItems.pop_front();
+ }else if (m_delegate){
+ d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this)));
+ }
+ if (d->delegate && d){//###Data can be zero if creating an item leads to a reset - this screws things up.
+ d->delegate->setX(d->curX() - d->delegate->width()/2);//TODO: adjust for system?
+ d->delegate->setY(d->curY() - d->delegate->height()/2);
+ QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate));
+ if (mpa){
+ mpa->m_mp = this;
+ mpa->attach();
+ }
+ d->delegate->setParentItem(this);
+ if (m_fade)
+ d->delegate->setOpacity(0.);
+ d->delegate->setVisible(false);//Will be set to true when we prepare the next frame
+ m_activeCount++;
+ }
+ }
+ m_loadables.clear();
+}
+
+void QQuickItemParticle::reset()
+{
+ QQuickParticlePainter::reset();
+ //TODO: Cleanup items?
+ m_loadables.clear();
+ //deletables?
+}
+
+
+QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
+{
+ //Dummy update just to get painting tick
+ if (m_pleaseReset){
+ m_pleaseReset = false;
+ reset();
+ }
+ prepareNextFrame();
+
+ update();//Get called again
+ if (n)
+ n->markDirty(QSGNode::DirtyMaterial);
+ return QQuickItem::updatePaintNode(n,d);
+}
+
+void QQuickItemParticle::prepareNextFrame()
+{
+ if (!m_system)
+ return;
+ qint64 timeStamp = m_system->systemSync(this);
+ qreal curT = timeStamp/1000.0;
+ qreal dt = curT - m_lastT;
+ m_lastT = curT;
+ if (!m_activeCount)
+ return;
+
+ //TODO: Size, better fade?
+ foreach (const QString &str, m_groups){
+ int gIdx = m_system->groupIds[str];
+ int count = m_system->groupData[gIdx]->size();
+
+ for (int i=0; i<count; i++){
+ QQuickParticleData* data = m_system->groupData[gIdx]->data[i];
+ QQuickItem* item = data->delegate;
+ if (!item)
+ continue;
+ qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan;
+ if (m_stasis.contains(item)) {
+ data->t += dt;//Stasis effect
+ continue;
+ }
+ if (t >= 1.0){//Usually happens from load
+ m_deletables << item;
+ data->delegate = 0;
+ }else{//Fade
+ data->delegate->setVisible(true);
+ if (m_fade){
+ qreal o = 1.;
+ if (t<0.2)
+ o = t*5;
+ if (t>0.8)
+ o = (1-t)*5;
+ item->setOpacity(o);
+ }
+ }
+ item->setX(data->curX() - item->width()/2 - m_systemOffset.x());
+ item->setY(data->curY() - item->height()/2 - m_systemOffset.y());
+ }
+ }
+}
+
+QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickItemParticleAttached(object);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickitemparticle_p.h b/src/quick/particles/qquickitemparticle_p.h
new file mode 100644
index 0000000000..33d2bcfb4b
--- /dev/null
+++ b/src/quick/particles/qquickitemparticle_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ITEMPARTICLE_H
+#define ITEMPARTICLE_H
+#include "qquickparticlepainter_p.h"
+#include <QPointer>
+#include <QSet>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickVisualDataModel;
+class QQuickItemParticleAttached;
+
+class QQuickItemParticle : public QQuickParticlePainter
+{
+ Q_OBJECT
+ Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged)
+ Q_PROPERTY(QDeclarativeComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+public:
+ explicit QQuickItemParticle(QQuickItem *parent = 0);
+
+ bool fade() const { return m_fade; }
+
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+ static QQuickItemParticleAttached *qmlAttachedProperties(QObject *object);
+ QDeclarativeComponent* delegate() const
+ {
+ return m_delegate;
+ }
+
+signals:
+ void fadeChanged();
+
+ void delegateChanged(QDeclarativeComponent* arg);
+
+public slots:
+ //TODO: Add a follow mode, where moving the delegate causes the logical particle to go with it?
+ void freeze(QQuickItem* item);
+ void unfreeze(QQuickItem* item);
+ void take(QQuickItem* item,bool prioritize=false);//take by modelparticle
+ void give(QQuickItem* item);//give from modelparticle
+
+ void setFade(bool arg){if (arg == m_fade) return; m_fade = arg; emit fadeChanged();}
+ void setDelegate(QDeclarativeComponent* arg)
+ {
+ if (m_delegate != arg) {
+ m_delegate = arg;
+ emit delegateChanged(arg);
+ }
+ }
+
+protected:
+ virtual void reset();
+ virtual void commit(int gIdx, int pIdx);
+ virtual void initialize(int gIdx, int pIdx);
+ void prepareNextFrame();
+private slots:
+ void tick();
+private:
+ QList<QQuickItem* > m_deletables;
+ QList< QQuickParticleData* > m_loadables;
+ bool m_fade;
+
+ QList<QQuickItem*> m_pendingItems;
+ QList<int> m_available;
+ QSet<QQuickItem*> m_stasis;
+ qreal m_lastT;
+ int m_activeCount;
+ QDeclarativeComponent* m_delegate;
+};
+
+class QQuickItemParticleAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickItemParticle* particle READ particle CONSTANT);
+public:
+ QQuickItemParticleAttached(QObject* parent)
+ : QObject(parent), m_mp(0)
+ {;}
+ QQuickItemParticle* particle() {return m_mp;}
+ void detach(){emit detached();}
+ void attach(){emit attached();}
+private:
+ QQuickItemParticle* m_mp;
+ friend class QQuickItemParticle;
+Q_SIGNALS:
+ void detached();
+ void attached();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPEINFO(QQuickItemParticle, QML_HAS_ATTACHED_PROPERTIES)
+
+QT_END_HEADER
+#endif // ITEMPARTICLE_H
diff --git a/src/quick/particles/qquicklineextruder.cpp b/src/quick/particles/qquicklineextruder.cpp
new file mode 100644
index 0000000000..73413537cc
--- /dev/null
+++ b/src/quick/particles/qquicklineextruder.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qquicklineextruder_p.h"
+#include <cmath>
+
+/*!
+ \qmlclass LineShape QQuickLineExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Shape
+ \brief The LineShape represents a line to Affectors and Emitter
+
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::LineShape::mirrored
+
+ By default, the line goes from (0,0) to (width, height) of the item that
+ this shape is being applied to.
+
+ If mirrored is set to true, this will be mirrored along the y axis.
+ The line will then go from (0,height) to (width, 0).
+*/
+
+QQuickLineExtruder::QQuickLineExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent), m_mirrored(false)
+{
+}
+
+QPointF QQuickLineExtruder::extrude(const QRectF &r)
+{
+ qreal x,y;
+ if (!r.height()){
+ x = r.width() * ((qreal)rand())/RAND_MAX;
+ y = 0;
+ }else{
+ y = r.height() * ((qreal)rand())/RAND_MAX;
+ if (!r.width()){
+ x = 0;
+ }else{
+ x = r.width()/r.height() * y;
+ if (m_mirrored)
+ x = r.width() - x;
+ }
+ }
+ return QPointF(x,y);
+}
diff --git a/src/quick/particles/qquicklineextruder_p.h b/src/quick/particles/qquicklineextruder_p.h
new file mode 100644
index 0000000000..8258b26043
--- /dev/null
+++ b/src/quick/particles/qquicklineextruder_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LINEEXTRUDER_H
+#define LINEEXTRUDER_H
+#include "qquickparticleextruder_p.h"
+
+class QQuickLineExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ //Default is topleft to bottom right. Flipped makes it topright to bottom left
+ Q_PROPERTY(bool mirrored READ mirrored WRITE setmirrored NOTIFY mirroredChanged)
+
+public:
+ explicit QQuickLineExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ bool mirrored() const
+ {
+ return m_mirrored;
+ }
+
+signals:
+
+ void mirroredChanged(bool arg);
+
+public slots:
+
+ void setmirrored(bool arg)
+ {
+ if (m_mirrored != arg) {
+ m_mirrored = arg;
+ emit mirroredChanged(arg);
+ }
+ }
+private:
+ bool m_mirrored;
+};
+
+#endif // LINEEXTRUDER_H
diff --git a/src/quick/particles/qquickmaskextruder.cpp b/src/quick/particles/qquickmaskextruder.cpp
new file mode 100644
index 0000000000..f4850712f0
--- /dev/null
+++ b/src/quick/particles/qquickmaskextruder.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickmaskextruder_p.h"
+#include <QImage>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass MaskShape QQuickMaskExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Shape
+ \brief The MaskShape element allows you to represent an image as a shape to affectors and emitters.
+
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::MaskShape::source
+
+ The image to use as the mask. Areas with non-zero opacity
+ will be considered inside the shape.
+*/
+
+
+QQuickMaskExtruder::QQuickMaskExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent)
+ , m_lastWidth(-1)
+ , m_lastHeight(-1)
+{
+}
+
+QPointF QQuickMaskExtruder::extrude(const QRectF &r)
+{
+ ensureInitialized(r);
+ if (!m_mask.count() || m_img.isNull())
+ return r.topLeft();
+ const QPointF p = m_mask[rand() % m_mask.count()];
+ //### Should random sub-pixel positioning be added?
+ return p + r.topLeft();
+}
+
+bool QQuickMaskExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list?
+ if (m_img.isNull())
+ return false;
+ QPoint p = point.toPoint() - bounds.topLeft().toPoint();
+ return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p);
+}
+
+void QQuickMaskExtruder::ensureInitialized(const QRectF &r)
+{
+ if (m_lastWidth == r.width() && m_lastHeight == r.height())
+ return;//Same as before
+ m_lastWidth = r.width();
+ m_lastHeight = r.height();
+
+ m_img = QImage();
+ m_mask.clear();
+ if (m_source.isEmpty())
+ return;
+ m_img = QImage(m_source.toLocalFile());
+ if (m_img.isNull()){
+ qWarning() << "MaskShape: Cannot load" << qPrintable(m_source.toLocalFile());
+ return;
+ }
+ m_img = m_img.createAlphaMask();
+ m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier
+ m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling?
+ for (int i=0; i<r.width(); i++){
+ for (int j=0; j<r.height(); j++){
+ if (m_img.pixelIndex(i,j))//Direct bit manipulation is presumably more efficient
+ m_mask << QPointF(i,j);
+ }
+ }
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickmaskextruder_p.h b/src/quick/particles/qquickmaskextruder_p.h
new file mode 100644
index 0000000000..180bcf26e2
--- /dev/null
+++ b/src/quick/particles/qquickmaskextruder_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MASKEXTRUDER_H
+#define MASKEXTRUDER_H
+#include "qquickparticleextruder_p.h"
+#include <QUrl>
+#include <QImage>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMaskExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+public:
+ explicit QQuickMaskExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+
+ QUrl source() const
+ {
+ return m_source;
+ }
+
+signals:
+
+ void sourceChanged(QUrl arg);
+
+public slots:
+
+ void setSource(QUrl arg)
+ {
+ if (m_source != arg) {
+ m_source = arg;
+ m_lastHeight = -1;//Trigger reset
+ m_lastWidth = -1;
+ emit sourceChanged(arg);
+ }
+ }
+private:
+ QUrl m_source;
+
+ void ensureInitialized(const QRectF &r);
+ int m_lastWidth;
+ int m_lastHeight;
+ QImage m_img;
+ QList<QPointF> m_mask;//TODO: More memory efficient datastructures
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // MASKEXTRUDER_H
diff --git a/src/quick/particles/qquickparticleaffector.cpp b/src/quick/particles/qquickparticleaffector.cpp
new file mode 100644
index 0000000000..5bfc699548
--- /dev/null
+++ b/src/quick/particles/qquickparticleaffector.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticleaffector_p.h"
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Affector QQuickParticleAffector
+ \inqmlmodule QtQuick.Particles 2
+ \brief Affector elements can alter the attributes of logical particles at any point in their lifetime.
+
+ The base Affector does not alter any attributes, but can be used to emit a signal
+ when a particle meets certain conditions.
+
+ If an affector has a defined size, then it will only affect particles within its size and position on screen.
+
+ Affectors have different performance characteristics to the other particle system elements. In particular,
+ they have some simplifications to try to maintain a simulation at real-time or faster. When running a system
+ with Affectors, irregular frame timings that grow too large ( > one second per frame) will cause the Affectors
+ to try and cut corners with a faster but less accurate simulation. If the system has multiple affectors the order
+ in which they are applied is not guaranteed, and when simulating larger time shifts they will simulate the whole
+ shift each, which can lead to different results compared to smaller time shifts.
+
+ Accurate simulation for large numbers of particles (hundreds) with multiple affectors may be possible on some hardware,
+ but on less capable hardware you should expect small irregularties in the simulation as simulates with worse granularity.
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::Affector::system
+ This is the system which will be affected by the element.
+ If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+ \qmlproperty list<string> QtQuick.Particles2::Affector::groups
+ Which logical particle groups will be affected.
+
+ If empty, it will affect all particles.
+*/
+/*!
+ \qmlproperty list<string> QtQuick.Particles2::Affector::whenCollidingWith
+ If any logical particle groups are specified here, then the affector
+ will only be triggered if the particle being examined intersects with
+ a particle of one of these groups.
+
+ This is different from the groups property. The groups property selects which
+ particles might be examined, and if they meet other criteria (including being
+ within the bounds of the Affector, modified by shape) then they will be tested
+ again to see if they intersect with a particles from one of the particle groups
+ in whenCollidingWith.
+
+ By default, no groups are specified.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Affector::enabled
+ If enabled is set to false, this affector will not affect any particles.
+
+ Usually this is used to conditionally turn an affector on or off.
+
+ Default value is true.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Affector::once
+ If once is set to true, this affector will only affect each particle
+ once in their lifetimes. If the affector normally simulates a continuous
+ effect over time, then it will simulate the effect of one second of time
+ the one instant it affects the particle.
+
+ Default value is false.
+*/
+/*!
+ \qmlproperty Shape QtQuick.Particles2::Affector::shape
+ If a size has been defined, the shape property can be used to affect a
+ non-rectangular area.
+*/
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::onAffected(x, y)
+
+ This signal is emitted each time the affector actually affects a particle.
+
+ x,y are the coordinates of the affected particle, relative to the ParticleSystem.
+
+*/
+
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle particle, real dt)
+
+ This handler is called when particles are selected to be affected.
+
+ dt is the time since the last time it was affected. Use dt to normalize
+ trajectory manipulations to real time.
+
+ Note that JS is slower to execute, so it is not recommended to use this in
+ high-volume particle systems.
+*/
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::affected(real x, real y)
+
+ This handler is called when a particle is selected to be affected. It will
+ only be called if signal is set to true.
+
+ x,y is the particles current position.
+*/
+QQuickParticleAffector::QQuickParticleAffector(QQuickItem *parent) :
+ QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_onceOff(false), m_enabled(true)
+ , m_system(0), m_updateIntSet(false), m_shape(new QQuickParticleExtruder(this))
+{
+}
+
+bool QQuickParticleAffector::isAffectedConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("affected(qreal,qreal)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+
+void QQuickParticleAffector::componentComplete()
+{
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
+ QQuickItem::componentComplete();
+}
+
+bool QQuickParticleAffector::activeGroup(int g) {
+ if (m_updateIntSet){
+ m_groupIds.clear();
+ foreach (const QString &p, m_groups)
+ m_groupIds << m_system->groupIds[p];//###Can this occur before group ids are properly assigned?
+ m_updateIntSet = false;
+ }
+ return m_groupIds.isEmpty() || m_groupIds.contains(g);
+}
+
+bool QQuickParticleAffector::shouldAffect(QQuickParticleData* d)
+{
+ if (!d)
+ return false;
+ if (activeGroup(d->group)){
+ if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))
+ || !d->stillAlive())
+ return false;
+ //Need to have previous location for affected anyways
+ if (width() == 0 || height() == 0
+ || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()), QPointF(d->curX(), d->curY()))){
+ if (m_whenCollidingWith.isEmpty() || isColliding(d)){
+ return true;
+ }
+ }
+ }
+ return false;
+
+}
+
+void QQuickParticleAffector::postAffect(QQuickParticleData* d)
+{
+ m_system->needsReset << d;
+ if (m_onceOff)
+ m_onceOffed << qMakePair(d->group, d->index);
+ if (isAffectedConnected())
+ emit affected(d->curX(), d->curY());
+}
+
+const qreal QQuickParticleAffector::simulationDelta = 0.020;
+const qreal QQuickParticleAffector::simulationCutoff = 1.000;//If this goes above 1.0, then m_once behaviour needs special codepath
+
+void QQuickParticleAffector::affectSystem(qreal dt)
+{
+ if (!m_enabled)
+ return;
+ //If not reimplemented, calls affectParticle per particle
+ //But only on particles in targeted system/area
+ updateOffsets();//### Needed if an ancestor is transformed.
+ if (m_onceOff)
+ dt = 1.0;
+ foreach (QQuickParticleGroupData* gd, m_system->groupData) {
+ if (activeGroup(m_system->groupData.key(gd))) {
+ foreach (QQuickParticleData* d, gd->data) {
+ if (shouldAffect(d)) {
+ bool affected = false;
+ qreal myDt = dt;
+ if (!m_ignoresTime && myDt < simulationCutoff) {
+ int realTime = m_system->timeInt;
+ m_system->timeInt -= myDt * 1000.0;
+ while (myDt > simulationDelta) {
+ m_system->timeInt += simulationDelta * 1000.0;
+ if (d->alive())//Only affect during the parts it was alive for
+ affected = affectParticle(d, simulationDelta) || affected;
+ myDt -= simulationDelta;
+ }
+ m_system->timeInt = realTime;
+ }
+ if (myDt > 0.0)
+ affected = affectParticle(d, myDt) || affected;
+ if (affected)
+ postAffect(d);
+ }
+ }
+ }
+ }
+}
+
+bool QQuickParticleAffector::affectParticle(QQuickParticleData *, qreal )
+{
+ return true;
+}
+
+void QQuickParticleAffector::reset(QQuickParticleData* pd)
+{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
+ if (m_onceOff)
+ if (activeGroup(pd->group))
+ m_onceOffed.remove(qMakePair(pd->group, pd->index));
+}
+
+void QQuickParticleAffector::updateOffsets()
+{
+ if (m_system)
+ m_offset = m_system->mapFromItem(this, QPointF(0, 0));
+}
+
+bool QQuickParticleAffector::isColliding(QQuickParticleData *d)
+{
+ qreal myCurX = d->curX();
+ qreal myCurY = d->curY();
+ qreal myCurSize = d->curSize()/2;
+ foreach (const QString &group, m_whenCollidingWith){
+ foreach (QQuickParticleData* other, m_system->groupData[m_system->groupIds[group]]->data){
+ if (!other->stillAlive())
+ continue;
+ qreal otherCurX = other->curX();
+ qreal otherCurY = other->curY();
+ qreal otherCurSize = other->curSize()/2;
+ if ((myCurX + myCurSize > otherCurX - otherCurSize
+ && myCurX - myCurSize < otherCurX + otherCurSize)
+ && (myCurY + myCurSize > otherCurY - otherCurSize
+ && myCurY - myCurSize < otherCurY + otherCurSize))
+ return true;
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickparticleaffector_p.h b/src/quick/particles/qquickparticleaffector_p.h
new file mode 100644
index 0000000000..94eba70ae4
--- /dev/null
+++ b/src/quick/particles/qquickparticleaffector_p.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLEAFFECTOR_H
+#define PARTICLEAFFECTOR_H
+
+#include <QObject>
+#include "qquickparticlesystem_p.h"
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleAffector : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
+ Q_PROPERTY(QStringList whenCollidingWith READ whenCollidingWith WRITE setWhenCollidingWith NOTIFY whenCollidingWithChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool once READ onceOff WRITE setOnceOff NOTIFY onceChanged)
+ Q_PROPERTY(QQuickParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged)
+
+public:
+ explicit QQuickParticleAffector(QQuickItem *parent = 0);
+ virtual void affectSystem(qreal dt);
+ virtual void reset(QQuickParticleData*);//As some store their own data per particle?
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+ QStringList groups() const
+ {
+ return m_groups;
+ }
+
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ bool onceOff() const
+ {
+ return m_onceOff;
+ }
+
+ QQuickParticleExtruder* shape() const
+ {
+ return m_shape;
+ }
+
+ QStringList whenCollidingWith() const
+ {
+ return m_whenCollidingWith;
+ }
+
+signals:
+
+ void systemChanged(QQuickParticleSystem* arg);
+
+ void groupsChanged(QStringList arg);
+
+ void enabledChanged(bool arg);
+
+ void onceChanged(bool arg);
+
+ void shapeChanged(QQuickParticleExtruder* arg);
+
+ void affected(qreal x, qreal y);
+
+ void whenCollidingWithChanged(QStringList arg);
+
+public slots:
+void setSystem(QQuickParticleSystem* arg)
+{
+ if (m_system != arg) {
+ m_system = arg;
+ m_system->registerParticleAffector(this);
+ emit systemChanged(arg);
+ }
+}
+
+void setGroups(QStringList arg)
+{
+ if (m_groups != arg) {
+ m_groups = arg;
+ m_updateIntSet = true;
+ emit groupsChanged(arg);
+ }
+}
+
+void setEnabled(bool arg)
+{
+ if (m_enabled != arg) {
+ m_enabled = arg;
+ emit enabledChanged(arg);
+ }
+}
+
+void setOnceOff(bool arg)
+{
+ if (m_onceOff != arg) {
+ m_onceOff = arg;
+ m_needsReset = true;
+ emit onceChanged(arg);
+ }
+}
+
+void setShape(QQuickParticleExtruder* arg)
+{
+ if (m_shape != arg) {
+ m_shape = arg;
+ emit shapeChanged(arg);
+ }
+}
+
+void setWhenCollidingWith(QStringList arg)
+{
+ if (m_whenCollidingWith != arg) {
+ m_whenCollidingWith = arg;
+ emit whenCollidingWithChanged(arg);
+ }
+}
+public slots:
+ void updateOffsets();
+
+protected:
+ friend class QQuickParticleSystem;
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+ bool m_needsReset:1;//### What is this really saving?
+ bool m_ignoresTime:1;
+ bool m_onceOff:1;
+ bool m_enabled:1;
+
+ QQuickParticleSystem* m_system;
+ QStringList m_groups;
+ bool activeGroup(int g);
+ bool shouldAffect(QQuickParticleData* datum);//Call to do the logic on whether it is affecting that datum
+ void postAffect(QQuickParticleData* datum);//Call to do the post-affect logic on particles which WERE affected(once off, needs reset, affected signal)
+ virtual void componentComplete();
+ QPointF m_offset;
+ bool isAffectedConnected();
+ static const qreal simulationDelta;
+ static const qreal simulationCutoff;
+private:
+ QSet<int> m_groupIds;
+ QSet<QPair<int, int> > m_onceOffed;
+ bool m_updateIntSet;
+
+ QQuickParticleExtruder* m_shape;
+
+ QStringList m_whenCollidingWith;
+
+ bool isColliding(QQuickParticleData* d);
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // PARTICLEAFFECTOR_H
diff --git a/src/quick/particles/qquickparticleemitter.cpp b/src/quick/particles/qquickparticleemitter.cpp
new file mode 100644
index 0000000000..f227256f1b
--- /dev/null
+++ b/src/quick/particles/qquickparticleemitter.cpp
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticleemitter_p.h"
+#include <private/qdeclarativeengine_p.h>
+QT_BEGIN_NAMESPACE
+
+
+/*!
+ \qmlclass Emitter QQuickParticleEmitter
+ \inqmlmodule QtQuick.Particles 2
+ \brief The Emitter element allows you to emit logical particles.
+
+ This element emits logical particles into the ParticleSystem, with the
+ given starting attributes.
+
+ Note that logical particles are not
+ automatically rendered, you will need to have one or more
+ ParticlePainter elements visualizing them.
+
+ Note that the given starting attributes can be modified at any point
+ in the particle's lifetime by any Affector element in the same
+ ParticleSystem. This includes attributes like lifespan.
+*/
+
+
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::Emitter::system
+
+ This is the Particle system that the Emitter will emit into.
+ This can be omitted if the Emitter is a direct child of the ParticleSystem
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::Emitter::group
+
+ This is the logical particle group which it will emit into.
+
+ Default value is "" (empty string).
+*/
+/*!
+ \qmlproperty Shape QtQuick.Particles2::Emitter::shape
+
+ This shape is applied with the size of the Emitter. Particles will be emitted
+ randomly from any area covered by the shape.
+
+ The default shape is a filled in rectangle, which corresponds to the full bounding
+ box of the Emitter.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Emitter::emitting
+
+ If set to false, the emitter will cease emissions until it is set to true.
+
+ Default value is true.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::emitRate
+
+ Number of particles emitted per second.
+
+ Default value is 10 particles per second.
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::lifeSpan
+
+ The time in milliseconds each emitted particle should last for.
+
+ If you do not want particles to automatically die after a time, for example if
+ you wish to dispose of them manually, set lifeSpan to Emitter.InfiniteLife.
+
+ lifeSpans greater than or equal to 600000 (10 minutes) will be treated as infinite.
+ Particles with lifeSpans less than or equal to 0 will start out dead.
+
+ Default value is 1000 (one second).
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::lifeSpanVariation
+
+ Particle lifespans will vary by up to this much in either direction.
+
+ Default value is 0.
+*/
+
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::maximumEmitted
+
+ The maximum number of particles at a time that this emitter will have alive.
+
+ This can be set as a performance optimization (when using burst and pulse) or
+ to stagger emissions.
+
+ If this is set to a number below zero, then there is no maximum limit on the number
+ of particles this emitter can have alive.
+
+ The default value is -1.
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::startTime
+
+ If this value is set when the emitter is loaded, then it will emit particles from the
+ past, up to startTime milliseconds ago. These will simulate as if they were emitted then,
+ but will not have any affectors applied to them. Affectors will take effect from the present time.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::size
+
+ The size in pixels of the particles at the start of their life.
+
+ Default value is 16.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::endSize
+
+ The size in pixels of the particles at the end of their life. Size will
+ be linearly interpolated during the life of the particle from this value and
+ size. If endSize is -1, then the size of the particle will remain constant at
+ the starting size.
+
+ Default value is -1.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::sizeVariation
+
+ The size of a particle can vary by this much up or down from size/endSize. The same
+ random addition is made to both size and endSize for a single particle.
+
+ Default value is 0.
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Emitter::speed
+
+ The starting speed of the particles emitted.
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Emitter::acceleration
+
+ The starting acceleraton of the particles emitted.
+*/
+/*!
+ \qmlproperty qreal QtQuick.Particles2::Emitter::speedFromMovement
+
+ If this value is non-zero, then any movement of the emitter will provide additional
+ starting velocity to the particles based on the movement. The additional vector will be the
+ same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters
+ movement multiplied by speedFromMovement.
+
+ Default value is 0.
+*/
+
+/*!
+ \qmlsignal QtQuick.Particles2::Emitter::onEmitParticles(Array particles)
+
+ This handler is called when particles are emitted. particles is a javascript
+ array of Particle objects. You can modify particle attributes directly within the handler.
+
+ Note that JS is slower to execute, so it is not recommended to use this in
+ high-volume particle systems.
+*/
+
+/*! \qmlmethod QtQuick.Particles2::Emitter::burst(int count)
+
+ Emits count particles from this emitter immediately.
+*/
+
+/*! \qmlmethod QtQuick.Particles2::Emitter::burst(int x, int y, int count)
+
+ Emits count particles from this emitter immediately. The particles are emitted
+ as if the Emitter was positioned at x,y but all other properties are the same.
+*/
+
+/*! \qmlmethod QtQuick.Particles2::Emitter::pulse(int duration)
+
+ If the emitter is not enabled, enables it for duration milliseconds and then switches
+ it back off.
+*/
+
+QQuickParticleEmitter::QQuickParticleEmitter(QQuickItem *parent) :
+ QQuickItem(parent)
+ , m_particlesPerSecond(10)
+ , m_particleDuration(1000)
+ , m_particleDurationVariation(0)
+ , m_enabled(true)
+ , m_system(0)
+ , m_extruder(0)
+ , m_defaultExtruder(0)
+ , m_speed(&m_nullVector)
+ , m_acceleration(&m_nullVector)
+ , m_particleSize(16)
+ , m_particleEndSize(-1)
+ , m_particleSizeVariation(0)
+ , m_startTime(0)
+ , m_overwrite(true)
+ , m_pulseLeft(0)
+ , m_maxParticleCount(-1)
+ , m_speed_from_movement(0)
+ , m_reset_last(true)
+ , m_last_timestamp(-1)
+ , m_last_emission(0)
+
+{
+ //TODO: Reset speed/acc back to null vector? Or allow null pointer?
+ connect(this, SIGNAL(maximumEmittedChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+ connect(this, SIGNAL(particlesPerSecondChanged(qreal)),
+ this, SIGNAL(particleCountChanged()));
+ connect(this, SIGNAL(particleDurationChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+}
+
+QQuickParticleEmitter::~QQuickParticleEmitter()
+{
+ if (m_defaultExtruder)
+ delete m_defaultExtruder;
+}
+
+bool QQuickParticleEmitter::isEmitConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("emitParticles(QDeclarativeV8Handle)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+void QQuickParticleEmitter::componentComplete()
+{
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
+ QQuickItem::componentComplete();
+}
+
+void QQuickParticleEmitter::setEnabled(bool arg)
+{
+ if (m_enabled != arg) {
+ m_enabled = arg;
+ emit enabledChanged(arg);
+ }
+}
+
+
+QQuickParticleExtruder* QQuickParticleEmitter::effectiveExtruder()
+{
+ if (m_extruder)
+ return m_extruder;
+ if (!m_defaultExtruder)
+ m_defaultExtruder = new QQuickParticleExtruder;
+ return m_defaultExtruder;
+}
+
+void QQuickParticleEmitter::pulse(int milliseconds)
+{
+ if (!particleCount())
+ qWarning() << "pulse called on an emitter with a particle count of zero";
+ if (!m_enabled)
+ m_pulseLeft = milliseconds;
+}
+
+void QQuickParticleEmitter::burst(int num)
+{
+ if (!particleCount())
+ qWarning() << "burst called on an emitter with a particle count of zero";
+ m_burstQueue << qMakePair(num, QPointF(x(), y()));
+}
+
+void QQuickParticleEmitter::burst(int num, qreal x, qreal y)
+{
+ if (!particleCount())
+ qWarning() << "burst called on an emitter with a particle count of zero";
+ m_burstQueue << qMakePair(num, QPointF(x, y));
+}
+
+void QQuickParticleEmitter::setMaxParticleCount(int arg)
+{
+ if (m_maxParticleCount != arg) {
+ if (arg < 0 && m_maxParticleCount >= 0){
+ connect(this, SIGNAL(particlesPerSecondChanged(qreal)),
+ this, SIGNAL(particleCountChanged()));
+ connect(this, SIGNAL(particleDurationChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+ }else if (arg >= 0 && m_maxParticleCount < 0){
+ disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)),
+ this, SIGNAL(particleCountChanged()));
+ disconnect(this, SIGNAL(particleDurationChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+ }
+ m_overwrite = arg < 0;
+ m_maxParticleCount = arg;
+ emit maximumEmittedChanged(arg);
+ }
+}
+
+int QQuickParticleEmitter::particleCount() const
+{
+ if (m_maxParticleCount >= 0)
+ return m_maxParticleCount;
+ return m_particlesPerSecond*((m_particleDuration+m_particleDurationVariation)/1000.0);
+}
+
+void QQuickParticleEmitter::setSpeedFromMovement(qreal t)
+{
+ if (t == m_speed_from_movement)
+ return;
+ m_speed_from_movement = t;
+ emit speedFromMovementChanged();
+}
+
+void QQuickParticleEmitter::reset()
+{
+ m_reset_last = true;
+}
+
+void QQuickParticleEmitter::emitWindow(int timeStamp)
+{
+ if (m_system == 0)
+ return;
+ if ((!m_enabled || !m_particlesPerSecond)&& !m_pulseLeft && m_burstQueue.isEmpty()){
+ m_reset_last = true;
+ return;
+ }
+
+ if (m_reset_last) {
+ m_last_emitter = m_last_last_emitter = QPointF(x(), y());
+ if (m_last_timestamp == -1)
+ m_last_timestamp = (timeStamp - m_startTime)/1000.;
+ else
+ m_last_timestamp = timeStamp/1000.;
+ m_last_emission = m_last_timestamp;
+ m_reset_last = false;
+ m_emitCap = particleCount();
+ }
+
+ if (m_pulseLeft){
+ m_pulseLeft -= timeStamp - m_last_timestamp * 1000.;
+ if (m_pulseLeft < 0){
+ if (!m_enabled)
+ timeStamp += m_pulseLeft;
+ m_pulseLeft = 0;
+ }
+ }
+ qreal time = timeStamp / 1000.;
+ qreal particleRatio = 1. / m_particlesPerSecond;
+ qreal pt = m_last_emission;
+ qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
+ if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
+ pt = time - maxLife;
+
+ qreal opt = pt; // original particle time
+ qreal dt = time - m_last_timestamp; // timestamp delta...
+ if (!dt)
+ dt = 0.000001;
+
+ // emitter difference since last...
+ qreal dex = (x() - m_last_emitter.x());
+ qreal dey = (y() - m_last_emitter.y());
+
+ qreal ax = (m_last_last_emitter.x() + m_last_emitter.x()) / 2;
+ qreal bx = m_last_emitter.x();
+ qreal cx = (x() + m_last_emitter.x()) / 2;
+ qreal ay = (m_last_last_emitter.y() + m_last_emitter.y()) / 2;
+ qreal by = m_last_emitter.y();
+ qreal cy = (y() + m_last_emitter.y()) / 2;
+
+ qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
+ qreal emitter_x_offset = m_last_emitter.x() - x();
+ qreal emitter_y_offset = m_last_emitter.y() - y();
+ if (!m_burstQueue.isEmpty() && !m_pulseLeft && !m_enabled)//'outside time' emissions only
+ pt = time;
+
+ QList<QQuickParticleData*> toEmit;
+
+ while ((pt < time && m_emitCap) || !m_burstQueue.isEmpty()) {
+ //int pos = m_last_particle % m_particle_count;
+ QQuickParticleData* datum = m_system->newDatum(m_system->groupIds[m_group], !m_overwrite);
+ if (datum){//actually emit(otherwise we've been asked to skip this one)
+ datum->e = this;//###useful?
+ qreal t = 1 - (pt - opt) / dt;
+ qreal vx =
+ - 2 * ax * (1 - t)
+ + 2 * bx * (1 - 2 * t)
+ + 2 * cx * t;
+ qreal vy =
+ - 2 * ay * (1 - t)
+ + 2 * by * (1 - 2 * t)
+ + 2 * cy * t;
+
+
+ // Particle timestamp
+ datum->t = pt;
+ datum->lifeSpan =
+ (m_particleDuration
+ + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
+ / 1000.0;
+
+ if (datum->lifeSpan >= m_system->maxLife){
+ datum->lifeSpan = m_system->maxLife;
+ m_emitCap--;//emitCap keeps us from reemitting 'infinite' particles after their life. Unless you reset the emitter.
+ }
+
+ // Particle position
+ QRectF boundsRect;
+ if (!m_burstQueue.isEmpty()){
+ boundsRect = QRectF(m_burstQueue.first().second.x() - x(), m_burstQueue.first().second.y() - y(),
+ width(), height());
+ } else {
+ boundsRect = QRectF(emitter_x_offset + dex * (pt - opt) / dt, emitter_y_offset + dey * (pt - opt) / dt
+ , width(), height());
+ }
+ QPointF newPos = effectiveExtruder()->extrude(boundsRect);
+ datum->x = newPos.x();
+ datum->y = newPos.y();
+
+ // Particle speed
+ const QPointF &speed = m_speed->sample(newPos);
+ datum->vx = speed.x()
+ + m_speed_from_movement * vx;
+ datum->vy = speed.y()
+ + m_speed_from_movement * vy;
+
+ // Particle acceleration
+ const QPointF &accel = m_acceleration->sample(newPos);
+ datum->ax = accel.x();
+ datum->ay = accel.y();
+
+ // Particle size
+ float sizeVariation = -m_particleSizeVariation
+ + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
+
+ float size = qMax((qreal)0.0 , m_particleSize + sizeVariation);
+ float endSize = qMax((qreal)0.0 , sizeAtEnd + sizeVariation);
+
+ datum->size = size;// * float(m_emitting);
+ datum->endSize = endSize;// * float(m_emitting);
+
+ toEmit << datum;
+ }
+ if (m_burstQueue.isEmpty()){
+ pt += particleRatio;
+ }else{
+ m_burstQueue.first().first--;
+ if (m_burstQueue.first().first <= 0)
+ m_burstQueue.pop_front();
+ }
+ }
+
+ if (isEmitConnected()) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
+ v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
+ for (int i=0; i<toEmit.size(); i++)
+ array->Set(i, toEmit[i]->v8Value().toHandle());
+
+ emitParticles(QDeclarativeV8Handle::fromHandle(array));//A chance for arbitrary JS changes
+ }
+ foreach (QQuickParticleData* d, toEmit)
+ m_system->emitParticle(d);
+
+ m_last_emission = pt;
+
+ m_last_last_last_emitter = m_last_last_emitter;
+ m_last_last_emitter = m_last_emitter;
+ m_last_emitter = QPointF(x(), y());
+ m_last_timestamp = time;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickparticleemitter_p.h b/src/quick/particles/qquickparticleemitter_p.h
new file mode 100644
index 0000000000..bfdb63eef7
--- /dev/null
+++ b/src/quick/particles/qquickparticleemitter_p.h
@@ -0,0 +1,349 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLEEMITTER_H
+#define PARTICLEEMITTER_H
+
+#include <QtQuick/QQuickItem>
+#include <QDebug>
+#include "qquickparticlesystem_p.h"
+#include "qquickparticleextruder_p.h"
+#include "qquickdirection_p.h"
+
+#include <QList>
+#include <QPair>
+#include <QPointF>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleEmitter : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+ Q_PROPERTY(QString group READ group WRITE setGroup NOTIFY groupChanged)
+ Q_PROPERTY(QQuickParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
+
+ Q_PROPERTY(qreal emitRate READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged)
+ Q_PROPERTY(int lifeSpan READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged)
+ Q_PROPERTY(int lifeSpanVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged)
+ Q_PROPERTY(int maximumEmitted READ maxParticleCount WRITE setMaxParticleCount NOTIFY maximumEmittedChanged)
+
+ Q_PROPERTY(qreal size READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged)
+ Q_PROPERTY(qreal endSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged)
+ Q_PROPERTY(qreal sizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged)
+
+ Q_PROPERTY(QQuickDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged)
+ Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged)
+ Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged)
+
+ Q_ENUMS(Lifetime)
+public:
+ explicit QQuickParticleEmitter(QQuickItem *parent = 0);
+ virtual ~QQuickParticleEmitter();
+ virtual void emitWindow(int timeStamp);
+
+ enum Lifetime {
+ InfiniteLife = QQuickParticleSystem::maxLife
+ };
+
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ qreal particlesPerSecond() const
+ {
+ return m_particlesPerSecond;
+ }
+
+ int particleDuration() const
+ {
+ return m_particleDuration;
+ }
+
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+ QString group() const
+ {
+ return m_group;
+ }
+
+ int particleDurationVariation() const
+ {
+ return m_particleDurationVariation;
+ }
+
+ qreal speedFromMovement() const { return m_speed_from_movement; }
+ void setSpeedFromMovement(qreal s);
+ virtual void componentComplete();
+signals:
+ void emitParticles(QDeclarativeV8Handle particles);
+ void particlesPerSecondChanged(qreal);
+ void particleDurationChanged(int);
+ void enabledChanged(bool);
+
+ void systemChanged(QQuickParticleSystem* arg);
+
+ void groupChanged(QString arg);
+
+ void particleDurationVariationChanged(int arg);
+
+ void extruderChanged(QQuickParticleExtruder* arg);
+
+ void particleSizeChanged(qreal arg);
+
+ void particleEndSizeChanged(qreal arg);
+
+ void particleSizeVariationChanged(qreal arg);
+
+ void speedChanged(QQuickDirection * arg);
+
+ void accelerationChanged(QQuickDirection * arg);
+
+ void maximumEmittedChanged(int arg);
+ void particleCountChanged();
+
+ void speedFromMovementChanged();
+
+ void startTimeChanged(int arg);
+
+public slots:
+ void pulse(int milliseconds);
+ void burst(int num);
+ void burst(int num, qreal x, qreal y);
+
+ void setEnabled(bool arg);
+
+ void setParticlesPerSecond(qreal arg)
+ {
+ if (m_particlesPerSecond != arg) {
+ m_particlesPerSecond = arg;
+ emit particlesPerSecondChanged(arg);
+ }
+ }
+
+ void setParticleDuration(int arg)
+ {
+ if (m_particleDuration != arg) {
+ m_particleDuration = arg;
+ emit particleDurationChanged(arg);
+ }
+ }
+
+ void setSystem(QQuickParticleSystem* arg)
+ {
+ if (m_system != arg) {
+ m_system = arg;
+ m_system->registerParticleEmitter(this);
+ emit systemChanged(arg);
+ }
+ }
+
+ void setGroup(QString arg)
+ {
+ if (m_group != arg) {
+ m_group = arg;
+ emit groupChanged(arg);
+ }
+ }
+
+ void setParticleDurationVariation(int arg)
+ {
+ if (m_particleDurationVariation != arg) {
+ m_particleDurationVariation = arg;
+ emit particleDurationVariationChanged(arg);
+ }
+ }
+ void setExtruder(QQuickParticleExtruder* arg)
+ {
+ if (m_extruder != arg) {
+ m_extruder = arg;
+ emit extruderChanged(arg);
+ }
+ }
+
+ void setParticleSize(qreal arg)
+ {
+ if (m_particleSize != arg) {
+ m_particleSize = arg;
+ emit particleSizeChanged(arg);
+ }
+ }
+
+ void setParticleEndSize(qreal arg)
+ {
+ if (m_particleEndSize != arg) {
+ m_particleEndSize = arg;
+ emit particleEndSizeChanged(arg);
+ }
+ }
+
+ void setParticleSizeVariation(qreal arg)
+ {
+ if (m_particleSizeVariation != arg) {
+ m_particleSizeVariation = arg;
+ emit particleSizeVariationChanged(arg);
+ }
+ }
+
+ void setSpeed(QQuickDirection * arg)
+ {
+ if (m_speed != arg) {
+ m_speed = arg;
+ emit speedChanged(arg);
+ }
+ }
+
+ void setAcceleration(QQuickDirection * arg)
+ {
+ if (m_acceleration != arg) {
+ m_acceleration = arg;
+ emit accelerationChanged(arg);
+ }
+ }
+
+ void setMaxParticleCount(int arg);
+
+ void setStartTime(int arg)
+ {
+ if (m_startTime != arg) {
+ m_startTime = arg;
+ emit startTimeChanged(arg);
+ }
+ }
+
+ virtual void reset();
+public:
+ int particleCount() const;
+
+ QQuickParticleExtruder* extruder() const
+ {
+ return m_extruder;
+ }
+
+ qreal particleSize() const
+ {
+ return m_particleSize;
+ }
+
+ qreal particleEndSize() const
+ {
+ return m_particleEndSize;
+ }
+
+ qreal particleSizeVariation() const
+ {
+ return m_particleSizeVariation;
+ }
+
+ QQuickDirection * speed() const
+ {
+ return m_speed;
+ }
+
+ QQuickDirection * acceleration() const
+ {
+ return m_acceleration;
+ }
+
+ int maxParticleCount() const
+ {
+ return m_maxParticleCount;
+ }
+
+ int startTime() const
+ {
+ return m_startTime;
+ }
+
+protected:
+ qreal m_particlesPerSecond;
+ int m_particleDuration;
+ int m_particleDurationVariation;
+ bool m_enabled;
+ QQuickParticleSystem* m_system;
+ QString m_group;
+ QQuickParticleExtruder* m_extruder;
+ QQuickParticleExtruder* m_defaultExtruder;
+ QQuickParticleExtruder* effectiveExtruder();
+ QQuickDirection * m_speed;
+ QQuickDirection * m_acceleration;
+ qreal m_particleSize;
+ qreal m_particleEndSize;
+ qreal m_particleSizeVariation;
+
+ qreal m_speedFromMovement;
+ int m_startTime;
+ bool m_overwrite;
+
+ int m_pulseLeft;
+ QList<QPair<int, QPointF > > m_burstQueue;
+ int m_maxParticleCount;
+
+ //Used in default implementation, but might be useful
+ qreal m_speed_from_movement;
+
+ int m_emitCap;
+ bool m_reset_last;
+ qreal m_last_timestamp;
+ qreal m_last_emission;
+
+ QPointF m_last_emitter;
+ QPointF m_last_last_emitter;
+ QPointF m_last_last_last_emitter;
+
+ bool isEmitConnected();
+private:
+ QQuickDirection m_nullVector;
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // PARTICLEEMITTER_H
diff --git a/src/quick/particles/qquickparticleextruder.cpp b/src/quick/particles/qquickparticleextruder.cpp
new file mode 100644
index 0000000000..8b25de26be
--- /dev/null
+++ b/src/quick/particles/qquickparticleextruder.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Shape QQuickParticleExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \brief The Shape element allows you to specify an area for affectors and emitter.
+
+ The base class is just a rectangle.
+*/
+
+QQuickParticleExtruder::QQuickParticleExtruder(QObject *parent) :
+ QObject(parent)
+{
+}
+
+QPointF QQuickParticleExtruder::extrude(const QRectF &rect)
+{
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+}
+
+bool QQuickParticleExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ return bounds.contains(point);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickparticleextruder_p.h b/src/quick/particles/qquickparticleextruder_p.h
new file mode 100644
index 0000000000..d1f7008a31
--- /dev/null
+++ b/src/quick/particles/qquickparticleextruder_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLEEXTRUDER_H
+#define PARTICLEEXTRUDER_H
+
+#include <QObject>
+#include <QRectF>
+#include <QPointF>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleExtruder : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickParticleExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+
+signals:
+public slots:
+protected:
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // PARTICLEEXTRUDER_H
diff --git a/src/quick/particles/qquickparticlegroup.cpp b/src/quick/particles/qquickparticlegroup.cpp
new file mode 100644
index 0000000000..5486339611
--- /dev/null
+++ b/src/quick/particles/qquickparticlegroup.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticlegroup_p.h"
+
+/*!
+ \qmlclass ParticleGroup QQuickParticleGroup
+ \inqmlmodule QtQuick.Particles 2
+ \brief ParticleGroup elements allow you to set attributes on a logical particle group.
+
+ This element allows you to set timed transitions on particle groups.
+
+ You can also use this element to group particle system elements related to the logical
+ particle group. Emitters, Affectors and Painters set as direct children of a ParticleGroup
+ will automatically apply to that logical particle group. TrailEmitters will automatically follow
+ the group.
+
+ If a ParticleGroup element is not defined for a group, the group will function normally as if
+ none of the transition properties were set.
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::system
+ This is the system which will contain the group.
+
+ If the ParticleGroup is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::ParticleGroup::name
+ This is the name of the particle group, and how it is generally referred to by other elements.
+
+ If elements refer to a name which does not have an explicit ParticleGroup created, it will
+ work normally (with no transitions specified for the group). If you do not need to assign
+ duration based transitions to a group, you do not need to create a ParticleGroup with that name (although you may).
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::ParticleGroup::duration
+ The time in milliseconds before the group will attempt to transition.
+
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::durationVariation
+ The maximum number of milliseconds that the duration of the transition cycle varies per particle in the group.
+
+ Default value is zero.
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::to
+ The weighted list of transitions valid for this group.
+
+ If the chosen transition stays in this group, another duration (+/- up to durationVariation)
+ milliseconds will occur before another transition is attempted.
+*/
+
+QQuickParticleGroup::QQuickParticleGroup(QObject* parent)
+ : QQuickStochasticState(parent)
+ , m_system(0)
+{
+
+}
+
+void delayedRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
+{
+ QQuickParticleGroup* pg = qobject_cast<QQuickParticleGroup*>(prop->object);
+ if (pg)
+ pg->delayRedirect(value);
+}
+
+QDeclarativeListProperty<QObject> QQuickParticleGroup::particleChildren()
+{
+ QQuickParticleSystem* system = qobject_cast<QQuickParticleSystem*>(parent());
+ if (system)
+ return QDeclarativeListProperty<QObject>(this, 0, &QQuickParticleSystem::statePropertyRedirect);
+ else
+ return QDeclarativeListProperty<QObject>(this, 0, &delayedRedirect);
+}
+
+void QQuickParticleGroup::setSystem(QQuickParticleSystem* arg)
+{
+ if (m_system != arg) {
+ m_system = arg;
+ m_system->registerParticleGroup(this);
+ performDelayedRedirects();
+ emit systemChanged(arg);
+ }
+}
+
+void QQuickParticleGroup::delayRedirect(QObject *obj)
+{
+ m_delayedRedirects << obj;
+}
+
+void QQuickParticleGroup::performDelayedRedirects()
+{
+ if (!m_system)
+ return;
+ foreach (QObject* obj, m_delayedRedirects)
+ m_system->stateRedirect(this, m_system, obj);
+
+ m_delayedRedirects.clear();
+}
+
+void QQuickParticleGroup::componentComplete(){
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parent()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parent()));
+}
diff --git a/src/quick/particles/qquickparticlegroup_p.h b/src/quick/particles/qquickparticlegroup_p.h
new file mode 100644
index 0000000000..547e7fc597
--- /dev/null
+++ b/src/quick/particles/qquickparticlegroup_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QQuickPARTICLEGROUP
+#define QQuickPARTICLEGROUP
+#include <private/qquickspriteengine_p.h>
+#include "qquickparticlesystem_p.h"
+#include "qdeclarativeparserstatus.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleGroup : public QQuickStochasticState, public QDeclarativeParserStatus
+{
+ Q_OBJECT
+ //### Would setting limits per group be useful? Or clutter the API?
+ //Q_PROPERTY(int maximumAlive READ maximumAlive WRITE setMaximumAlive NOTIFY maximumAliveChanged)
+
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+
+ //Intercept children requests and assign to the group & system
+ Q_PROPERTY(QDeclarativeListProperty<QObject> particleChildren READ particleChildren DESIGNABLE false)//### Hidden property for in-state system definitions - ought not to be used in actual "Sprite" states
+ Q_CLASSINFO("DefaultProperty", "particleChildren")
+ Q_INTERFACES(QDeclarativeParserStatus)
+
+public:
+ explicit QQuickParticleGroup(QObject* parent = 0);
+
+ QDeclarativeListProperty<QObject> particleChildren();
+
+ int maximumAlive() const
+ {
+ return m_maximumAlive;
+ }
+
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+public slots:
+
+ void setMaximumAlive(int arg)
+ {
+ if (m_maximumAlive != arg) {
+ m_maximumAlive = arg;
+ emit maximumAliveChanged(arg);
+ }
+ }
+
+ void setSystem(QQuickParticleSystem* arg);
+
+ void delayRedirect(QObject* obj);
+
+signals:
+
+ void maximumAliveChanged(int arg);
+
+ void systemChanged(QQuickParticleSystem* arg);
+
+protected:
+ virtual void componentComplete();
+ virtual void classBegin(){;}
+
+private:
+
+ void performDelayedRedirects();
+
+ int m_maximumAlive;
+ QQuickParticleSystem* m_system;
+ QList<QObject*> m_delayedRedirects;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/particles/qquickparticlepainter.cpp b/src/quick/particles/qquickparticlepainter.cpp
new file mode 100644
index 0000000000..812c4b86dc
--- /dev/null
+++ b/src/quick/particles/qquickparticlepainter.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticlepainter_p.h"
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass ParticlePainter QQuickParticlePainter
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief ParticlePainter elements allow you to specify how to paint particles.
+
+ The default implementation paints nothing. See the subclasses if you want to
+ paint something visible.
+
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticlePainter::system
+ This is the system whose particles can be painted by the element.
+ If the ParticlePainter is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+ \qmlproperty list<string> QtQuick.Particles2::ParticlePainter::groups
+ Which logical particle groups will be painted.
+
+ If empty, it will paint the default particle group ("").
+*/
+QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent) :
+ QQuickItem(parent),
+ m_system(0), m_count(0), m_pleaseReset(true)
+{
+}
+
+void QQuickParticlePainter::componentComplete()
+{
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
+ QQuickItem::componentComplete();
+}
+
+
+void QQuickParticlePainter::setSystem(QQuickParticleSystem *arg)
+{
+ if (m_system != arg) {
+ m_system = arg;
+ if (m_system){
+ m_system->registerParticlePainter(this);
+ reset();
+ }
+ emit systemChanged(arg);
+ }
+}
+
+void QQuickParticlePainter::load(QQuickParticleData* d)
+{
+ initialize(d->group, d->index);
+ if (m_pleaseReset)
+ return;
+ m_pendingCommits << qMakePair<int, int>(d->group, d->index);
+}
+
+void QQuickParticlePainter::reload(QQuickParticleData* d)
+{
+ if (m_pleaseReset)
+ return;
+ m_pendingCommits << qMakePair<int, int>(d->group, d->index);
+}
+
+void QQuickParticlePainter::reset()
+{
+}
+
+void QQuickParticlePainter::setCount(int c)//### TODO: some resizeing so that particles can reallocate on size change instead of recreate
+{
+ Q_ASSERT(c >= 0); //XXX
+ if (c == m_count)
+ return;
+ m_count = c;
+ emit countChanged();
+ reset();
+}
+
+int QQuickParticlePainter::count()
+{
+ return m_count;
+}
+
+void QQuickParticlePainter::calcSystemOffset(bool resetPending)
+{
+ if (!m_system || !parentItem())
+ return;
+ QPointF lastOffset = m_systemOffset;
+ m_systemOffset = -1 * this->mapFromItem(m_system, QPointF(0.0, 0.0));
+ if (lastOffset != m_systemOffset && !resetPending){
+ //Reload all particles//TODO: Necessary?
+ foreach (const QString &g, m_groups){
+ int gId = m_system->groupIds[g];
+ foreach (QQuickParticleData* d, m_system->groupData[gId]->data)
+ reload(d);
+ }
+ }
+}
+typedef QPair<int,int> intPair;
+void QQuickParticlePainter::performPendingCommits()
+{
+ calcSystemOffset();
+ foreach (intPair p, m_pendingCommits)
+ commit(p.first, p.second);
+ m_pendingCommits.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickparticlepainter_p.h b/src/quick/particles/qquickparticlepainter_p.h
new file mode 100644
index 0000000000..44aac3466f
--- /dev/null
+++ b/src/quick/particles/qquickparticlepainter_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLE_H
+#define PARTICLE_H
+
+#include <QObject>
+#include <QDebug>
+#include <QPair>
+#include "qquickparticlesystem_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticlePainter : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
+
+public:
+ explicit QQuickParticlePainter(QQuickItem *parent = 0);
+ //Data Interface to system
+ void load(QQuickParticleData*);
+ void reload(QQuickParticleData*);
+ void setCount(int c);
+ int count();
+ void performPendingCommits();//Called from updatePaintNode
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+
+ QStringList groups() const
+ {
+ return m_groups;
+ }
+
+signals:
+ void countChanged();
+ void systemChanged(QQuickParticleSystem* arg);
+
+ void groupsChanged(QStringList arg);
+
+public slots:
+ void setSystem(QQuickParticleSystem* arg);
+
+ void setGroups(QStringList arg)
+ {
+ if (m_groups != arg) {
+ m_groups = arg;
+ emit groupsChanged(arg);
+ }
+ }
+
+ void calcSystemOffset(bool resetPending = false);
+
+protected:
+ /* Reset resets all your internal data structures. But anything attached to a particle should
+ be in attached data. So reset + reloads should have no visible effect.
+ ###Hunt down all cases where we do a complete reset for convenience and be more targeted
+ */
+ virtual void reset();
+
+ virtual void componentComplete();
+ virtual void initialize(int gIdx, int pIdx){//Called from main thread
+ Q_UNUSED(gIdx);
+ Q_UNUSED(pIdx);
+ }
+ virtual void commit(int gIdx, int pIdx){//Called in Render Thread
+ //###If you need to do something on size changed, check m_data size in this? Or we reset you every time?
+ Q_UNUSED(gIdx);
+ Q_UNUSED(pIdx);
+ }
+
+ QQuickParticleSystem* m_system;
+ friend class QQuickParticleSystem;
+ int m_count;
+ bool m_pleaseReset;//Used by subclasses, but it's a nice optimization to know when stuff isn't going to matter.
+ QStringList m_groups;
+ QPointF m_systemOffset;
+
+private:
+ QSet<QPair<int,int> > m_pendingCommits;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // PARTICLE_H
diff --git a/src/quick/particles/qquickparticlesmodule.cpp b/src/quick/particles/qquickparticlesmodule.cpp
new file mode 100644
index 0000000000..26439c4f50
--- /dev/null
+++ b/src/quick/particles/qquickparticlesmodule.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickangledirection_p.h"
+#include "qquickcustomparticle_p.h"
+#include "qquickellipseextruder_p.h"
+#include "qquicktrailemitter_p.h"
+#include "qquickfriction_p.h"
+#include "qquickgravity_p.h"
+#include "qquickimageparticle_p.h"
+#include "qquickitemparticle_p.h"
+#include "qquickage_p.h"
+#include "qquicklineextruder_p.h"
+#include "qquickmaskextruder_p.h"
+#include "qquickparticleaffector_p.h"
+#include "qquickparticleemitter_p.h"
+#include "qquickparticleextruder_p.h"
+#include "qquickparticlepainter_p.h"
+#include "qquickparticlesmodule_p.h"
+#include "qquickparticlesystem_p.h"
+#include "qquickpointattractor_p.h"
+#include "qquickpointdirection_p.h"
+#include "qquickspritegoal_p.h"
+#include "qquickdirection_p.h"
+#include "qquicktargetdirection_p.h"
+#include "qquickturbulence_p.h"
+#include "qquickwander_p.h"
+#include "qquickcumulativedirection_p.h"
+#include "qquickcustomaffector_p.h"
+#include "qquickrectangleextruder_p.h"
+#include "qquickparticlegroup_p.h"
+#include "qquickgroupgoal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QQuickParticlesModule::defineModule()
+{
+ const char* uri = "QtQuick.Particles";
+
+ qmlRegisterType<QQuickParticleSystem>(uri, 2, 0, "ParticleSystem");
+ qmlRegisterType<QQuickParticleGroup>(uri, 2, 0, "ParticleGroup");
+
+ qmlRegisterType<QQuickImageParticle>(uri, 2, 0, "ImageParticle");
+ qmlRegisterType<QQuickCustomParticle>(uri, 2, 0, "CustomParticle");
+ qmlRegisterType<QQuickItemParticle>(uri, 2, 0, "ItemParticle");
+
+ qmlRegisterType<QQuickParticleEmitter>(uri, 2, 0, "Emitter");
+ qmlRegisterType<QQuickTrailEmitter>(uri, 2, 0, "TrailEmitter");
+
+ qmlRegisterType<QQuickEllipseExtruder>(uri, 2, 0, "EllipseShape");
+ qmlRegisterType<QQuickRectangleExtruder>(uri, 2, 0, "RectangleShape");
+ qmlRegisterType<QQuickLineExtruder>(uri, 2, 0, "LineShape");
+ qmlRegisterType<QQuickMaskExtruder>(uri, 2, 0, "MaskShape");
+
+ qmlRegisterType<QQuickPointDirection>(uri, 2, 0, "PointDirection");
+ qmlRegisterType<QQuickAngleDirection>(uri, 2, 0, "AngleDirection");
+ qmlRegisterType<QQuickTargetDirection>(uri, 2, 0, "TargetDirection");
+ qmlRegisterType<QQuickCumulativeDirection>(uri, 2, 0, "CumulativeDirection");
+
+ qmlRegisterType<QQuickCustomAffector>(uri, 2, 0, "Affector");
+ qmlRegisterType<QQuickWanderAffector>(uri, 2, 0, "Wander");
+ qmlRegisterType<QQuickFrictionAffector>(uri, 2, 0, "Friction");
+ qmlRegisterType<QQuickAttractorAffector>(uri, 2, 0, "Attractor");
+ qmlRegisterType<QQuickGravityAffector>(uri, 2, 0, "Gravity");
+ qmlRegisterType<QQuickAgeAffector>(uri, 2, 0, "Age");
+ qmlRegisterType<QQuickSpriteGoalAffector>(uri, 2, 0, "SpriteGoal");
+ qmlRegisterType<QQuickGroupGoalAffector>(uri, 2, 0, "GroupGoal");
+ qmlRegisterType<QQuickTurbulenceAffector>(uri, 2, 0 , "Turbulence");
+
+ //Exposed just for completeness
+ qmlRegisterUncreatableType<QQuickParticleAffector>(uri, 2, 0, "ParticleAffector",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+ qmlRegisterUncreatableType<QQuickParticlePainter>(uri, 2, 0, "ParticlePainter",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+ qmlRegisterUncreatableType<QQuickParticleExtruder>(uri, 2, 0, "ParticleExtruder",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+ qmlRegisterUncreatableType<QQuickDirection>(uri, 2, 0, "NullVector",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quick/particles/qquickparticlesmodule_p.h b/src/quick/particles/qquickparticlesmodule_p.h
new file mode 100644
index 0000000000..e553b23558
--- /dev/null
+++ b/src/quick/particles/qquickparticlesmodule_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickPARTICLESMODULE_H
+#define QQuickPARTICLESMODULE_H
+
+#include <qdeclarative.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticlesModule
+{
+public:
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQuickPARTICLESMODULE_H
diff --git a/src/quick/particles/qquickparticlesystem.cpp b/src/quick/particles/qquickparticlesystem.cpp
new file mode 100644
index 0000000000..9df086a045
--- /dev/null
+++ b/src/quick/particles/qquickparticlesystem.cpp
@@ -0,0 +1,1106 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticlesystem_p.h"
+#include <QtQuick/qsgnode.h>
+#include "qquickparticleemitter_p.h"
+#include "qquickparticleaffector_p.h"
+#include "qquickparticlepainter_p.h"
+#include <private/qquickspriteengine_p.h>
+#include <private/qquicksprite_p.h>
+#include "qquickv8particledata_p.h"
+#include "qquickparticlegroup_p.h"
+
+#include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
+#include <private/qdeclarativeengine_p.h>
+#include <cmath>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+//###Switch to define later, for now user-friendly (no compilation) debugging is worth it
+DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
+/*!
+ \qmlclass ParticleSystem QQuickParticleSystem
+ \inqmlmodule QtQuick.Particles 2
+ \brief The ParticleSystem brings together ParticlePainter, Emitter and Affector elements.
+
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ParticleSystem::running
+
+ If running is set to false, the particle system will stop the simulation. All particles
+ will be destroyed when the system is set to running again.
+
+ It can also be controlled with the start() and stop() methods.
+*/
+
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ParticleSystem::paused
+
+ If paused is set to true, the particle system will not advance the simulation. When
+ paused is set to false again, the simulation will resume from the same point it was
+ paused.
+
+ The simulation will automatically pause if it detects that there are no live particles
+ left, and unpause when new live particles are added.
+
+ It can also be controlled with the pause() and resume() methods.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ParticleSystem::empty
+
+ empty is set to true when there are no live particles left in the system.
+
+ You can use this to pause the system, keeping it from spending any time updating,
+ but you will need to resume it in order for additional particles to be generated
+ by the system.
+
+ To kill all the particles in the system, use a Kill affector.
+*/
+
+/*!
+ \qmlproperty list<Sprite> QtQuick.Particles2::ParticleSystem::particleStates
+
+ You can define a sub-set of particle groups in this property in order to provide them
+ with stochastic state transitions.
+
+ Each QtQuick2::Sprite in this list is interpreted as corresponding to the particle group
+ with ths same name. Any transitions defined in these sprites will take effect on the particle
+ groups as well. Additionally TrailEmitters, Affectors and ParticlePainters definined
+ inside one of these sprites are automatically associated with the corresponding particle group.
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::pause
+
+ Pauses the simulation if it is running.
+
+ \sa resume, paused
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::resume
+
+ Resumes the simulation if it is paused.
+
+ \sa pause, paused
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::start
+
+ Starts the simulation if it has not already running.
+
+ \sa stop, restart, running
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::stop
+
+ Stops the simulation if it is running.
+
+ \sa start, restart, running
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::restart
+
+ Stops the simulation if it is running, and then starts it.
+
+ \sa stop, restart, running
+*/
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::reset
+
+ Discards all currently existing particles.
+
+*/
+const qreal EPSILON = 0.001;
+//Utility functions for when within 1ms is close enough
+bool timeEqualOrGreater(qreal a, qreal b)
+{
+ return (a+EPSILON >= b);
+}
+
+bool timeLess(qreal a, qreal b)
+{
+ return (a-EPSILON < b);
+}
+
+bool timeEqual(qreal a, qreal b)
+{
+ return (a+EPSILON > b) && (a-EPSILON < b);
+}
+
+int roundedTime(qreal a)
+{// in ms
+ return (int)qRound(a*1000.0);
+}
+
+QQuickParticleDataHeap::QQuickParticleDataHeap()
+ : m_data(0)
+{
+ m_data.reserve(1000);
+ clear();
+}
+
+void QQuickParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData
+{
+ m_data.resize(1 << ++m_size);
+}
+
+void QQuickParticleDataHeap::insert(QQuickParticleData* data)
+{
+ insertTimed(data, roundedTime(data->t + data->lifeSpan));
+}
+
+void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time)
+{
+ //TODO: Optimize 0 lifespan (or already dead) case
+ if (m_lookups.contains(time)) {
+ m_data[m_lookups[time]].data << data;
+ return;
+ }
+ if (m_end == (1 << m_size))
+ grow();
+ m_data[m_end].time = time;
+ m_data[m_end].data.clear();
+ m_data[m_end].data.insert(data);
+ m_lookups.insert(time, m_end);
+ bubbleUp(m_end++);
+}
+
+int QQuickParticleDataHeap::top()
+{
+ if (m_end == 0)
+ return 1 << 30;
+ return m_data[0].time;
+}
+
+QSet<QQuickParticleData*> QQuickParticleDataHeap::pop()
+{
+ if (!m_end)
+ return QSet<QQuickParticleData*> ();
+ QSet<QQuickParticleData*> ret = m_data[0].data;
+ m_lookups.remove(m_data[0].time);
+ if (m_end == 1) {
+ --m_end;
+ } else {
+ m_data[0] = m_data[--m_end];
+ bubbleDown(0);
+ }
+ return ret;
+}
+
+void QQuickParticleDataHeap::clear()
+{
+ m_size = 0;
+ m_end = 0;
+ //m_size is in powers of two. So to start at 0 we have one allocated
+ m_data.resize(1);
+ m_lookups.clear();
+}
+
+bool QQuickParticleDataHeap::contains(QQuickParticleData* d)
+{
+ for (int i=0; i<m_end; i++)
+ if (m_data[i].data.contains(d))
+ return true;
+ return false;
+}
+
+void QQuickParticleDataHeap::swap(int a, int b)
+{
+ m_tmp = m_data[a];
+ m_data[a] = m_data[b];
+ m_data[b] = m_tmp;
+ m_lookups[m_data[a].time] = a;
+ m_lookups[m_data[b].time] = b;
+}
+
+void QQuickParticleDataHeap::bubbleUp(int idx)//tends to be called once
+{
+ if (!idx)
+ return;
+ int parent = (idx-1)/2;
+ if (m_data[idx].time < m_data[parent].time) {
+ swap(idx, parent);
+ bubbleUp(parent);
+ }
+}
+
+void QQuickParticleDataHeap::bubbleDown(int idx)//tends to be called log n times
+{
+ int left = idx*2 + 1;
+ if (left >= m_end)
+ return;
+ int lesser = left;
+ int right = idx*2 + 2;
+ if (right < m_end) {
+ if (m_data[left].time > m_data[right].time)
+ lesser = right;
+ }
+ if (m_data[idx].time > m_data[lesser].time) {
+ swap(idx, lesser);
+ bubbleDown(lesser);
+ }
+}
+
+QQuickParticleGroupData::QQuickParticleGroupData(int id, QQuickParticleSystem* sys):index(id),m_size(0),m_system(sys)
+{
+ initList();
+}
+
+QQuickParticleGroupData::~QQuickParticleGroupData()
+{
+ foreach (QQuickParticleData* d, data)
+ delete d;
+}
+
+int QQuickParticleGroupData::size()
+{
+ return m_size;
+}
+
+QString QQuickParticleGroupData::name()//### Worth caching as well?
+{
+ return m_system->groupIds.key(index);
+}
+
+void QQuickParticleGroupData::setSize(int newSize)
+{
+ if (newSize == m_size)
+ return;
+ Q_ASSERT(newSize > m_size);//XXX allow shrinking
+ data.resize(newSize);
+ for (int i=m_size; i<newSize; i++) {
+ data[i] = new QQuickParticleData(m_system);
+ data[i]->group = index;
+ data[i]->index = i;
+ reusableIndexes << i;
+ }
+ int delta = newSize - m_size;
+ m_size = newSize;
+ foreach (QQuickParticlePainter* p, painters)
+ p->setCount(p->count() + delta);
+}
+
+void QQuickParticleGroupData::initList()
+{
+ dataHeap.clear();
+}
+
+void QQuickParticleGroupData::kill(QQuickParticleData* d)
+{
+ Q_ASSERT(d->group == index);
+ d->lifeSpan = 0;//Kill off
+ foreach (QQuickParticlePainter* p, painters)
+ p->reload(d);
+ reusableIndexes << d->index;
+}
+
+QQuickParticleData* QQuickParticleGroupData::newDatum(bool respectsLimits)
+{
+ //recycle();//Extra recycler round to be sure?
+
+ while (!reusableIndexes.empty()) {
+ int idx = *(reusableIndexes.begin());
+ reusableIndexes.remove(idx);
+ if (data[idx]->stillAlive()) {// ### This means resurrection of 'dead' particles. Is that allowed?
+ prepareRecycler(data[idx]);
+ continue;
+ }
+ return data[idx];
+ }
+ if (respectsLimits)
+ return 0;
+
+ int oldSize = m_size;
+ setSize(oldSize + 10);//###+1,10%,+10? Choose something non-arbitrarily
+ reusableIndexes.remove(oldSize);
+ return data[oldSize];
+}
+
+bool QQuickParticleGroupData::recycle()
+{
+ while (dataHeap.top() <= m_system->timeInt) {
+ foreach (QQuickParticleData* datum, dataHeap.pop()) {
+ if (!datum->stillAlive()) {
+ reusableIndexes << datum->index;
+ } else {
+ prepareRecycler(datum); //ttl has been altered mid-way, put it back
+ }
+ }
+ }
+
+ //TODO: If the data is clear, gc (consider shrinking stack size)?
+ return reusableIndexes.count() == m_size;
+}
+
+void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d)
+{
+ if (d->lifeSpan*1000 < m_system->maxLife) {
+ dataHeap.insert(d);
+ } else {
+ while ((roundedTime(d->t) + 2*m_system->maxLife/3) <= m_system->timeInt)
+ d->extendLife(m_system->maxLife/3000.0);
+ dataHeap.insertTimed(d, roundedTime(d->t) + 2*m_system->maxLife/3);
+ }
+}
+
+QQuickParticleData::QQuickParticleData(QQuickParticleSystem* sys)
+ : group(0)
+ , e(0)
+ , system(sys)
+ , index(0)
+ , systemIndex(-1)
+ , colorOwner(0)
+ , rotationOwner(0)
+ , deformationOwner(0)
+ , animationOwner(0)
+ , v8Datum(0)
+{
+ x = 0;
+ y = 0;
+ t = -1;
+ lifeSpan = 0;
+ size = 0;
+ endSize = 0;
+ vx = 0;
+ vy = 0;
+ ax = 0;
+ ay = 0;
+ xx = 1;
+ xy = 0;
+ yx = 0;
+ yy = 1;
+ rotation = 0;
+ rotationSpeed = 0;
+ autoRotate = 0;
+ animIdx = 0;
+ frameDuration = 1;
+ frameCount = 1;
+ animT = -1;
+ animX = 0;
+ animY = 0;
+ animWidth = 1;
+ animHeight = 1;
+ color.r = 255;
+ color.g = 255;
+ color.b = 255;
+ color.a = 255;
+ r = 0;
+ delegate = 0;
+ modelIndex = -1;
+}
+
+QQuickParticleData::~QQuickParticleData()
+{
+ delete v8Datum;
+}
+
+void QQuickParticleData::clone(const QQuickParticleData& other)
+{
+ x = other.x;
+ y = other.y;
+ t = other.t;
+ lifeSpan = other.lifeSpan;
+ size = other.size;
+ endSize = other.endSize;
+ vx = other.vx;
+ vy = other.vy;
+ ax = other.ax;
+ ay = other.ay;
+ xx = other.xx;
+ xy = other.xy;
+ yx = other.yx;
+ yy = other.yy;
+ rotation = other.rotation;
+ rotationSpeed = other.rotationSpeed;
+ autoRotate = other.autoRotate;
+ animIdx = other.animIdx;
+ frameDuration = other.frameDuration;
+ frameCount = other.frameCount;
+ animT = other.animT;
+ animX = other.animX;
+ animY = other.animY;
+ animWidth = other.animWidth;
+ animHeight = other.animHeight;
+ color.r = other.color.r;
+ color.g = other.color.g;
+ color.b = other.color.b;
+ color.a = other.color.a;
+ r = other.r;
+ delegate = other.delegate;
+ modelIndex = other.modelIndex;
+
+ colorOwner = other.colorOwner;
+ rotationOwner = other.rotationOwner;
+ deformationOwner = other.deformationOwner;
+ animationOwner = other.animationOwner;
+}
+
+QDeclarativeV8Handle QQuickParticleData::v8Value()
+{
+ if (!v8Datum)
+ v8Datum = new QQuickV8ParticleData(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(system)), this);
+ return v8Datum->v8Value();
+}
+//sets the x accleration without affecting the instantaneous x velocity or position
+void QQuickParticleData::setInstantaneousAX(qreal ax)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal vx = (this->vx + t*this->ax) - t*ax;
+ qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
+ qreal x = ex - t*vx - 0.5 * t*t*ax;
+
+ this->ax = ax;
+ this->vx = vx;
+ this->x = x;
+}
+
+//sets the x velocity without affecting the instantaneous x postion
+void QQuickParticleData::setInstantaneousVX(qreal vx)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal evx = vx - t*this->ax;
+ qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
+ qreal x = ex - t*evx - 0.5 * t*t*this->ax;
+
+ this->vx = evx;
+ this->x = x;
+}
+
+//sets the instantaneous x postion
+void QQuickParticleData::setInstantaneousX(qreal x)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ this->x = x - t*this->vx - 0.5 * t*t*this->ax;
+}
+
+//sets the y accleration without affecting the instantaneous y velocity or position
+void QQuickParticleData::setInstantaneousAY(qreal ay)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal vy = (this->vy + t*this->ay) - t*ay;
+ qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
+ qreal y = ey - t*vy - 0.5 * t*t*ay;
+
+ this->ay = ay;
+ this->vy = vy;
+ this->y = y;
+}
+
+//sets the y velocity without affecting the instantaneous y position
+void QQuickParticleData::setInstantaneousVY(qreal vy)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal evy = vy - t*this->ay;
+ qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
+ qreal y = ey - t*evy - 0.5 * t*t*this->ay;
+
+ this->vy = evy;
+ this->y = y;
+}
+
+//sets the instantaneous Y position
+void QQuickParticleData::setInstantaneousY(qreal y)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ this->y = y - t*this->vy - 0.5 * t*t*this->ay;
+}
+
+qreal QQuickParticleData::curX() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return this->x + this->vx * t + 0.5 * this->ax * t * t;
+}
+
+qreal QQuickParticleData::curVX() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return this->vx + t*this->ax;
+}
+
+qreal QQuickParticleData::curY() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return y + vy * t + 0.5 * ay * t * t;
+}
+
+qreal QQuickParticleData::curVY() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return vy + t*ay;
+}
+
+void QQuickParticleData::debugDump()
+{
+ qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
+ << "Pos: " << x << "," << y
+ << "Vel: " << vx << "," << vy
+ << "Acc: " << ax << "," << ay
+ << "Size: " << size << "," << endSize
+ << "Time: " << t << "," <<lifeSpan << ";" << (system->timeInt / 1000.0) ;
+}
+
+bool QQuickParticleData::stillAlive()
+{
+ if (!system)
+ return false;
+ return (t + lifeSpan - EPSILON) > ((qreal)system->timeInt/1000.0);
+}
+
+bool QQuickParticleData::alive()
+{
+ if (!system)
+ return false;
+ qreal st = ((qreal)system->timeInt/1000.0);
+ return (t + EPSILON) < st && (t + lifeSpan - EPSILON) > st;
+}
+
+float QQuickParticleData::curSize()
+{
+ if (!system || !lifeSpan)
+ return 0.0f;
+ return size + (endSize - size) * (1 - (lifeLeft() / lifeSpan));
+}
+
+float QQuickParticleData::lifeLeft()
+{
+ if (!system)
+ return 0.0f;
+ return (t + lifeSpan) - (system->timeInt/1000.0);
+}
+
+void QQuickParticleData::extendLife(float time)
+{
+ qreal newX = curX();
+ qreal newY = curY();
+ qreal newVX = curVX();
+ qreal newVY = curVY();
+
+ t += time;
+ animT += time;
+
+ qreal elapsed = (system->timeInt / 1000.0) - t;
+ qreal evy = newVY - elapsed*ay;
+ qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
+ qreal evx = newVX - elapsed*ax;
+ qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
+
+ x = ex;
+ vx = evx;
+ y = ey;
+ vy = evy;
+}
+
+QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
+ QQuickItem(parent),
+ stateEngine(0),
+ m_running(true),
+ particleCount(0),
+ m_nextIndex(0),
+ m_componentComplete(false),
+ m_paused(false)
+{
+ connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
+ this, SLOT(loadPainter(QObject*)));
+
+ m_debugMode = qmlParticlesDebug();
+}
+
+QQuickParticleSystem::~QQuickParticleSystem()
+{
+ foreach (QQuickParticleGroupData* gd, groupData)
+ delete gd;
+}
+
+void QQuickParticleSystem::initGroups()
+{
+ m_reusableIndexes.clear();
+ m_nextIndex = 0;
+
+ qDeleteAll(groupData);
+ groupData.clear();
+ groupIds.clear();
+
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group
+ groupData.insert(0,gd);
+ groupIds.insert(QString(), 0);
+ m_nextGroupId = 1;
+}
+
+void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
+{
+ //TODO: a way to Unregister emitters, painters and affectors
+ m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
+ connect(p, SIGNAL(groupsChanged(QStringList)),
+ &m_painterMapper, SLOT(map()));
+ loadPainter(p);
+}
+
+void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
+{
+ m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
+ connect(e, SIGNAL(particleCountChanged()),
+ this, SLOT(emittersChanged()));
+ connect(e, SIGNAL(groupChanged(QString)),
+ this, SLOT(emittersChanged()));
+ emittersChanged();
+ e->reset();//Start, so that starttime factors appropriately
+}
+
+void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
+{
+ m_affectors << QPointer<QQuickParticleAffector>(a);
+}
+
+void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
+{
+ m_groups << QPointer<QQuickParticleGroup>(g);
+ createEngine();
+}
+
+void QQuickParticleSystem::setRunning(bool arg)
+{
+ if (m_running != arg) {
+ m_running = arg;
+ emit runningChanged(arg);
+ setPaused(false);
+ if (m_animation)//Not created until componentCompleted
+ m_running ? m_animation->start() : m_animation->stop();
+ reset();
+ }
+}
+
+void QQuickParticleSystem::setPaused(bool arg) {
+ if (m_paused != arg) {
+ m_paused = arg;
+ if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
+ m_paused ? m_animation->pause() : m_animation->resume();
+ if (!m_paused) {
+ foreach (QQuickParticlePainter *p, m_painters)
+ p->update();
+ }
+ emit pausedChanged(arg);
+ }
+}
+
+void QQuickParticleSystem::statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
+{
+ //Hooks up automatic state-associated stuff
+ QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
+ QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
+ if (!group || !sys || !value)
+ return;
+ stateRedirect(group, sys, value);
+}
+
+void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
+{
+ QStringList list;
+ list << group->name();
+ QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
+ if (a) {
+ a->setParentItem(sys);
+ a->setGroups(list);
+ a->setSystem(sys);
+ return;
+ }
+ QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
+ if (fe) {
+ fe->setParentItem(sys);
+ fe->setFollow(group->name());
+ fe->setSystem(sys);
+ return;
+ }
+ QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
+ if (e) {
+ e->setParentItem(sys);
+ e->setGroup(group->name());
+ e->setSystem(sys);
+ return;
+ }
+ QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
+ if (p) {
+ p->setParentItem(sys);
+ p->setGroups(list);
+ p->setSystem(sys);
+ return;
+ }
+ qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
+}
+
+void QQuickParticleSystem::componentComplete()
+
+{
+ QQuickItem::componentComplete();
+ m_componentComplete = true;
+ m_animation = new QQuickParticleSystemAnimation(this);
+ reset();//restarts animation as well
+}
+
+void QQuickParticleSystem::reset()
+{
+ if (!m_componentComplete)
+ return;
+
+ timeInt = 0;
+ //Clear guarded pointers which have been deleted
+ int cleared = 0;
+ cleared += m_emitters.removeAll(0);
+ cleared += m_painters.removeAll(0);
+ cleared += m_affectors.removeAll(0);
+
+ bySysIdx.resize(0);
+ initGroups();//Also clears all logical particles
+
+ if (!m_running)
+ return;
+
+ foreach (QQuickParticleEmitter* e, m_emitters)
+ e->reset();
+
+ emittersChanged();
+
+ foreach (QQuickParticlePainter *p, m_painters) {
+ loadPainter(p);
+ p->reset();
+ }
+
+ //### Do affectors need reset too?
+ if (m_animation) {//Animation is explicitly disabled in benchmarks
+ //reset restarts animation (if running)
+ if ((m_animation->state() == QAbstractAnimation::Running))
+ m_animation->stop();
+ m_animation->start();
+ if (m_paused)
+ m_animation->pause();
+ }
+
+ initialized = true;
+}
+
+
+void QQuickParticleSystem::loadPainter(QObject *p)
+{
+ if (!m_componentComplete)
+ return;
+
+ QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p);
+ Q_ASSERT(painter);//XXX
+ foreach (QQuickParticleGroupData* sg, groupData)
+ sg->painters.remove(painter);
+ int particleCount = 0;
+ if (painter->groups().isEmpty()) {//Uses default particle
+ QStringList def;
+ def << QString();
+ painter->setGroups(def);
+ particleCount += groupData[0]->size();
+ groupData[0]->painters << painter;
+ } else {
+ foreach (const QString &group, painter->groups()) {
+ if (group != QLatin1String("") && !groupIds[group]) {//new group
+ int id = m_nextGroupId++;
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
+ groupIds.insert(group, id);
+ groupData.insert(id, gd);
+ }
+ particleCount += groupData[groupIds[group]]->size();
+ groupData[groupIds[group]]->painters << painter;
+ }
+ }
+ painter->setCount(particleCount);
+ painter->update();//Initial update here
+ return;
+}
+
+void QQuickParticleSystem::emittersChanged()
+{
+ if (!m_componentComplete)
+ return;
+
+ m_emitters.removeAll(0);
+
+
+ QList<int> previousSizes;
+ QList<int> newSizes;
+ for (int i=0; i<m_nextGroupId; i++) {
+ previousSizes << groupData[i]->size();
+ newSizes << 0;
+ }
+
+ foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes.
+ if (!groupIds.contains(e->group())
+ || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier
+ int id = m_nextGroupId++;
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
+ groupIds.insert(e->group(), id);
+ groupData.insert(id, gd);
+ previousSizes << 0;
+ newSizes << 0;
+ }
+ newSizes[groupIds[e->group()]] += e->particleCount();
+ //###: Cull emptied groups?
+ }
+
+ //TODO: Garbage collection?
+ particleCount = 0;
+ for (int i=0; i<m_nextGroupId; i++) {
+ groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
+ particleCount += groupData[i]->size();
+ }
+
+ if (m_debugMode)
+ qDebug() << "Particle system emitters changed. New particle count: " << particleCount;
+
+ if (particleCount > bySysIdx.size())//New datum requests haven't updated it
+ bySysIdx.resize(particleCount);
+
+ foreach (QQuickParticlePainter *p, m_painters)
+ loadPainter(p);
+
+ if (!m_groups.isEmpty())
+ createEngine();
+
+}
+
+void QQuickParticleSystem::createEngine()
+{
+ if (!m_componentComplete)
+ return;
+ if (stateEngine && m_debugMode)
+ qDebug() << "Resetting Existing Sprite Engine...";
+ //### Solve the losses if size/states go down
+ foreach (QQuickParticleGroup* group, m_groups) {
+ bool exists = false;
+ foreach (const QString &name, groupIds.keys())
+ if (group->name() == name)
+ exists = true;
+ if (!exists) {
+ int id = m_nextGroupId++;
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
+ groupIds.insert(group->name(), id);
+ groupData.insert(id, gd);
+ }
+ }
+
+ if (m_groups.count()) {
+ //Reorder groups List so as to have the same order as groupData
+ QList<QQuickParticleGroup*> newList;
+ for (int i=0; i<m_nextGroupId; i++) {
+ bool exists = false;
+ QString name = groupData[i]->name();
+ foreach (QQuickParticleGroup* existing, m_groups) {
+ if (existing->name() == name) {
+ newList << existing;
+ exists = true;
+ }
+ }
+ if (!exists) {
+ newList << new QQuickParticleGroup(this);
+ newList.back()->setName(name);
+ }
+ }
+ m_groups = newList;
+ QList<QQuickStochasticState*> states;
+ foreach (QQuickParticleGroup* g, m_groups)
+ states << (QQuickStochasticState*)g;
+
+ if (!stateEngine)
+ stateEngine = new QQuickStochasticEngine(this);
+ stateEngine->setCount(particleCount);
+ stateEngine->m_states = states;
+
+ connect(stateEngine, SIGNAL(stateChanged(int)),
+ this, SLOT(particleStateChange(int)));
+
+ } else {
+ if (stateEngine)
+ delete stateEngine;
+ stateEngine = 0;
+ }
+
+}
+
+void QQuickParticleSystem::particleStateChange(int idx)
+{
+ moveGroups(bySysIdx[idx], stateEngine->curState(idx));
+}
+
+void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
+{
+ if (!d || newGIdx == d->group)
+ return;
+
+ QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
+ if (!pd)
+ return;
+
+ pd->clone(*d);
+ finishNewDatum(pd);
+
+ d->systemIndex = -1;
+ groupData[d->group]->kill(d);
+}
+
+int QQuickParticleSystem::nextSystemIndex()
+{
+ if (!m_reusableIndexes.isEmpty()) {
+ int ret = *(m_reusableIndexes.begin());
+ m_reusableIndexes.remove(ret);
+ return ret;
+ }
+ if (m_nextIndex >= bySysIdx.size()) {
+ bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
+ if (stateEngine)
+ stateEngine->setCount(bySysIdx.size());
+
+ }
+ return m_nextIndex++;
+}
+
+QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
+{
+ Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert
+
+ QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
+ if (!ret) {
+ return 0;
+ }
+ if (sysIndex == -1) {
+ if (ret->systemIndex == -1)
+ ret->systemIndex = nextSystemIndex();
+ } else {
+ if (ret->systemIndex != -1) {
+ if (stateEngine)
+ stateEngine->stop(ret->systemIndex);
+ m_reusableIndexes << ret->systemIndex;
+ bySysIdx[ret->systemIndex] = 0;
+ }
+ ret->systemIndex = sysIndex;
+ }
+ bySysIdx[ret->systemIndex] = ret;
+
+ if (stateEngine)
+ stateEngine->start(ret->systemIndex, ret->group);
+
+ m_empty = false;
+ return ret;
+}
+
+void QQuickParticleSystem::emitParticle(QQuickParticleData* pd)
+{// called from prepareNextFrame()->emitWindow - enforce?
+ //Account for relative emitter position
+ QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
+ if (!offset.isNull()) {
+ pd->x += offset.x();
+ pd->y += offset.y();
+ }
+
+ finishNewDatum(pd);
+}
+
+void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
+{
+ Q_ASSERT(pd);
+ groupData[pd->group]->prepareRecycler(pd);
+
+ foreach (QQuickParticleAffector *a, m_affectors)
+ if (a && a->m_needsReset)
+ a->reset(pd);
+ foreach (QQuickParticlePainter* p, groupData[pd->group]->painters)
+ if (p)
+ p->load(pd);
+}
+
+void QQuickParticleSystem::updateCurrentTime( int currentTime )
+{
+ if (!initialized)
+ return;//error in initialization
+
+ //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
+ qreal dt = timeInt / 1000.;
+ timeInt = currentTime;
+ qreal time = timeInt / 1000.;
+ dt = time - dt;
+ needsReset.clear();
+
+ m_emitters.removeAll(0);
+ m_painters.removeAll(0);
+ m_affectors.removeAll(0);
+
+ bool oldClear = m_empty;
+ m_empty = true;
+ foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles
+ m_empty = gd->recycle() && m_empty;
+
+ if (stateEngine)
+ stateEngine->updateSprites(timeInt);
+
+ foreach (QQuickParticleEmitter* emitter, m_emitters)
+ emitter->emitWindow(timeInt);
+ foreach (QQuickParticleAffector* a, m_affectors)
+ a->affectSystem(dt);
+ foreach (QQuickParticleData* d, needsReset)
+ foreach (QQuickParticlePainter* p, groupData[d->group]->painters)
+ p->reload(d);
+
+ if (oldClear != m_empty)
+ emptyChanged(m_empty);
+}
+
+int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
+{
+ if (!m_running)
+ return 0;
+ if (!initialized)
+ return 0;//error in initialization
+ p->performPendingCommits();
+ return timeInt;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickparticlesystem_p.h b/src/quick/particles/qquickparticlesystem_p.h
new file mode 100644
index 0000000000..6a3403aa93
--- /dev/null
+++ b/src/quick/particles/qquickparticlesystem_p.h
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLESYSTEM_H
+#define PARTICLESYSTEM_H
+
+#include <QtQuick/QQuickItem>
+#include <QElapsedTimer>
+#include <QVector>
+#include <QHash>
+#include <QPointer>
+#include <QSignalMapper>
+#include <private/qquicksprite_p.h>
+#include <QAbstractAnimation>
+#include <QtDeclarative/qdeclarative.h>
+#include <private/qv8engine_p.h> //For QDeclarativeV8Handle
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleSystem;
+class QQuickParticleAffector;
+class QQuickParticleEmitter;
+class QQuickParticlePainter;
+class QQuickParticleData;
+class QQuickParticleSystemAnimation;
+class QQuickStochasticEngine;
+class QQuickSprite;
+class QQuickV8ParticleData;
+class QQuickParticleGroup;
+class QQuickImageParticle;
+
+struct QQuickParticleDataHeapNode{
+ int time;//in ms
+ QSet<QQuickParticleData*> data;//Set ptrs instead?
+};
+
+class QQuickParticleDataHeap {
+ //Idea is to do a binary heap, but which also stores a set of int,Node* so that if the int already exists, you can
+ //add it to the data* list. Pops return the whole list at once.
+public:
+ QQuickParticleDataHeap();
+ void insert(QQuickParticleData* data);
+ void insertTimed(QQuickParticleData* data, int time);
+
+ int top();
+
+ QSet<QQuickParticleData*> pop();
+
+ void clear();
+
+ bool contains(QQuickParticleData*);//O(n), for debugging purposes only
+private:
+ void grow();
+ void swap(int, int);
+ void bubbleUp(int);
+ void bubbleDown(int);
+ int m_size;
+ int m_end;
+ QQuickParticleDataHeapNode m_tmp;
+ QVector<QQuickParticleDataHeapNode> m_data;
+ QHash<int,int> m_lookups;
+};
+
+class Q_AUTOTEST_EXPORT QQuickParticleGroupData {
+public:
+ QQuickParticleGroupData(int id, QQuickParticleSystem* sys);
+ ~QQuickParticleGroupData();
+
+ int size();
+ QString name();
+
+ void setSize(int newSize);
+
+ int index;
+ QSet<QQuickParticlePainter*> painters;//TODO: What if they are dynamically removed?
+
+ //TODO: Refactor particle data list out into a separate class
+ QVector<QQuickParticleData*> data;
+ QQuickParticleDataHeap dataHeap;
+ QSet<int> reusableIndexes;
+ bool recycle(); //Force recycling round, returns true if all indexes are now reusable
+
+ void initList();
+ void kill(QQuickParticleData* d);
+
+ //After calling this, initialize, then call prepareRecycler(d)
+ QQuickParticleData* newDatum(bool respectsLimits);
+
+ //TODO: Find and clean up those that don't get added to the recycler (currently they get lost)
+ void prepareRecycler(QQuickParticleData* d);
+
+private:
+ int m_size;
+ QQuickParticleSystem* m_system;
+};
+
+struct Color4ub {
+ uchar r;
+ uchar g;
+ uchar b;
+ uchar a;
+};
+
+class Q_AUTOTEST_EXPORT QQuickParticleData {
+public:
+ //TODO: QObject like memory management (without the cost, just attached to system)
+ QQuickParticleData(QQuickParticleSystem* sys);
+ ~QQuickParticleData();
+
+ //Convenience functions for working backwards, because parameters are from the start of particle life
+ //If setting multiple parameters at once, doing the conversion yourself will be faster.
+
+ //sets the x accleration without affecting the instantaneous x velocity or position
+ void setInstantaneousAX(qreal ax);
+ //sets the x velocity without affecting the instantaneous x postion
+ void setInstantaneousVX(qreal vx);
+ //sets the instantaneous x postion
+ void setInstantaneousX(qreal x);
+ //sets the y accleration without affecting the instantaneous y velocity or position
+ void setInstantaneousAY(qreal ay);
+ //sets the y velocity without affecting the instantaneous y postion
+ void setInstantaneousVY(qreal vy);
+ //sets the instantaneous Y postion
+ void setInstantaneousY(qreal y);
+
+ //TODO: Slight caching?
+ qreal curX() const;
+ qreal curVX() const;
+ qreal curAX() const { return ax; }
+ qreal curY() const;
+ qreal curVY() const;
+ qreal curAY() const { return ay; }
+
+ int group;
+ QQuickParticleEmitter* e;//### Needed?
+ QQuickParticleSystem* system;
+ int index;
+ int systemIndex;
+
+ //General Position Stuff
+ float x;
+ float y;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+
+ //Other stuff, now universally shared
+ Color4ub color;
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;//Assume that GPUs prefer floats to bools
+ float animIdx;
+ float frameDuration;
+ float frameCount;
+ float animT;
+ float animX;
+ float animY;
+ float animWidth;
+ float animHeight;
+ float r;
+ QQuickItem* delegate;
+ int modelIndex;
+ float update;//Used by custom affectors
+
+ //Used by image particle
+ QQuickImageParticle* colorOwner;
+ QQuickImageParticle* rotationOwner;
+ QQuickImageParticle* deformationOwner;
+ QQuickImageParticle* animationOwner;
+
+ void debugDump();
+ bool stillAlive();//Only checks end, because usually that's all you need and it's a little faster.
+ bool alive();
+ float lifeLeft();
+ float curSize();
+ void clone(const QQuickParticleData& other);//Not =, leaves meta-data like index
+ QDeclarativeV8Handle v8Value();
+ void extendLife(float time);
+private:
+ QQuickV8ParticleData* v8Datum;
+};
+
+class Q_AUTOTEST_EXPORT QQuickParticleSystem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
+ Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged)
+ Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
+
+public:
+ explicit QQuickParticleSystem(QQuickItem *parent = 0);
+ ~QQuickParticleSystem();
+
+ bool isRunning() const
+ {
+ return m_running;
+ }
+
+ int count(){ return particleCount; }
+
+ static const int maxLife = 600000;
+
+signals:
+
+ void systemInitialized();
+ void runningChanged(bool arg);
+ void pausedChanged(bool arg);
+ void emptyChanged(bool arg);
+
+public slots:
+ void start(){setRunning(true);}
+ void stop(){setRunning(false);}
+ void restart(){setRunning(false);setRunning(true);}
+ void pause(){setPaused(true);}
+ void resume(){setPaused(false);}
+
+ void reset();
+ void setRunning(bool arg);
+ void setPaused(bool arg);
+
+ virtual int duration() const { return -1; }
+
+
+protected:
+ //This one only once per frame (effectively)
+ void componentComplete();
+
+private slots:
+ void emittersChanged();
+ void loadPainter(QObject* p);
+ void createEngine(); //Not invoked by sprite engine, unlike Sprite uses
+ void particleStateChange(int idx);
+
+public:
+ //These can be called multiple times per frame, performance critical
+ void emitParticle(QQuickParticleData* p);
+ QQuickParticleData* newDatum(int groupId, bool respectLimits = true, int sysIdx = -1);
+ void finishNewDatum(QQuickParticleData*);
+ void moveGroups(QQuickParticleData *d, int newGIdx);
+ int nextSystemIndex();
+
+ //This one only once per painter per frame
+ int systemSync(QQuickParticlePainter* p);
+
+ //Data members here for ease of related class and auto-test usage. Not "public" API. TODO: d_ptrize
+ QSet<QQuickParticleData*> needsReset;
+ QVector<QQuickParticleData*> bySysIdx; //Another reference to the data (data owned by group), but by sysIdx
+ QHash<QString, int> groupIds;
+ QHash<int, QQuickParticleGroupData*> groupData;
+ QQuickStochasticEngine* stateEngine;
+
+ //Also only here for auto-test usage
+ void updateCurrentTime( int currentTime );
+ QQuickParticleSystemAnimation* m_animation;
+ bool m_running;
+
+ int timeInt;
+ bool initialized;
+ int particleCount;
+
+ void registerParticlePainter(QQuickParticlePainter* p);
+ void registerParticleEmitter(QQuickParticleEmitter* e);
+ void registerParticleAffector(QQuickParticleAffector* a);
+ void registerParticleGroup(QQuickParticleGroup* g);
+
+ static void statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value);
+ static void stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value);
+ bool isPaused() const
+ {
+ return m_paused;
+ }
+
+ bool isEmpty() const
+ {
+ return m_empty;
+ }
+
+private:
+ void initializeSystem();
+ void initGroups();
+ QList<QPointer<QQuickParticleEmitter> > m_emitters;
+ QList<QPointer<QQuickParticleAffector> > m_affectors;
+ QList<QPointer<QQuickParticlePainter> > m_painters;
+ QList<QPointer<QQuickParticlePainter> > m_syncList;
+ QList<QQuickParticleGroup*> m_groups;
+ int m_nextGroupId;
+ int m_nextIndex;
+ QSet<int> m_reusableIndexes;
+ bool m_componentComplete;
+
+ QSignalMapper m_painterMapper;
+ QSignalMapper m_emitterMapper;
+ bool m_paused;
+ bool m_debugMode;
+ bool m_allDead;
+ bool m_empty;
+};
+
+// Internally, this animation drives all the timing. Painters sync up in their updatePaintNode
+class QQuickParticleSystemAnimation : public QAbstractAnimation
+{
+ Q_OBJECT
+public:
+ QQuickParticleSystemAnimation(QQuickParticleSystem* system)
+ : QAbstractAnimation(static_cast<QObject*>(system)), m_system(system)
+ { }
+protected:
+ virtual void updateCurrentTime( int t )
+ {
+ m_system->updateCurrentTime(t);
+ }
+
+ virtual int duration() const
+ {
+ return -1;
+ }
+
+private:
+ QQuickParticleSystem* m_system;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // PARTICLESYSTEM_H
+
+
diff --git a/src/quick/particles/qquickpointattractor.cpp b/src/quick/particles/qquickpointattractor.cpp
new file mode 100644
index 0000000000..d96458c8bb
--- /dev/null
+++ b/src/quick/particles/qquickpointattractor.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpointattractor_p.h"
+#include <cmath>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Attractor QQuickAttractorAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Attractor allows you to attract particles towards a specific point.
+
+ Note that the size and position of this element affects which particles it affects.
+ The size of the point attracted to is always 0x0, and the location of that point
+ is specified by the pointX and pointY properties.
+
+ Note that Attractor has the standard Item x,y,width and height properties.
+ Like other affectors, these represent the affected area. They
+ do not represent the 0x0 point which is the target of the attraction.
+*/
+
+
+/*!
+ \qmlproperty real QtQuick.Particles2::PointAttractor::pointX
+
+ The x coordinate of the attracting point. This is relative
+ to the x coordinate of the Attractor.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointAttractor::pointY
+
+ The y coordinate of the attracting point. This is relative
+ to the y coordinate of the Attractor.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointAttractor::strength
+
+ The pull, in units per second, to be exerted on an item one pixel away.
+
+ Depending on how the attraction is proportionalToDistance this may have to
+ be very high or very low to have a reasonable effect on particles at a
+ distance.
+*/
+/*!
+ \qmlproperty AffectableParameter QtQuick.Particles2::Attractor::affectedParameter
+
+ What attribute of particles is directly affected.
+ \list
+ \o Attractor.Position
+ \o Attractor.Velocity
+ \o Attractor.Acceleration
+ \endlist
+*/
+/*!
+ \qmlproperty Proportion QtQuick.Particles2::Attractor::proportionalToDistance
+
+ How the distance from the particle to the point affects the strength of the attraction.
+
+ \list
+ \o Attractor.Constant
+ \o Attractor.Linear
+ \o Attractor.InverseLinear
+ \o Attractor.Quadratic
+ \o Attractor.InverseQuadratic
+ \endlist
+*/
+
+
+QQuickAttractorAffector::QQuickAttractorAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0)
+ , m_physics(Velocity), m_proportionalToDistance(Linear)
+{
+}
+
+bool QQuickAttractorAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ if (m_strength == 0.0)
+ return false;
+ qreal dx = m_x+m_offset.x() - d->curX();
+ qreal dy = m_y+m_offset.y() - d->curY();
+ qreal r = sqrt((dx*dx) + (dy*dy));
+ qreal theta = atan2(dy,dx);
+ qreal ds = 0;
+ switch (m_proportionalToDistance){
+ case InverseQuadratic:
+ ds = (m_strength / qMax<qreal>(1.,r*r));
+ break;
+ case InverseLinear:
+ ds = (m_strength / qMax<qreal>(1.,r));
+ break;
+ case Quadratic:
+ ds = (m_strength * qMax<qreal>(1.,r*r));
+ break;
+ case Linear:
+ ds = (m_strength * qMax<qreal>(1.,r));
+ break;
+ default: //also Constant
+ ds = m_strength;
+ }
+ ds *= dt;
+ dx = ds * cos(theta);
+ dy = ds * sin(theta);
+ qreal vx,vy;
+ switch (m_physics){
+ case Position:
+ d->x = (d->x + dx);
+ d->y = (d->y + dy);
+ break;
+ case Acceleration:
+ d->setInstantaneousAX(d->ax + dx);
+ d->setInstantaneousAY(d->ay + dy);
+ break;
+ case Velocity: //also default
+ default:
+ vx = d->curVX();
+ vy = d->curVY();
+ d->setInstantaneousVX(vx + dx);
+ d->setInstantaneousVY(vy + dy);
+ }
+
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickpointattractor_p.h b/src/quick/particles/qquickpointattractor_p.h
new file mode 100644
index 0000000000..6c36b299bf
--- /dev/null
+++ b/src/quick/particles/qquickpointattractor_p.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ATTRACTORAFFECTOR_H
+#define ATTRACTORAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAttractorAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
+ Q_PROPERTY(qreal pointX READ pointX WRITE setPointX NOTIFY pointXChanged)
+ Q_PROPERTY(qreal pointY READ pointY WRITE setPointY NOTIFY pointYChanged)
+ Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged)
+ Q_PROPERTY(Proportion proportionalToDistance READ proportionalToDistance WRITE setProportionalToDistance NOTIFY proportionalToDistanceChanged)
+ Q_ENUMS(AffectableParameters)
+ Q_ENUMS(Proportion)
+
+public:
+ enum Proportion{
+ Constant,
+ Linear,
+ Quadratic,
+ InverseLinear,
+ InverseQuadratic
+ };
+
+ enum AffectableParameters {
+ Position,
+ Velocity,
+ Acceleration
+ };
+
+ explicit QQuickAttractorAffector(QQuickItem *parent = 0);
+
+ qreal strength() const
+ {
+ return m_strength;
+ }
+
+ qreal pointX() const
+ {
+ return m_x;
+ }
+
+ qreal pointY() const
+ {
+ return m_y;
+ }
+
+ AffectableParameters affectedParameter() const
+ {
+ return m_physics;
+ }
+
+ Proportion proportionalToDistance() const
+ {
+ return m_proportionalToDistance;
+ }
+
+signals:
+
+ void strengthChanged(qreal arg);
+
+ void pointXChanged(qreal arg);
+
+ void pointYChanged(qreal arg);
+
+ void affectedParameterChanged(AffectableParameters arg);
+
+ void proportionalToDistanceChanged(Proportion arg);
+
+public slots:
+void setStrength(qreal arg)
+{
+ if (m_strength != arg) {
+ m_strength = arg;
+ emit strengthChanged(arg);
+ }
+}
+
+void setPointX(qreal arg)
+{
+ if (m_x != arg) {
+ m_x = arg;
+ emit pointXChanged(arg);
+ }
+}
+
+void setPointY(qreal arg)
+{
+ if (m_y != arg) {
+ m_y = arg;
+ emit pointYChanged(arg);
+ }
+}
+void setAffectedParameter(AffectableParameters arg)
+{
+ if (m_physics != arg) {
+ m_physics = arg;
+ emit affectedParameterChanged(arg);
+ }
+}
+
+void setProportionalToDistance(Proportion arg)
+{
+ if (m_proportionalToDistance != arg) {
+ m_proportionalToDistance = arg;
+ emit proportionalToDistanceChanged(arg);
+ }
+}
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+private:
+qreal m_strength;
+qreal m_x;
+qreal m_y;
+AffectableParameters m_physics;
+Proportion m_proportionalToDistance;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ATTRACTORAFFECTOR_H
diff --git a/src/quick/particles/qquickpointdirection.cpp b/src/quick/particles/qquickpointdirection.cpp
new file mode 100644
index 0000000000..22c33d9f15
--- /dev/null
+++ b/src/quick/particles/qquickpointdirection.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpointdirection_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass PointDirection QQuickPointDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The PointDirection element allows you to specify a direction that varies in x and y components
+
+ The PointDirection element allows both the specification of a direction by x and y components,
+ as well as varying the parameters by x or y component.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::x
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::y
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::xVariation
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::yVariation
+*/
+
+QQuickPointDirection::QQuickPointDirection(QObject *parent) :
+ QQuickDirection(parent)
+ , m_x(0)
+ , m_y(0)
+ , m_xVariation(0)
+ , m_yVariation(0)
+{
+}
+
+const QPointF QQuickPointDirection::sample(const QPointF &)
+{
+ QPointF ret;
+ ret.setX(m_x - m_xVariation + rand() / float(RAND_MAX) * m_xVariation * 2);
+ ret.setY(m_y - m_yVariation + rand() / float(RAND_MAX) * m_yVariation * 2);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickpointdirection_p.h b/src/quick/particles/qquickpointdirection_p.h
new file mode 100644
index 0000000000..1a6becda7a
--- /dev/null
+++ b/src/quick/particles/qquickpointdirection_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef POINTVECTOR_H
+#define POINTVECTOR_H
+#include "qquickdirection_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPointDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
+ Q_PROPERTY(qreal xVariation READ xVariation WRITE setXVariation NOTIFY xVariationChanged)
+ Q_PROPERTY(qreal yVariation READ yVariation WRITE setYVariation NOTIFY yVariationChanged)
+public:
+ explicit QQuickPointDirection(QObject *parent = 0);
+ virtual const QPointF sample(const QPointF &from);
+ qreal x() const
+ {
+ return m_x;
+ }
+
+ qreal y() const
+ {
+ return m_y;
+ }
+
+ qreal xVariation() const
+ {
+ return m_xVariation;
+ }
+
+ qreal yVariation() const
+ {
+ return m_yVariation;
+ }
+
+signals:
+
+ void xChanged(qreal arg);
+
+ void yChanged(qreal arg);
+
+ void xVariationChanged(qreal arg);
+
+ void yVariationChanged(qreal arg);
+
+public slots:
+ void setX(qreal arg)
+ {
+ if (m_x != arg) {
+ m_x = arg;
+ emit xChanged(arg);
+ }
+ }
+
+ void setY(qreal arg)
+ {
+ if (m_y != arg) {
+ m_y = arg;
+ emit yChanged(arg);
+ }
+ }
+
+ void setXVariation(qreal arg)
+ {
+ if (m_xVariation != arg) {
+ m_xVariation = arg;
+ emit xVariationChanged(arg);
+ }
+ }
+
+ void setYVariation(qreal arg)
+ {
+ if (m_yVariation != arg) {
+ m_yVariation = arg;
+ emit yVariationChanged(arg);
+ }
+ }
+
+private:
+
+ qreal m_x;
+ qreal m_y;
+ qreal m_xVariation;
+ qreal m_yVariation;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // POINTVECTOR_H
diff --git a/src/quick/particles/qquickrectangleextruder.cpp b/src/quick/particles/qquickrectangleextruder.cpp
new file mode 100644
index 0000000000..d840a08d5b
--- /dev/null
+++ b/src/quick/particles/qquickrectangleextruder.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickrectangleextruder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass RectangleShape QQuickRectangleExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \brief The RectangleShape element allows you to specify an area for affectors and emitter.
+
+ Just a rectangle.
+*/
+
+QQuickRectangleExtruder::QQuickRectangleExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent), m_fill(true)
+{
+}
+
+QPointF QQuickRectangleExtruder::extrude(const QRectF &rect)
+{
+ if (m_fill)
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+ int side = rand() % 4;
+ switch (side){//TODO: Doesn't this overlap the corners?
+ case 0:
+ return QPointF(rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+ case 1:
+ return QPointF(rect.width() + rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+ case 2:
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ rect.y());
+ default:
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ rect.height() + rect.y());
+ }
+}
+
+bool QQuickRectangleExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ return bounds.contains(point);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickrectangleextruder_p.h b/src/quick/particles/qquickrectangleextruder_p.h
new file mode 100644
index 0000000000..13491b9501
--- /dev/null
+++ b/src/quick/particles/qquickrectangleextruder_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef RECTANGLEEXTRUDER_H
+#define RECTANGLEEXTRUDER_H
+
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRectangleExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)
+
+public:
+ explicit QQuickRectangleExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+ bool fill() const
+ {
+ return m_fill;
+ }
+
+signals:
+
+ void fillChanged(bool arg);
+
+public slots:
+
+ void setFill(bool arg)
+ {
+ if (m_fill != arg) {
+ m_fill = arg;
+ emit fillChanged(arg);
+ }
+ }
+protected:
+ bool m_fill;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // RectangleEXTRUDER_H
diff --git a/src/quick/particles/qquickspritegoal.cpp b/src/quick/particles/qquickspritegoal.cpp
new file mode 100644
index 0000000000..7c839dbf53
--- /dev/null
+++ b/src/quick/particles/qquickspritegoal.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickspritegoal_p.h"
+#include <private/qquickspriteengine_p.h>
+#include <private/qquicksprite_p.h>
+#include "qquickimageparticle_p.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass SpriteGoal QQuickSpriteGoalAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The SpriteGoal Affector allows you to change the state of a sprite particle.
+
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::SpriteGoal::goalState
+
+ The name of the Sprite which the affected particles should move to.
+
+ Sprite states have defined durations and transitions between them, setting goalState
+ will cause it to disregard any path weightings (including 0) and head down the path
+ which will reach the goalState quickest. It will pass through intermediate states
+ on that path.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::SpriteGoal::jump
+
+ If true, affected sprites will jump directly to the goal state instead of taking the
+ the shortest valid path to get there. They will also not finish their current state,
+ but immediately move to the beginning of the goal state.
+
+ Default is false.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::SpriteGoal::systemStates
+
+ deprecated, use GroupGoal instead
+*/
+
+QQuickSpriteGoalAffector::QQuickSpriteGoalAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent),
+ m_goalIdx(-1),
+ m_lastEngine(0),
+ m_jump(false),
+ m_systemStates(false),
+ m_notUsingEngine(false)
+{
+ m_ignoresTime = true;
+}
+
+void QQuickSpriteGoalAffector::updateStateIndex(QQuickStochasticEngine* e)
+{
+ if (m_systemStates){
+ m_goalIdx = m_system->groupIds[m_goalState];
+ }else{
+ m_lastEngine = e;
+ for (int i=0; i<e->stateCount(); i++){
+ if (e->state(i)->name() == m_goalState){
+ m_goalIdx = i;
+ return;
+ }
+ }
+ m_goalIdx = -1;//Can't find it
+ }
+}
+
+void QQuickSpriteGoalAffector::setGoalState(QString arg)
+{
+ if (m_goalState != arg) {
+ m_goalState = arg;
+ emit goalStateChanged(arg);
+ if (m_goalState.isEmpty())
+ m_goalIdx = -1;
+ else
+ m_goalIdx = -2;
+ }
+}
+
+bool QQuickSpriteGoalAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ QQuickStochasticEngine *engine = 0;
+ if (!m_systemStates){
+ //TODO: Affect all engines
+ foreach (QQuickParticlePainter *p, m_system->groupData[d->group]->painters)
+ if (qobject_cast<QQuickImageParticle*>(p))
+ engine = qobject_cast<QQuickImageParticle*>(p)->spriteEngine();
+ }else{
+ engine = m_system->stateEngine;
+ if (!engine)
+ m_notUsingEngine = true;
+ }
+ if (!engine && !m_notUsingEngine)
+ return false;
+
+ if (m_goalIdx == -2 || engine != m_lastEngine)
+ updateStateIndex(engine);
+ int index = d->index;
+ if (m_systemStates)
+ index = d->systemIndex;
+ if (m_notUsingEngine){//systemStates && no stochastic states defined. So cut out the engine
+ //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group
+ m_system->moveGroups(d, m_goalIdx);
+ }else if (engine->curState(index) != m_goalIdx){
+ engine->setGoal(m_goalIdx, index, m_jump);
+ return true; //Doesn't affect particle data, but necessary for onceOff
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickspritegoal_p.h b/src/quick/particles/qquickspritegoal_p.h
new file mode 100644
index 0000000000..3dc4cf88ca
--- /dev/null
+++ b/src/quick/particles/qquickspritegoal_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPRITEGOALAFFECTOR_H
+#define SPRITEGOALAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+#include <QtDeclarative/qdeclarativeinfo.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStochasticEngine;
+
+class QQuickSpriteGoalAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
+ Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged)
+ Q_PROPERTY(bool systemStates READ systemStates WRITE setSystemStates NOTIFY systemStatesChanged)
+public:
+ explicit QQuickSpriteGoalAffector(QQuickItem *parent = 0);
+
+ QString goalState() const
+ {
+ return m_goalState;
+ }
+
+ bool jump() const
+ {
+ return m_jump;
+ }
+ bool systemStates() const
+ {
+ return m_systemStates;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+
+ void goalStateChanged(QString arg);
+
+ void jumpChanged(bool arg);
+
+ void systemStatesChanged(bool arg);
+
+public slots:
+
+void setGoalState(QString arg);
+
+void setJump(bool arg)
+{
+ if (m_jump != arg) {
+ m_jump = arg;
+ emit jumpChanged(arg);
+ }
+}
+
+void setSystemStates(bool arg)
+{
+ if (m_systemStates != arg) {
+ //TODO: GroupGoal was added (and this deprecated) Oct 4 - remove it in a few weeks.
+ qmlInfo(this) << "systemStates is deprecated and will be removed soon. Use GroupGoal instead.";
+ m_systemStates = arg;
+ emit systemStatesChanged(arg);
+ }
+}
+
+private:
+ void updateStateIndex(QQuickStochasticEngine* e);
+ QString m_goalState;
+ int m_goalIdx;
+ QQuickStochasticEngine* m_lastEngine;
+ bool m_jump;
+ bool m_systemStates;
+
+ bool m_notUsingEngine;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SPRITEGOALAFFECTOR_H
diff --git a/src/quick/particles/qquicktargetdirection.cpp b/src/quick/particles/qquicktargetdirection.cpp
new file mode 100644
index 0000000000..eb2eb16c6e
--- /dev/null
+++ b/src/quick/particles/qquicktargetdirection.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktargetdirection_p.h"
+#include "qquickparticleemitter_p.h"
+#include <cmath>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass TargetDirection QQuickTargetDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The TargetDirection element allows you to specify a direction towards the target point
+
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::targetX
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::targetY
+*/
+/*!
+ \qmlproperty Item QtQuick.Particles2::TargetDirection::targetItem
+ If specified, this will take precedence over targetX and targetY.
+ The targeted point will be the center of the specified Item
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::targetVariation
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::magnitude
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::magnitudeVariation
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::TargetDirection::proportionalMagnitude
+
+ If true, then the value of magnitude and magnitudeVariation shall be interpreted as multiples
+ of the distance between the source point and the target point, per second.
+
+ If false(default), then the value of magnitude and magnitudeVariation shall be interpreted as
+ pixels per second.
+*/
+
+QQuickTargetDirection::QQuickTargetDirection(QObject *parent) :
+ QQuickDirection(parent)
+ , m_targetX(0)
+ , m_targetY(0)
+ , m_targetVariation(0)
+ , m_proportionalMagnitude(false)
+ , m_magnitude(0)
+ , m_magnitudeVariation(0)
+ , m_targetItem(0)
+{
+}
+
+const QPointF QQuickTargetDirection::sample(const QPointF &from)
+{
+ //###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile?
+ QPointF ret;
+ qreal targetX;
+ qreal targetY;
+ if (m_targetItem){
+ QQuickParticleEmitter* parentEmitter = qobject_cast<QQuickParticleEmitter*>(parent());
+ targetX = m_targetItem->width()/2;
+ targetY = m_targetItem->height()/2;
+ if (!parentEmitter){
+ qWarning() << "Directed vector is not a child of the emitter. Mapping of target item coordinates may fail.";
+ targetX += m_targetItem->x();
+ targetY += m_targetItem->y();
+ }else{
+ ret = parentEmitter->mapFromItem(m_targetItem, QPointF(targetX, targetY));
+ targetX = ret.x();
+ targetY = ret.y();
+ }
+ }else{
+ targetX = m_targetX;
+ targetY = m_targetY;
+ }
+ targetX += 0 - from.x() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2;
+ targetY += 0 - from.y() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2;
+ qreal theta = atan2(targetY, targetX);
+ qreal mag = m_magnitude + rand()/(float)RAND_MAX * m_magnitudeVariation * 2 - m_magnitudeVariation;
+ if (m_proportionalMagnitude)
+ mag *= sqrt(targetX * targetX + targetY * targetY);
+ ret.setX(mag * cos(theta));
+ ret.setY(mag * sin(theta));
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquicktargetdirection_p.h b/src/quick/particles/qquicktargetdirection_p.h
new file mode 100644
index 0000000000..32b5cb0038
--- /dev/null
+++ b/src/quick/particles/qquicktargetdirection_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DIRECTEDVECTOR_H
+#define DIRECTEDVECTOR_H
+#include "qquickdirection_p.h"
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickTargetDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged)
+ Q_PROPERTY(qreal targetY READ targetY WRITE setTargetY NOTIFY targetYChanged)
+ //If targetItem is set, X/Y are ignored. Aims at middle of item, use variation for variation
+ Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
+
+ Q_PROPERTY(qreal targetVariation READ targetVariation WRITE setTargetVariation NOTIFY targetVariationChanged)
+
+ //TODO: An enum would be better
+ Q_PROPERTY(bool proportionalMagnitude READ proportionalMagnitude WRITE setProportionalMagnitude NOTIFY proprotionalMagnitudeChanged)
+ Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged)
+
+public:
+ explicit QQuickTargetDirection(QObject *parent = 0);
+ virtual const QPointF sample(const QPointF &from);
+
+ qreal targetX() const
+ {
+ return m_targetX;
+ }
+
+ qreal targetY() const
+ {
+ return m_targetY;
+ }
+
+ qreal targetVariation() const
+ {
+ return m_targetVariation;
+ }
+
+ qreal magnitude() const
+ {
+ return m_magnitude;
+ }
+
+ bool proportionalMagnitude() const
+ {
+ return m_proportionalMagnitude;
+ }
+
+ qreal magnitudeVariation() const
+ {
+ return m_magnitudeVariation;
+ }
+
+ QQuickItem* targetItem() const
+ {
+ return m_targetItem;
+ }
+
+signals:
+
+ void targetXChanged(qreal arg);
+
+ void targetYChanged(qreal arg);
+
+ void targetVariationChanged(qreal arg);
+
+ void magnitudeChanged(qreal arg);
+
+ void proprotionalMagnitudeChanged(bool arg);
+
+ void magnitudeVariationChanged(qreal arg);
+
+ void targetItemChanged(QQuickItem* arg);
+
+public slots:
+ void setTargetX(qreal arg)
+ {
+ if (m_targetX != arg) {
+ m_targetX = arg;
+ emit targetXChanged(arg);
+ }
+ }
+
+ void setTargetY(qreal arg)
+ {
+ if (m_targetY != arg) {
+ m_targetY = arg;
+ emit targetYChanged(arg);
+ }
+ }
+
+ void setTargetVariation(qreal arg)
+ {
+ if (m_targetVariation != arg) {
+ m_targetVariation = arg;
+ emit targetVariationChanged(arg);
+ }
+ }
+
+ void setMagnitude(qreal arg)
+ {
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ emit magnitudeChanged(arg);
+ }
+ }
+
+ void setProportionalMagnitude(bool arg)
+ {
+ if (m_proportionalMagnitude != arg) {
+ m_proportionalMagnitude = arg;
+ emit proprotionalMagnitudeChanged(arg);
+ }
+ }
+
+ void setMagnitudeVariation(qreal arg)
+ {
+ if (m_magnitudeVariation != arg) {
+ m_magnitudeVariation = arg;
+ emit magnitudeVariationChanged(arg);
+ }
+ }
+
+ void setTargetItem(QQuickItem* arg)
+ {
+ if (m_targetItem != arg) {
+ m_targetItem = arg;
+ emit targetItemChanged(arg);
+ }
+ }
+
+private:
+ qreal m_targetX;
+ qreal m_targetY;
+ qreal m_targetVariation;
+ bool m_proportionalMagnitude;
+ qreal m_magnitude;
+ qreal m_magnitudeVariation;
+ QQuickItem *m_targetItem;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // DIRECTEDVECTOR_H
diff --git a/src/quick/particles/qquicktrailemitter.cpp b/src/quick/particles/qquicktrailemitter.cpp
new file mode 100644
index 0000000000..272a3ebf84
--- /dev/null
+++ b/src/quick/particles/qquicktrailemitter.cpp
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktrailemitter_p.h"
+#include <private/qdeclarativeengine_p.h>
+#include <cmath>
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass TrailEmitter QQuickTrailEmitter
+ \inqmlmodule QtQuick.Particles 2
+ \inherits QQuickParticleEmitter
+ \brief The TrailEmitter element allows you to emit logical particles from other logical particles.
+
+ This element emits logical particles into the ParticleSystem, with the
+ starting positions based on those of other logical particles.
+*/
+QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
+ QQuickParticleEmitter(parent)
+ , m_particlesPerParticlePerSecond(0)
+ , m_lastTimeStamp(0)
+ , m_emitterXVariation(0)
+ , m_emitterYVariation(0)
+ , m_followCount(0)
+ , m_emissionExtruder(0)
+ , m_defaultEmissionExtruder(new QQuickParticleExtruder(this))
+{
+ //TODO: If followed increased their size
+ connect(this, SIGNAL(followChanged(QString)),
+ this, SLOT(recalcParticlesPerSecond()));
+ connect(this, SIGNAL(particleDurationChanged(int)),
+ this, SLOT(recalcParticlesPerSecond()));
+ connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)),
+ this, SLOT(recalcParticlesPerSecond()));
+}
+
+/*!
+ \qmlproperty string QtQuick.Particles2::TrailEmitter::follow
+
+ The type of logical particle which this is emitting from.
+*/
+/*!
+ \qmlproperty qreal QtQuick.Particles2::TrailEmitter::speedFromMovement
+
+ If this value is non-zero, then any movement of the emitter will provide additional
+ starting velocity to the particles based on the movement. The additional vector will be the
+ same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters
+ movement multiplied by speedFromMovement.
+
+ Default value is 0.
+*/
+/*!
+ \qmlproperty Shape QtQuick.Particles2::TrailEmitter::emitShape
+
+ As the area of a TrailEmitter is the area it follows, a separate shape can be provided
+ to be the shape it emits out of. This shape has width and height specified by emitWidth
+ and emitHeight, and is centered on the followed particle's position.
+
+ The default shape is a filled Rectangle.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TrailEmitter::emitWidth
+
+ The width in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
+ the width will be the current size of the particle being followed.
+
+ Default is 0.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TrailEmitter::emitHeight
+
+ The height in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
+ the height will be the current size of the particle being followed.
+
+ Default is 0.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle
+*/
+/*!
+ \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticles(Array particles, real followed)
+
+ This handler is called when particles are emitted from the \a followed particle. \a particles contains an array of particle objects which can be directly manipulated.
+
+ If you use this signal handler, emitParticles will not be emitted.
+
+*/
+
+bool QQuickTrailEmitter::isEmitFollowConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("emitFollowParticles(QDeclarativeV8Handle,QDeclarativeV8Handle)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+void QQuickTrailEmitter::recalcParticlesPerSecond(){
+ if (!m_system)
+ return;
+ m_followCount = m_system->groupData[m_system->groupIds[m_follow]]->size();
+ if (!m_followCount){
+ setParticlesPerSecond(1);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS)
+ }else{
+ setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount);
+ m_lastEmission.resize(m_followCount);
+ m_lastEmission.fill(m_lastTimeStamp);
+ }
+}
+
+void QQuickTrailEmitter::reset()
+{
+ m_followCount = 0;
+}
+
+void QQuickTrailEmitter::emitWindow(int timeStamp)
+{
+ if (m_system == 0)
+ return;
+ if (!m_enabled && !m_pulseLeft && m_burstQueue.isEmpty())
+ return;
+ if (m_followCount != m_system->groupData[m_system->groupIds[m_follow]]->size()){
+ qreal oldPPS = m_particlesPerSecond;
+ recalcParticlesPerSecond();
+ if (m_particlesPerSecond != oldPPS)
+ return;//system may need to update
+ }
+
+ if (m_pulseLeft){
+ m_pulseLeft -= timeStamp - m_lastTimeStamp * 1000.;
+ if (m_pulseLeft < 0){
+ timeStamp += m_pulseLeft;
+ m_pulseLeft = 0;
+ }
+ }
+
+ //TODO: Implement startTime and speedFromMovement
+ qreal time = timeStamp / 1000.;
+ qreal particleRatio = 1. / m_particlesPerParticlePerSecond;
+ qreal pt;
+ qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
+
+ //Have to map it into this system, because particlesystem automaps it back
+ QPointF offset = m_system->mapFromItem(this, QPointF(0, 0));
+ qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
+
+ int gId = m_system->groupIds[m_follow];
+ int gId2 = m_system->groupIds[m_group];
+ for (int i=0; i<m_system->groupData[gId]->data.count(); i++) {
+ QQuickParticleData *d = m_system->groupData[gId]->data[i];
+ if (!d->stillAlive()){
+ m_lastEmission[i] = time; //Should only start emitting when it returns to life
+ continue;
+ }
+ pt = m_lastEmission[i];
+ if (pt < d->t)
+ pt = d->t;
+ if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
+ pt = time - maxLife;
+
+ if ((width() || height()) && !effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
+ m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
+ continue;
+ }
+
+ QList<QQuickParticleData*> toEmit;
+
+ while (pt < time || !m_burstQueue.isEmpty()){
+ QQuickParticleData* datum = m_system->newDatum(gId2, !m_overwrite);
+ if (datum){//else, skip this emission
+ datum->e = this;//###useful?
+
+ // Particle timestamp
+ datum->t = pt;
+ datum->lifeSpan =
+ (m_particleDuration
+ + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
+ / 1000.0;
+
+ // Particle position
+ // Note that burst location doesn't get used for follow emitter
+ qreal followT = pt - d->t;
+ qreal followT2 = followT * followT * 0.5;
+ qreal eW = m_emitterXVariation < 0 ? d->curSize() : m_emitterXVariation;
+ qreal eH = m_emitterYVariation < 0 ? d->curSize() : m_emitterYVariation;
+ //Subtract offset, because PS expects this in emitter coordinates
+ QRectF boundsRect(d->x - offset.x() + d->vx * followT + d->ax * followT2 - eW/2,
+ d->y - offset.y() + d->vy * followT + d->ay * followT2 - eH/2,
+ eW, eH);
+
+ QQuickParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder;
+ const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect);
+ datum->x = newPos.x();
+ datum->y = newPos.y();
+
+ // Particle speed
+ const QPointF &speed = m_speed->sample(newPos);
+ datum->vx = speed.x()
+ + m_speed_from_movement * d->vx;
+ datum->vy = speed.y()
+ + m_speed_from_movement * d->vy;
+
+ // Particle acceleration
+ const QPointF &accel = m_acceleration->sample(newPos);
+ datum->ax = accel.x();
+ datum->ay = accel.y();
+
+ // Particle size
+ float sizeVariation = -m_particleSizeVariation
+ + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
+
+ float size = qMax((qreal)0.0, m_particleSize + sizeVariation);
+ float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation);
+
+ datum->size = size * float(m_enabled);
+ datum->endSize = endSize * float(m_enabled);
+
+ toEmit << datum;
+
+ m_system->emitParticle(datum);
+ }
+ if (!m_burstQueue.isEmpty()){
+ m_burstQueue.first().first--;
+ if (m_burstQueue.first().first <= 0)
+ m_burstQueue.pop_front();
+ }else{
+ pt += particleRatio;
+ }
+ }
+
+ if (isEmitConnected() || isEmitFollowConnected()) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
+ v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
+ for (int i=0; i<toEmit.size(); i++)
+ array->Set(i, toEmit[i]->v8Value().toHandle());
+
+ if (isEmitFollowConnected())
+ emitFollowParticles(QDeclarativeV8Handle::fromHandle(array), d->v8Value());//A chance for many arbitrary JS changes
+ else if (isEmitConnected())
+ emitParticles(QDeclarativeV8Handle::fromHandle(array));//A chance for arbitrary JS changes
+ }
+ foreach (QQuickParticleData* d, toEmit)
+ m_system->emitParticle(d);
+ m_lastEmission[d->index] = pt;
+ }
+
+ m_lastTimeStamp = time;
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquicktrailemitter_p.h b/src/quick/particles/qquicktrailemitter_p.h
new file mode 100644
index 0000000000..871d8b1b69
--- /dev/null
+++ b/src/quick/particles/qquicktrailemitter_p.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FOLLOWEMITTER_H
+#define FOLLOWEMITTER_H
+#include "qquickparticleemitter_p.h"
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTrailEmitter : public QQuickParticleEmitter
+{
+ Q_OBJECT
+ Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged)
+ Q_PROPERTY(int emitRatePerParticle READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged)
+
+ Q_PROPERTY(QQuickParticleExtruder* emitShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged)
+ Q_PROPERTY(qreal emitHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged)
+ Q_PROPERTY(qreal emitWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged)
+
+ Q_ENUMS(EmitSize)
+public:
+ enum EmitSize {
+ ParticleSize = -2//Anything less than 0 will do
+ };
+ explicit QQuickTrailEmitter(QQuickItem *parent = 0);
+ virtual void emitWindow(int timeStamp);
+ virtual void reset();
+
+ int particlesPerParticlePerSecond() const
+ {
+ return m_particlesPerParticlePerSecond;
+ }
+
+ qreal emitterXVariation() const
+ {
+ return m_emitterXVariation;
+ }
+
+ qreal emitterYVariation() const
+ {
+ return m_emitterYVariation;
+ }
+
+ QString follow() const
+ {
+ return m_follow;
+ }
+
+ QQuickParticleExtruder* emissonShape() const
+ {
+ return m_emissionExtruder;
+ }
+
+signals:
+ void emitFollowParticles(QDeclarativeV8Handle particles, QDeclarativeV8Handle followed);
+
+ void particlesPerParticlePerSecondChanged(int arg);
+
+ void emitterXVariationChanged(qreal arg);
+
+ void emitterYVariationChanged(qreal arg);
+
+ void followChanged(QString arg);
+
+ void emissionShapeChanged(QQuickParticleExtruder* arg);
+
+public slots:
+
+ void setParticlesPerParticlePerSecond(int arg)
+ {
+ if (m_particlesPerParticlePerSecond != arg) {
+ m_particlesPerParticlePerSecond = arg;
+ emit particlesPerParticlePerSecondChanged(arg);
+ }
+ }
+ void setEmitterXVariation(qreal arg)
+ {
+ if (m_emitterXVariation != arg) {
+ m_emitterXVariation = arg;
+ emit emitterXVariationChanged(arg);
+ }
+ }
+
+ void setEmitterYVariation(qreal arg)
+ {
+ if (m_emitterYVariation != arg) {
+ m_emitterYVariation = arg;
+ emit emitterYVariationChanged(arg);
+ }
+ }
+
+ void setFollow(QString arg)
+ {
+ if (m_follow != arg) {
+ m_follow = arg;
+ emit followChanged(arg);
+ }
+ }
+
+ void setEmissionShape(QQuickParticleExtruder* arg)
+ {
+ if (m_emissionExtruder != arg) {
+ m_emissionExtruder = arg;
+ emit emissionShapeChanged(arg);
+ }
+ }
+
+private slots:
+ void recalcParticlesPerSecond();
+
+private:
+ QSet<QQuickParticleData*> m_pending;
+ QVector<qreal> m_lastEmission;
+ int m_particlesPerParticlePerSecond;
+ qreal m_lastTimeStamp;
+ qreal m_emitterXVariation;
+ qreal m_emitterYVariation;
+ QString m_follow;
+ int m_followCount;
+ QQuickParticleExtruder* m_emissionExtruder;
+ QQuickParticleExtruder* m_defaultEmissionExtruder;
+ bool isEmitFollowConnected();
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // FOLLOWEMITTER_H
diff --git a/src/quick/particles/qquickturbulence.cpp b/src/quick/particles/qquickturbulence.cpp
new file mode 100644
index 0000000000..0afd827af1
--- /dev/null
+++ b/src/quick/particles/qquickturbulence.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickturbulence_p.h"
+#include "qquickparticlepainter_p.h"//TODO: Why was this needed again?
+#include <cmath>
+#include <cstdlib>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Turbulence QQuickTurbulenceAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief Turbulence provides fluid like forces based on a noise image.
+
+ The Turbulence Element scales the noise source over the area it affects,
+ and uses the curl of that source to generate force vectors.
+
+ Turbulence requires a fixed size. Unlike other affectors, a 0x0 Turbulence element
+ will affect no particles.
+
+ The source should be relatively smooth black and white noise, such as perlin noise.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Turbulence::strength
+
+ The magnitude of the velocity vector at any point varies between zero and
+ the square root of two. It will then be multiplied by strength to get the
+ velocity per second for the particles affected by the turbulence.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::Turbulence::noiseSource
+
+ The source image to generate the turbulence from. It will be scaled to the size of the element,
+ so equal or larger sizes will give better results. Tweaking this image is the only way to tweak
+ behavior such as where vortices are or how many exist.
+
+ The source should be a relatively smooth black and white noise image, such as perlin noise.
+ A default image will be used if none is provided.
+*/
+
+QQuickTurbulenceAffector::QQuickTurbulenceAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent),
+ m_strength(10), m_lastT(0), m_gridSize(0), m_field(0), m_vectorField(0), m_inited(false)
+{
+}
+
+void QQuickTurbulenceAffector::geometryChanged(const QRectF &, const QRectF &)
+{
+ initializeGrid();
+}
+
+QQuickTurbulenceAffector::~QQuickTurbulenceAffector()
+{
+ if (m_field) {
+ for (int i=0; i<m_gridSize; i++)
+ free(m_field[i]);
+ free(m_field);
+ }
+ if (m_vectorField) {
+ for (int i=0; i<m_gridSize; i++)
+ free(m_vectorField[i]);
+ free(m_vectorField);
+ }
+}
+
+void QQuickTurbulenceAffector::initializeGrid()
+{
+ if (!m_inited)
+ return;
+
+ int arg = qMax(width(), height());
+ if (m_gridSize != arg) {
+ if (m_field){ //deallocate and then reallocate grid
+ for (int i=0; i<m_gridSize; i++)
+ free(m_field[i]);
+ free(m_field);
+ m_system = 0;
+ }
+ if (m_vectorField) {
+ for (int i=0; i<m_gridSize; i++)
+ free(m_vectorField[i]);
+ free(m_vectorField);
+ }
+ m_gridSize = arg;
+ }
+
+ m_field = (qreal**)malloc(m_gridSize * sizeof(qreal*));
+ for (int i=0; i<m_gridSize; i++)
+ m_field[i] = (qreal*)malloc(m_gridSize * sizeof(qreal));
+ m_vectorField = (QPointF**)malloc(m_gridSize * sizeof(QPointF*));
+ for (int i=0; i<m_gridSize; i++)
+ m_vectorField[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF));
+
+ QImage image;
+ if (!m_noiseSource.isEmpty())
+ image = QImage(m_noiseSource.toLocalFile()).scaled(QSize(m_gridSize, m_gridSize));
+ if (image.isNull())
+ image = QImage(QStringLiteral(":particleresources/noise.png")).scaled(QSize(m_gridSize, m_gridSize));
+
+ for (int i=0; i<m_gridSize; i++)
+ for (int j=0; j<m_gridSize; j++)
+ m_field[i][j] = qRed(image.pixel(QPoint(i,j)));//Red as proxy for Value
+ for (int i=0; i<m_gridSize; i++){
+ for (int j=0; j<m_gridSize; j++){
+ m_vectorField[i][j].setX(boundsRespectingField(i,j) - boundsRespectingField(i,j-1));
+ m_vectorField[i][j].setY(boundsRespectingField(i-1,j) - boundsRespectingField(i,j));
+ }
+ }
+}
+
+qreal QQuickTurbulenceAffector::boundsRespectingField(int x, int y)
+{
+ if (x < 0)
+ x = 0;
+ if (x >= m_gridSize)
+ x = m_gridSize - 1;
+ if (y < 0)
+ y = 0;
+ if (y >= m_gridSize)
+ y = m_gridSize - 1;
+ return m_field[x][y];
+}
+
+void QQuickTurbulenceAffector::ensureInit()
+{
+ if (m_inited)
+ return;
+ m_inited = true;
+ initializeGrid();
+}
+
+void QQuickTurbulenceAffector::affectSystem(qreal dt)
+{
+ if (!m_system || !m_enabled)
+ return;
+ ensureInit();
+ if (!m_gridSize)
+ return;
+
+ updateOffsets();//### Needed if an ancestor is transformed.
+
+ QRect boundsRect(0,0,m_gridSize,m_gridSize);
+ foreach (QQuickParticleGroupData *gd, m_system->groupData){
+ if (!activeGroup(m_system->groupData.key(gd)))
+ continue;
+ foreach (QQuickParticleData *d, gd->data){
+ if (!shouldAffect(d))
+ continue;
+ QPoint pos = (QPointF(d->curX(), d->curY()) - m_offset).toPoint();
+ if (!boundsRect.contains(pos,true))//Need to redo bounds checking due to quantization.
+ continue;
+ qreal fx = 0.0;
+ qreal fy = 0.0;
+ fx += m_vectorField[pos.x()][pos.y()].x() * m_strength;
+ fy += m_vectorField[pos.x()][pos.y()].y() * m_strength;
+ if (fx || fy){
+ d->setInstantaneousVX(d->curVX()+ fx * dt);
+ d->setInstantaneousVY(d->curVY()+ fy * dt);
+ postAffect(d);
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickturbulence_p.h b/src/quick/particles/qquickturbulence_p.h
new file mode 100644
index 0000000000..b7eb7e662f
--- /dev/null
+++ b/src/quick/particles/qquickturbulence_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TURBULENCEAFFECTOR_H
+#define TURBULENCEAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+#include <QDeclarativeListProperty>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticlePainter;
+
+class QQuickTurbulenceAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
+ Q_PROPERTY(QUrl noiseSource READ noiseSource WRITE setNoiseSource NOTIFY noiseSourceChanged)
+ public:
+ explicit QQuickTurbulenceAffector(QQuickItem *parent = 0);
+ ~QQuickTurbulenceAffector();
+ virtual void affectSystem(qreal dt);
+
+ qreal strength() const
+ {
+ return m_strength;
+ }
+
+ QUrl noiseSource() const
+ {
+ return m_noiseSource;
+ }
+signals:
+
+ void strengthChanged(qreal arg);
+
+ void noiseSourceChanged(QUrl arg);
+
+public slots:
+
+ void setStrength(qreal arg)
+ {
+ if (m_strength != arg) {
+ m_strength = arg;
+ emit strengthChanged(arg);
+ }
+ }
+
+ void setNoiseSource(QUrl arg)
+ {
+ if (m_noiseSource != arg) {
+ m_noiseSource = arg;
+ emit noiseSourceChanged(arg);
+ initializeGrid();
+ }
+ }
+
+protected:
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+private:
+ void ensureInit();
+ void mapUpdate();
+ void initializeGrid();
+ qreal boundsRespectingField(int x, int y);
+ qreal m_strength;
+ qreal m_lastT;
+ int m_gridSize;
+ qreal** m_field;
+ QPointF** m_vectorField;
+ bool m_inited;
+ QUrl m_noiseSource;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // TURBULENCEAFFECTOR_H
diff --git a/src/quick/particles/qquickv8particledata.cpp b/src/quick/particles/qquickv8particledata.cpp
new file mode 100644
index 0000000000..80ce355cd7
--- /dev/null
+++ b/src/quick/particles/qquickv8particledata.cpp
@@ -0,0 +1,503 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickv8particledata_p.h"
+#include "qquickparticlesystem_p.h"//for QQuickParticleData
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Particle
+ \inqmlmodule QtQuick.Particles 2
+ \brief Particle elements can be manipulated in custom emitters and affectors.
+
+ Particle elements are always managed internally by the ParticleSystem and cannot be created in QML.
+ However, sometimes they are exposed via signals so as to allow arbitrary changes to the particle state
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialX
+ The x coordinate of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialVX
+ The x velocity of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialAX
+ The x acceleration of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialY
+ The y coordinate of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialVY
+ The y velocity of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialAY
+ The y acceleration of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::x
+ The current x coordinate of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::vx
+ The current x velocity of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::ax
+ The current x acceleration of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::y
+ The current y coordinate of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::vy
+ The current y velocity of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::ay
+ The current y acceleration of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::t
+ The time, in seconds since the beginning of the simulation, that the particle was born.
+*/
+
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::startSize
+ The size in pixels that the particle image is at the start
+ of its life.
+*/
+
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::endSize
+ The size in pixels that the particle image is at the end
+ of its life. If this value is less than 0, then it is
+ disregarded and the particle will have its startSize for the
+ entire lifetime.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::lifeSpan
+ The time in seconds that the particle will live for.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::rotation
+ Degrees clockwise that the particle image is rotated at
+ the beginning of its life.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::rotationSpeed
+ Degrees clockwise per second that the particle image is rotated at while alive.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Particle::autoRotate
+ If autoRotate is true, then the particle's rotation will be
+ set so that it faces the direction of travel, plus any
+ rotation from the rotation or rotationSpeed properties.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::Particle::update
+
+ Inside an Affector, the changes made to the particle will only be
+ applied if update is set to true.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::xDeformationVectorX
+
+ The x component of the deformation vector along the X axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::yDeformationVectorX
+
+ The y component of the deformation vector along the X axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::xDeformationVectorY
+
+ The x component of the deformation vector along the X axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::yDeformationVectorY
+
+ The y component of the deformation vector along the Y axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::red
+
+ ImageParticle can draw colorized particles. When it does so, red is used
+ as the red channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::green
+
+ ImageParticle can draw colorized particles. When it does so, green is used
+ as the green channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::blue
+
+ ImageParticle can draw colorized particles. When it does so, blue is used
+ as the blue channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::alpha
+
+ ImageParticle can draw colorized particles. When it does so, alpha is used
+ as the alpha channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+/*!
+ \qmlmethod real QtQuick.Particles2::Particle::lifeLeft
+ The time in seconds that the particle has left to live at
+ the current point in time.
+*/
+/*!
+ \qmlmethod real QtQuick.Particles2::Particle::currentSize
+ The currentSize of the particle, interpolating between startSize and endSize based on the currentTime.
+*/
+
+
+
+//### Particle data handles are not locked to within certain scopes like QQuickContext2D, but there's no way to reload either...
+class QV8ParticleDataResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(ParticleDataType)
+public:
+ QV8ParticleDataResource(QV8Engine *e) : QV8ObjectResource(e) {}
+ QQuickParticleData* datum;//TODO: Guard needed?
+};
+
+class QV8ParticleDataDeletable : public QV8Engine::Deletable
+{
+public:
+ QV8ParticleDataDeletable(QV8Engine *engine);
+ ~QV8ParticleDataDeletable();
+
+ v8::Persistent<v8::Function> constructor;
+};
+
+static v8::Handle<v8::Value> particleData_discard(const v8::Arguments &args)
+{
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+
+ if (!r || !r->datum)
+ V8THROW_ERROR("Not a valid ParticleData object");
+
+ r->datum->lifeSpan = 0; //Don't kill(), because it could still be in the middle of being created
+ return v8::Undefined();
+}
+
+static v8::Handle<v8::Value> particleData_lifeLeft(const v8::Arguments &args)
+{
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+ if (!r || !r->datum)
+ V8THROW_ERROR("Not a valid ParticleData object");
+
+ return v8::Number::New(r->datum->lifeLeft());
+}
+
+static v8::Handle<v8::Value> particleData_curSize(const v8::Arguments &args)
+{
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+ if (!r || !r->datum)
+ V8THROW_ERROR("Not a valid ParticleData object");
+
+ return v8::Number::New(r->datum->curSize());
+}
+#define COLOR_GETTER_AND_SETTER(VAR, NAME) static v8::Handle<v8::Value> particleData_get_ ## NAME (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Number::New((r->datum->color. VAR )/255.0);\
+}\
+\
+static void particleData_set_ ## NAME (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum->color. VAR = qMin(255, qMax(0, (int)floor(value->NumberValue() * 255.0)));\
+}
+
+
+#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Boolean::New(r->datum-> VARIABLE);\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum-> VARIABLE = value->BooleanValue() ? 1.0 : 0.0;\
+}
+
+#define FLOAT_GETTER_AND_SETTER(VARIABLE) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Number::New(r->datum-> VARIABLE);\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum-> VARIABLE = value->NumberValue();\
+}
+
+#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Number::New(r->datum-> GETTER ());\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum-> SETTER ( value->NumberValue() );\
+}
+
+#define REGISTER_ACCESSOR(FT, ENGINE, VARIABLE, NAME) FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #NAME ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE , v8::External::Wrap(ENGINE))
+
+COLOR_GETTER_AND_SETTER(r, red)
+COLOR_GETTER_AND_SETTER(g, green)
+COLOR_GETTER_AND_SETTER(b, blue)
+COLOR_GETTER_AND_SETTER(a, alpha)
+SEMIBOOL_GETTER_AND_SETTER(autoRotate)
+SEMIBOOL_GETTER_AND_SETTER(update)
+FLOAT_GETTER_AND_SETTER(x)
+FLOAT_GETTER_AND_SETTER(y)
+FLOAT_GETTER_AND_SETTER(t)
+FLOAT_GETTER_AND_SETTER(lifeSpan)
+FLOAT_GETTER_AND_SETTER(size)
+FLOAT_GETTER_AND_SETTER(endSize)
+FLOAT_GETTER_AND_SETTER(vx)
+FLOAT_GETTER_AND_SETTER(vy)
+FLOAT_GETTER_AND_SETTER(ax)
+FLOAT_GETTER_AND_SETTER(ay)
+FLOAT_GETTER_AND_SETTER(xx)
+FLOAT_GETTER_AND_SETTER(xy)
+FLOAT_GETTER_AND_SETTER(yx)
+FLOAT_GETTER_AND_SETTER(yy)
+FLOAT_GETTER_AND_SETTER(rotation)
+FLOAT_GETTER_AND_SETTER(rotationSpeed)
+FLOAT_GETTER_AND_SETTER(animIdx)
+FLOAT_GETTER_AND_SETTER(frameDuration)
+FLOAT_GETTER_AND_SETTER(frameCount)
+FLOAT_GETTER_AND_SETTER(animT)
+FLOAT_GETTER_AND_SETTER(r)
+FAKE_FLOAT_GETTER_AND_SETTER(curX, curX, setInstantaneousX)
+FAKE_FLOAT_GETTER_AND_SETTER(curVX, curVX, setInstantaneousVX)
+FAKE_FLOAT_GETTER_AND_SETTER(curAX, curAX, setInstantaneousAX)
+FAKE_FLOAT_GETTER_AND_SETTER(curY, curY, setInstantaneousY)
+FAKE_FLOAT_GETTER_AND_SETTER(curVY, curVY, setInstantaneousVY)
+FAKE_FLOAT_GETTER_AND_SETTER(curAY, curAY, setInstantaneousAY)
+
+QV8ParticleDataDeletable::QV8ParticleDataDeletable(QV8Engine *engine)
+{
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("discard"), V8FUNCTION(particleData_discard, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("lifeLeft"), V8FUNCTION(particleData_lifeLeft, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("currentSize"), V8FUNCTION(particleData_curSize, engine));
+ REGISTER_ACCESSOR(ft, engine, x, initialX);
+ REGISTER_ACCESSOR(ft, engine, y, initialY);
+ REGISTER_ACCESSOR(ft, engine, t, t);
+ REGISTER_ACCESSOR(ft, engine, lifeSpan, lifeSpan);
+ REGISTER_ACCESSOR(ft, engine, size, startSize);
+ REGISTER_ACCESSOR(ft, engine, endSize, endSize);
+ REGISTER_ACCESSOR(ft, engine, vx, initialVX);
+ REGISTER_ACCESSOR(ft, engine, vy, initialVY);
+ REGISTER_ACCESSOR(ft, engine, ax, initialAX);
+ REGISTER_ACCESSOR(ft, engine, ay, initialAY);
+ REGISTER_ACCESSOR(ft, engine, xx, xDeformationVectorX);
+ REGISTER_ACCESSOR(ft, engine, xy, xDeformationVectorY);
+ REGISTER_ACCESSOR(ft, engine, yx, yDeformationVectorX);
+ REGISTER_ACCESSOR(ft, engine, yy, yDeformationVectorY);
+ REGISTER_ACCESSOR(ft, engine, rotation, rotation);
+ REGISTER_ACCESSOR(ft, engine, rotationSpeed, rotationSpeed);
+ REGISTER_ACCESSOR(ft, engine, autoRotate, autoRotate);
+ REGISTER_ACCESSOR(ft, engine, animIdx, animationIndex);
+ REGISTER_ACCESSOR(ft, engine, frameDuration, frameDuration);
+ REGISTER_ACCESSOR(ft, engine, frameCount, frameCount);
+ REGISTER_ACCESSOR(ft, engine, animT, animationT);
+ REGISTER_ACCESSOR(ft, engine, r, r);
+ REGISTER_ACCESSOR(ft, engine, update, update);
+ REGISTER_ACCESSOR(ft, engine, curX, x);
+ REGISTER_ACCESSOR(ft, engine, curVX, vx);
+ REGISTER_ACCESSOR(ft, engine, curAX, ax);
+ REGISTER_ACCESSOR(ft, engine, curY, y);
+ REGISTER_ACCESSOR(ft, engine, curVY, vy);
+ REGISTER_ACCESSOR(ft, engine, curAY, ay);
+ REGISTER_ACCESSOR(ft, engine, red, red);
+ REGISTER_ACCESSOR(ft, engine, green, green);
+ REGISTER_ACCESSOR(ft, engine, blue, blue);
+ REGISTER_ACCESSOR(ft, engine, alpha, alpha);
+
+ constructor = qPersistentNew(ft->GetFunction());
+}
+
+QV8ParticleDataDeletable::~QV8ParticleDataDeletable()
+{
+ qPersistentDispose(constructor);
+}
+
+V8_DEFINE_EXTENSION(QV8ParticleDataDeletable, particleV8Data);
+
+
+QQuickV8ParticleData::QQuickV8ParticleData(QV8Engine* engine, QQuickParticleData* datum)
+{
+ if (!engine || !datum)
+ return;
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ QV8ParticleDataDeletable *d = particleV8Data(engine);
+ m_v8Value = qPersistentNew(d->constructor->NewInstance());
+ QV8ParticleDataResource *r = new QV8ParticleDataResource(engine);
+ r->datum = datum;
+ m_v8Value->SetExternalResource(r);
+}
+
+QQuickV8ParticleData::~QQuickV8ParticleData()
+{
+ qPersistentDispose(m_v8Value);
+}
+
+QDeclarativeV8Handle QQuickV8ParticleData::v8Value()
+{
+ return QDeclarativeV8Handle::fromHandle(m_v8Value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickv8particledata_p.h b/src/quick/particles/qquickv8particledata_p.h
new file mode 100644
index 0000000000..f94a987aac
--- /dev/null
+++ b/src/quick/particles/qquickv8particledata_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickV8PARTICLEDATA_H
+#define QQuickV8PARTICLEDATA_H
+
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleData;
+class QQuickV8ParticleData {
+public:
+ QQuickV8ParticleData(QV8Engine*,QQuickParticleData*);
+ ~QQuickV8ParticleData();
+ QDeclarativeV8Handle v8Value();
+private:
+ v8::Persistent<v8::Object> m_v8Value;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif
diff --git a/src/quick/particles/qquickwander.cpp b/src/quick/particles/qquickwander.cpp
new file mode 100644
index 0000000000..dfbff36076
--- /dev/null
+++ b/src/quick/particles/qquickwander.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickwander_p.h"
+#include "qquickparticlesystem_p.h"//for ParticlesVertices
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Wander QQuickWanderAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Wander affector allows particles to randomly vary their trajectory.
+
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Wander::pace
+
+ Maximum attribute change per second.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Wander::xVariance
+
+ Maximum attribute x value (as a result of Wander).
+
+ If unset, Wander will not affect x values.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Wander::yVariance
+
+ Maximum attribute y value (as a result of Wander).
+
+ If unset, Wander will not affect y values.
+*/
+/*!
+ \qmlproperty AffectableParameter QtQuick.Particles2::Wander::affectedParameter
+
+ What attribute of particles is directly affected.
+ \list
+ \o PointAttractor.Position
+ \o PointAttractor.Velocity
+ \o PointAttractor.Acceleration
+ \endlist
+*/
+
+QQuickWanderAffector::QQuickWanderAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_xVariance(0), m_yVariance(0), m_pace(0)
+ , m_affectedParameter(Velocity)
+{
+ m_needsReset = true;
+}
+
+QQuickWanderAffector::~QQuickWanderAffector()
+{
+ for (QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin();
+ iter != m_wanderData.constEnd(); iter++)
+ delete (*iter);
+}
+
+WanderData* QQuickWanderAffector::getData(int idx)
+{
+ if (m_wanderData.contains(idx))
+ return m_wanderData[idx];
+ WanderData* d = new WanderData;
+ d->x_vel = 0;
+ d->y_vel = 0;
+ d->x_peak = m_xVariance;
+ d->y_peak = m_yVariance;
+ d->x_var = m_pace * qreal(qrand()) / RAND_MAX;
+ d->y_var = m_pace * qreal(qrand()) / RAND_MAX;
+
+ m_wanderData.insert(idx, d);
+ return d;
+}
+
+void QQuickWanderAffector::reset(int systemIdx)
+{
+ if (m_wanderData.contains(systemIdx))
+ delete m_wanderData[systemIdx];
+ m_wanderData.remove(systemIdx);
+}
+
+bool QQuickWanderAffector::affectParticle(QQuickParticleData* data, qreal dt)
+{
+ /*TODO: Add a mode which does basically this - picking a direction, going in it (random speed) and then going back
+ WanderData* d = getData(data->systemIndex);
+ if (m_xVariance != 0.) {
+ if ((d->x_vel > d->x_peak && d->x_var > 0.0) || (d->x_vel < -d->x_peak && d->x_var < 0.0)) {
+ d->x_var = -d->x_var;
+ d->x_peak = m_xVariance + m_xVariance * qreal(qrand()) / RAND_MAX;
+ }
+ d->x_vel += d->x_var * dt;
+ }
+ qreal dx = dt * d->x_vel;
+
+ if (m_yVariance != 0.) {
+ if ((d->y_vel > d->y_peak && d->y_var > 0.0) || (d->y_vel < -d->y_peak && d->y_var < 0.0)) {
+ d->y_var = -d->y_var;
+ d->y_peak = m_yVariance + m_yVariance * qreal(qrand()) / RAND_MAX;
+ }
+ d->y_vel += d->y_var * dt;
+ }
+ qreal dy = dt * d->x_vel;
+
+ //### Should we be amending vel instead?
+ ParticleVertex* p = &(data->pv);
+ p->x += dx;
+
+ p->y += dy;
+ return true;
+ */
+ qreal dx = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1);
+ qreal dy = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1);
+ qreal newX, newY;
+ switch (m_affectedParameter){
+ case Position:
+ newX = data->curX() + dx;
+ if (m_xVariance > qAbs(newX) )
+ data->x += dx;
+ newY = data->curY() + dy;
+ if (m_yVariance > qAbs(newY) )
+ data->y += dy;
+ break;
+ default:
+ case Velocity:
+ newX = data->curVX() + dx;
+ if (m_xVariance > qAbs(newX) )
+ data->setInstantaneousVX(newX);
+ newY = data->curVY() + dy;
+ if (m_yVariance > qAbs(newY) )
+ data->setInstantaneousVY(newY);
+ break;
+ case Acceleration:
+ newX = data->ax + dx;
+ if (m_xVariance > qAbs(newX) )
+ data->setInstantaneousAX(newX);
+ newY = data->ay + dy;
+ if (m_yVariance > qAbs(newY) )
+ data->setInstantaneousAY(newY);
+ break;
+ }
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickwander_p.h b/src/quick/particles/qquickwander_p.h
new file mode 100644
index 0000000000..f8a28e57e4
--- /dev/null
+++ b/src/quick/particles/qquickwander_p.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WANDERAFFECTOR_H
+#define WANDERAFFECTOR_H
+#include <QHash>
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+struct WanderData{
+ qreal x_vel;
+ qreal y_vel;
+ qreal x_peak;
+ qreal x_var;
+ qreal y_peak;
+ qreal y_var;
+};
+
+class QQuickWanderAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged)
+ Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged)
+ Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged)
+ Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged)
+ Q_ENUMS(AffectableParameters)
+
+public:
+ enum AffectableParameters {
+ Position,
+ Velocity,
+ Acceleration
+ };
+
+ explicit QQuickWanderAffector(QQuickItem *parent = 0);
+ ~QQuickWanderAffector();
+ virtual void reset(int systemIdx);
+
+ qreal xVariance() const
+ {
+ return m_xVariance;
+ }
+
+ qreal yVariance() const
+ {
+ return m_yVariance;
+ }
+
+ qreal pace() const
+ {
+ return m_pace;
+ }
+
+ AffectableParameters affectedParameter() const
+ {
+ return m_affectedParameter;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+
+ void xVarianceChanged(qreal arg);
+
+ void yVarianceChanged(qreal arg);
+
+ void paceChanged(qreal arg);
+
+
+ void affectedParameterChanged(AffectableParameters arg);
+
+public slots:
+void setXVariance(qreal arg)
+{
+ if (m_xVariance != arg) {
+ m_xVariance = arg;
+ emit xVarianceChanged(arg);
+ }
+}
+
+void setYVariance(qreal arg)
+{
+ if (m_yVariance != arg) {
+ m_yVariance = arg;
+ emit yVarianceChanged(arg);
+ }
+}
+
+void setPace(qreal arg)
+{
+ if (m_pace != arg) {
+ m_pace = arg;
+ emit paceChanged(arg);
+ }
+}
+
+
+void setAffectedParameter(AffectableParameters arg)
+{
+ if (m_affectedParameter != arg) {
+ m_affectedParameter = arg;
+ emit affectedParameterChanged(arg);
+ }
+}
+
+private:
+ WanderData* getData(int idx);
+ QHash<int, WanderData*> m_wanderData;
+ qreal m_xVariance;
+ qreal m_yVariance;
+ qreal m_pace;
+ AffectableParameters m_affectedParameter;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // WANDERAFFECTOR_H