Rename Qt Quick-specific classes to QQuick*
[profile/ivi/qtdeclarative.git] / src / declarative / particles / qsgtrailemitter.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Declarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgtrailemitter_p.h"
43 #include <private/qdeclarativeengine_p.h>
44 #include <cmath>
45 QT_BEGIN_NAMESPACE
46
47 /*!
48     \qmlclass TrailEmitter QSGTrailEmitter
49     \inqmlmodule QtQuick.Particles 2
50     \inherits QSGParticleEmitter
51     \brief The TrailEmitter element allows you to emit logical particles from other logical particles.
52
53     This element emits logical particles into the ParticleSystem, with the
54     starting positions based on those of other logical particles.
55 */
56 QSGTrailEmitter::QSGTrailEmitter(QQuickItem *parent) :
57     QSGParticleEmitter(parent)
58   , m_particlesPerParticlePerSecond(0)
59   , m_lastTimeStamp(0)
60   , m_emitterXVariation(0)
61   , m_emitterYVariation(0)
62   , m_followCount(0)
63   , m_emissionExtruder(0)
64   , m_defaultEmissionExtruder(new QSGParticleExtruder(this))
65 {
66     //TODO: If followed increased their size
67     connect(this, SIGNAL(followChanged(QString)),
68             this, SLOT(recalcParticlesPerSecond()));
69     connect(this, SIGNAL(particleDurationChanged(int)),
70             this, SLOT(recalcParticlesPerSecond()));
71     connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)),
72             this, SLOT(recalcParticlesPerSecond()));
73 }
74
75 /*!
76     \qmlproperty string QtQuick.Particles2::TrailEmitter::follow
77
78     The type of logical particle which this is emitting from.
79 */
80 /*!
81     \qmlproperty qreal QtQuick.Particles2::TrailEmitter::speedFromMovement
82
83     If this value is non-zero, then any movement of the emitter will provide additional
84     starting velocity to the particles based on the movement. The additional vector will be the
85     same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters
86     movement multiplied by speedFromMovement.
87
88     Default value is 0.
89 */
90 /*!
91     \qmlproperty Shape QtQuick.Particles2::TrailEmitter::emitShape
92
93     As the area of a TrailEmitter is the area it follows, a separate shape can be provided
94     to be the shape it emits out of. This shape has width and height specified by emitWidth
95     and emitHeight, and is centered on the followed particle's position.
96
97     The default shape is a filled Rectangle.
98 */
99 /*!
100     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitWidth
101
102     The width in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
103     the width will be the current size of the particle being followed.
104
105     Default is 0.
106 */
107 /*!
108     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitHeight
109
110     The height in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
111     the height will be the current size of the particle being followed.
112
113     Default is 0.
114 */
115 /*!
116     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle
117 */
118 /*!
119     \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticles(Array particles, real followed)
120
121     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.
122
123     If you use this signal handler, emitParticles will not be emitted.
124
125 */
126
127 bool QSGTrailEmitter::isEmitFollowConnected()
128 {
129     static int idx = QObjectPrivate::get(this)->signalIndex("emitFollowParticles(QDeclarativeV8Handle,QDeclarativeV8Handle)");
130     return QObjectPrivate::get(this)->isSignalConnected(idx);
131 }
132
133 void QSGTrailEmitter::recalcParticlesPerSecond(){
134     if (!m_system)
135         return;
136     m_followCount = m_system->groupData[m_system->groupIds[m_follow]]->size();
137     if (!m_followCount){
138         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)
139     }else{
140         setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount);
141         m_lastEmission.resize(m_followCount);
142         m_lastEmission.fill(m_lastTimeStamp);
143     }
144 }
145
146 void QSGTrailEmitter::reset()
147 {
148     m_followCount = 0;
149 }
150
151 void QSGTrailEmitter::emitWindow(int timeStamp)
152 {
153     if (m_system == 0)
154         return;
155     if (!m_enabled && !m_pulseLeft && m_burstQueue.isEmpty())
156         return;
157     if (m_followCount != m_system->groupData[m_system->groupIds[m_follow]]->size()){
158         qreal oldPPS = m_particlesPerSecond;
159         recalcParticlesPerSecond();
160         if (m_particlesPerSecond != oldPPS)
161             return;//system may need to update
162     }
163
164     if (m_pulseLeft){
165         m_pulseLeft -= timeStamp - m_lastTimeStamp * 1000.;
166         if (m_pulseLeft < 0){
167             timeStamp += m_pulseLeft;
168             m_pulseLeft = 0;
169         }
170     }
171
172     //TODO: Implement startTime and speedFromMovement
173     qreal time = timeStamp / 1000.;
174     qreal particleRatio = 1. / m_particlesPerParticlePerSecond;
175     qreal pt;
176     qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
177
178     //Have to map it into this system, because particlesystem automaps it back
179     QPointF offset = m_system->mapFromItem(this, QPointF(0, 0));
180     qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
181
182     int gId = m_system->groupIds[m_follow];
183     int gId2 = m_system->groupIds[m_group];
184     foreach (QSGParticleData *d, m_system->groupData[gId]->data){
185         if (!d || !d->stillAlive()){
186             m_lastEmission[d->index] = time; //Should only start emitting when it returns to life
187             continue;
188         }
189         pt = m_lastEmission[d->index];
190         if (pt < d->t)
191             pt = d->t;
192         if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
193             pt = time - maxLife;
194
195         if ((width() || height()) && !effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
196             m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
197             continue;
198         }
199
200         QList<QSGParticleData*> toEmit;
201
202         while (pt < time || !m_burstQueue.isEmpty()){
203             QSGParticleData* datum = m_system->newDatum(gId2, !m_overwrite);
204             if (datum){//else, skip this emission
205                 datum->e = this;//###useful?
206
207                 // Particle timestamp
208                 datum->t = pt;
209                 datum->lifeSpan =
210                         (m_particleDuration
211                          + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
212                         / 1000.0;
213
214                 // Particle position
215                 // Note that burst location doesn't get used for follow emitter
216                 qreal followT =  pt - d->t;
217                 qreal followT2 = followT * followT * 0.5;
218                 qreal eW = m_emitterXVariation < 0 ? d->curSize() : m_emitterXVariation;
219                 qreal eH = m_emitterYVariation < 0 ? d->curSize() : m_emitterYVariation;
220                 //Subtract offset, because PS expects this in emitter coordinates
221                 QRectF boundsRect(d->x - offset.x() + d->vx * followT + d->ax * followT2 - eW/2,
222                                   d->y - offset.y() + d->vy * followT + d->ay * followT2 - eH/2,
223                                   eW, eH);
224
225                 QSGParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder;
226                 const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect);
227                 datum->x = newPos.x();
228                 datum->y = newPos.y();
229
230                 // Particle speed
231                 const QPointF &speed = m_speed->sample(newPos);
232                 datum->vx = speed.x()
233                     + m_speed_from_movement * d->vx;
234                 datum->vy = speed.y()
235                     + m_speed_from_movement * d->vy;
236
237                 // Particle acceleration
238                 const QPointF &accel = m_acceleration->sample(newPos);
239                 datum->ax = accel.x();
240                 datum->ay = accel.y();
241
242                 // Particle size
243                 float sizeVariation = -m_particleSizeVariation
244                         + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
245
246                 float size = qMax((qreal)0.0, m_particleSize + sizeVariation);
247                 float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation);
248
249                 datum->size = size * float(m_enabled);
250                 datum->endSize = endSize * float(m_enabled);
251
252                 toEmit << datum;
253
254                 m_system->emitParticle(datum);
255             }
256             if (!m_burstQueue.isEmpty()){
257                 m_burstQueue.first().first--;
258                 if (m_burstQueue.first().first <= 0)
259                     m_burstQueue.pop_front();
260             }else{
261                 pt += particleRatio;
262             }
263         }
264
265         if (isEmitConnected() || isEmitFollowConnected()) {
266             v8::HandleScope handle_scope;
267             v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
268             v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
269             for (int i=0; i<toEmit.size(); i++)
270                 array->Set(i, toEmit[i]->v8Value().toHandle());
271
272             if (isEmitFollowConnected())
273                 emitFollowParticles(QDeclarativeV8Handle::fromHandle(array), d->v8Value());//A chance for many arbitrary JS changes
274             else if (isEmitConnected())
275                 emitParticles(QDeclarativeV8Handle::fromHandle(array));//A chance for arbitrary JS changes
276         }
277         foreach (QSGParticleData* d, toEmit)
278             m_system->emitParticle(d);
279         m_lastEmission[d->index] = pt;
280     }
281
282     m_lastTimeStamp = time;
283 }
284 QT_END_NAMESPACE