follow rename of qt_module_config.prf to qt_module.prf
[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     \qmltype TrailEmitter
50     \instantiates QQuickTrailEmitter
51     \inqmlmodule QtQuick.Particles 2
52     \inherits QQuickParticleEmitter
53     \brief Emits logical particles from other logical particles
54     \ingroup qtquick-particles
55
56     This element emits logical particles into the ParticleSystem, with the
57     starting positions based on those of other logical particles.
58 */
59 QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
60     QQuickParticleEmitter(parent)
61   , m_particlesPerParticlePerSecond(0)
62   , m_lastTimeStamp(0)
63   , m_emitterXVariation(0)
64   , m_emitterYVariation(0)
65   , m_followCount(0)
66   , m_emissionExtruder(0)
67   , m_defaultEmissionExtruder(new QQuickParticleExtruder(this))
68 {
69     //TODO: If followed increased their size
70     connect(this, SIGNAL(followChanged(QString)),
71             this, SLOT(recalcParticlesPerSecond()));
72     connect(this, SIGNAL(particleDurationChanged(int)),
73             this, SLOT(recalcParticlesPerSecond()));
74     connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)),
75             this, SLOT(recalcParticlesPerSecond()));
76 }
77
78 /*!
79     \qmlproperty string QtQuick.Particles2::TrailEmitter::follow
80
81     The type of logical particle which this is emitting from.
82 */
83 /*!
84     \qmlproperty qreal QtQuick.Particles2::TrailEmitter::velocityFromMovement
85
86     If this value is non-zero, then any movement of the emitter will provide additional
87     starting velocity to the particles based on the movement. The additional vector will be the
88     same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters
89     movement multiplied by velocityFromMovement.
90
91     Default value is 0.
92 */
93 /*!
94     \qmlproperty Shape QtQuick.Particles2::TrailEmitter::emitShape
95
96     As the area of a TrailEmitter is the area it follows, a separate shape can be provided
97     to be the shape it emits out of. This shape has width and height specified by emitWidth
98     and emitHeight, and is centered on the followed particle's position.
99
100     The default shape is a filled Rectangle.
101 */
102 /*!
103     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitWidth
104
105     The width in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
106     the width will be the current size of the particle being followed.
107
108     Default is 0.
109 */
110 /*!
111     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitHeight
112
113     The height in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
114     the height will be the current size of the particle being followed.
115
116     Default is 0.
117 */
118 /*!
119     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle
120 */
121 /*!
122     \qmlsignal QtQuick.Particles2::TrailEmitter::onEmitFollowParticles(Array particles, Particle followed)
123
124     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.
125
126     If you use this signal handler, emitParticles will not be emitted.
127
128 */
129
130 bool QQuickTrailEmitter::isEmitFollowConnected()
131 {
132     IS_SIGNAL_CONNECTED(this, QQuickTrailEmitter, emitFollowParticles, (QQmlV8Handle,QQmlV8Handle));
133 }
134
135 void QQuickTrailEmitter::recalcParticlesPerSecond(){
136     if (!m_system)
137         return;
138     m_followCount = m_system->groupData[m_system->groupIds[m_follow]]->size();
139     if (!m_followCount){
140         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)
141     }else{
142         setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount);
143         m_lastEmission.resize(m_followCount);
144         m_lastEmission.fill(m_lastTimeStamp);
145     }
146 }
147
148 void QQuickTrailEmitter::reset()
149 {
150     m_followCount = 0;
151 }
152
153 void QQuickTrailEmitter::emitWindow(int timeStamp)
154 {
155     if (m_system == 0)
156         return;
157     if (!m_enabled && !m_pulseLeft && m_burstQueue.isEmpty())
158         return;
159     if (m_followCount != m_system->groupData[m_system->groupIds[m_follow]]->size()){
160         qreal oldPPS = m_particlesPerSecond;
161         recalcParticlesPerSecond();
162         if (m_particlesPerSecond != oldPPS)
163             return;//system may need to update
164     }
165
166     if (m_pulseLeft){
167         m_pulseLeft -= timeStamp - m_lastTimeStamp * 1000.;
168         if (m_pulseLeft < 0){
169             timeStamp += m_pulseLeft;
170             m_pulseLeft = 0;
171         }
172     }
173
174     //TODO: Implement startTime and velocityFromMovement
175     qreal time = timeStamp / 1000.;
176     qreal particleRatio = 1. / m_particlesPerParticlePerSecond;
177     qreal pt;
178     qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
179
180     //Have to map it into this system, because particlesystem automaps it back
181     QPointF offset = m_system->mapFromItem(this, QPointF(0, 0));
182     qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
183
184     int gId = m_system->groupIds[m_follow];
185     int gId2 = m_system->groupIds[m_group];
186     for (int i=0; i<m_system->groupData[gId]->data.count(); i++) {
187         QQuickParticleData *d = m_system->groupData[gId]->data[i];
188         if (!d->stillAlive()){
189             m_lastEmission[i] = time; //Should only start emitting when it returns to life
190             continue;
191         }
192         pt = m_lastEmission[i];
193         if (pt < d->t)
194             pt = d->t;
195         if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
196             pt = time - maxLife;
197
198         if ((width() || height()) && !effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
199             m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
200             continue;
201         }
202
203         QList<QQuickParticleData*> toEmit;
204
205         while (pt < time || !m_burstQueue.isEmpty()){
206             QQuickParticleData* datum = m_system->newDatum(gId2, !m_overwrite);
207             if (datum){//else, skip this emission
208                 datum->e = this;//###useful?
209
210                 // Particle timestamp
211                 datum->t = pt;
212                 datum->lifeSpan =
213                         (m_particleDuration
214                          + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
215                         / 1000.0;
216
217                 // Particle position
218                 // Note that burst location doesn't get used for follow emitter
219                 qreal followT =  pt - d->t;
220                 qreal followT2 = followT * followT * 0.5;
221                 qreal eW = m_emitterXVariation < 0 ? d->curSize() : m_emitterXVariation;
222                 qreal eH = m_emitterYVariation < 0 ? d->curSize() : m_emitterYVariation;
223                 //Subtract offset, because PS expects this in emitter coordinates
224                 QRectF boundsRect(d->x - offset.x() + d->vx * followT + d->ax * followT2 - eW/2,
225                                   d->y - offset.y() + d->vy * followT + d->ay * followT2 - eH/2,
226                                   eW, eH);
227
228                 QQuickParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder;
229                 const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect);
230                 datum->x = newPos.x();
231                 datum->y = newPos.y();
232
233                 // Particle velocity
234                 const QPointF &velocity = m_velocity->sample(newPos);
235                 datum->vx = velocity.x()
236                     + m_velocity_from_movement * d->vx;
237                 datum->vy = velocity.y()
238                     + m_velocity_from_movement * d->vy;
239
240                 // Particle acceleration
241                 const QPointF &accel = m_acceleration->sample(newPos);
242                 datum->ax = accel.x();
243                 datum->ay = accel.y();
244
245                 // Particle size
246                 float sizeVariation = -m_particleSizeVariation
247                         + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
248
249                 float size = qMax((qreal)0.0, m_particleSize + sizeVariation);
250                 float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation);
251
252                 datum->size = size * float(m_enabled);
253                 datum->endSize = endSize * float(m_enabled);
254
255                 toEmit << datum;
256
257                 m_system->emitParticle(datum);
258             }
259             if (!m_burstQueue.isEmpty()){
260                 m_burstQueue.first().first--;
261                 if (m_burstQueue.first().first <= 0)
262                     m_burstQueue.pop_front();
263             }else{
264                 pt += particleRatio;
265             }
266         }
267
268         foreach (QQuickParticleData* d, toEmit)
269             m_system->emitParticle(d);
270
271         if (isEmitConnected() || isEmitFollowConnected()) {
272             v8::HandleScope handle_scope;
273             v8::Context::Scope scope(QQmlEnginePrivate::getV8Engine(qmlEngine(this))->context());
274             v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
275             for (int i=0; i<toEmit.size(); i++)
276                 array->Set(i, toEmit[i]->v8Value().toHandle());
277
278             if (isEmitFollowConnected())
279                 emitFollowParticles(QQmlV8Handle::fromHandle(array), d->v8Value());//A chance for many arbitrary JS changes
280             else if (isEmitConnected())
281                 emitParticles(QQmlV8Handle::fromHandle(array));//A chance for arbitrary JS changes
282         }
283         m_lastEmission[d->index] = pt;
284     }
285
286     m_lastTimeStamp = time;
287 }
288 QT_END_NAMESPACE