1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQuick module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquicktrailemitter_p.h"
43 #include <private/qqmlengine_p.h>
44 #include <private/qqmlglobal_p.h>
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
55 This element emits logical particles into the ParticleSystem, with the
56 starting positions based on those of other logical particles.
58 QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
59 QQuickParticleEmitter(parent)
60 , m_particlesPerParticlePerSecond(0)
62 , m_emitterXVariation(0)
63 , m_emitterYVariation(0)
65 , m_emissionExtruder(0)
66 , m_defaultEmissionExtruder(new QQuickParticleExtruder(this))
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()));
78 \qmlproperty string QtQuick.Particles2::TrailEmitter::follow
80 The type of logical particle which this is emitting from.
83 \qmlproperty qreal QtQuick.Particles2::TrailEmitter::speedFromMovement
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.
93 \qmlproperty Shape QtQuick.Particles2::TrailEmitter::emitShape
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.
99 The default shape is a filled Rectangle.
102 \qmlproperty real QtQuick.Particles2::TrailEmitter::emitWidth
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.
110 \qmlproperty real QtQuick.Particles2::TrailEmitter::emitHeight
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.
118 \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle
121 \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticles(Array particles, QtQuick.Particles2::Particle followed)
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.
125 If you use this signal handler, emitParticles will not be emitted.
129 bool QQuickTrailEmitter::isEmitFollowConnected()
131 IS_SIGNAL_CONNECTED(this, QQuickTrailEmitter, emitFollowParticles, (QQmlV8Handle,QQmlV8Handle));
134 void QQuickTrailEmitter::recalcParticlesPerSecond(){
137 m_followCount = m_system->groupData[m_system->groupIds[m_follow]]->size();
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)
141 setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount);
142 m_lastEmission.resize(m_followCount);
143 m_lastEmission.fill(m_lastTimeStamp);
147 void QQuickTrailEmitter::reset()
152 void QQuickTrailEmitter::emitWindow(int timeStamp)
156 if (!m_enabled && !m_pulseLeft && m_burstQueue.isEmpty())
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
166 m_pulseLeft -= timeStamp - m_lastTimeStamp * 1000.;
167 if (m_pulseLeft < 0){
168 timeStamp += m_pulseLeft;
173 //TODO: Implement startTime and speedFromMovement
174 qreal time = timeStamp / 1000.;
175 qreal particleRatio = 1. / m_particlesPerParticlePerSecond;
177 qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
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;
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
191 pt = m_lastEmission[i];
194 if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
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
202 QList<QQuickParticleData*> toEmit;
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?
209 // Particle timestamp
213 + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
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,
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();
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;
239 // Particle acceleration
240 const QPointF &accel = m_acceleration->sample(newPos);
241 datum->ax = accel.x();
242 datum->ay = accel.y();
245 float sizeVariation = -m_particleSizeVariation
246 + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
248 float size = qMax((qreal)0.0, m_particleSize + sizeVariation);
249 float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation);
251 datum->size = size * float(m_enabled);
252 datum->endSize = endSize * float(m_enabled);
256 m_system->emitParticle(datum);
258 if (!m_burstQueue.isEmpty()){
259 m_burstQueue.first().first--;
260 if (m_burstQueue.first().first <= 0)
261 m_burstQueue.pop_front();
267 foreach (QQuickParticleData* d, toEmit)
268 m_system->emitParticle(d);
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());
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
282 m_lastEmission[d->index] = pt;
285 m_lastTimeStamp = time;