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