0413f1d517c419fb321e6b5857c8e268936ca77f
[profile/ivi/qtdeclarative.git] / src / quick / particles / qquickparticleaffector.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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 "qquickparticleaffector_p.h"
43 #include <QDebug>
44 QT_BEGIN_NAMESPACE
45
46 /*!
47     \qmlclass Affector QQuickParticleAffector
48     \inqmlmodule QtQuick.Particles 2
49     \brief Affector elements can alter the attributes of logical particles at any point in their lifetime.
50
51     The base Affector does not alter any attributes, but can be used to emit a signal
52     when a particle meets certain conditions.
53
54     If an affector has a defined size, then it will only affect particles within its size and position on screen.
55
56     Affectors have different performance characteristics to the other particle system elements. In particular,
57     they have some simplifications to try to maintain a simulation at real-time or faster. When running a system
58     with Affectors, irregular frame timings that grow too large ( > one second per frame) will cause the Affectors
59     to try and cut corners with a faster but less accurate simulation. If the system has multiple affectors the order
60     in which they are applied is not guaranteed, and when simulating larger time shifts they will simulate the whole
61     shift each, which can lead to different results compared to smaller time shifts.
62
63     Accurate simulation for large numbers of particles (hundreds) with multiple affectors may be possible on some hardware,
64     but on less capable hardware you should expect small irregularties in the simulation as simulates with worse granularity.
65 */
66 /*!
67     \qmlproperty ParticleSystem QtQuick.Particles2::Affector::system
68     This is the system which will be affected by the element.
69     If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it.
70 */
71 /*!
72     \qmlproperty list<string> QtQuick.Particles2::Affector::groups
73     Which logical particle groups will be affected.
74
75     If empty, it will affect all particles.
76 */
77 /*!
78     \qmlproperty list<string> QtQuick.Particles2::Affector::whenCollidingWith
79     If any logical particle groups are specified here, then the affector
80     will only be triggered if the particle being examined intersects with
81     a particle of one of these groups.
82
83     This is different from the groups property. The groups property selects which
84     particles might be examined, and if they meet other criteria (including being
85     within the bounds of the Affector, modified by shape) then they will be tested
86     again to see if they intersect with a particles from one of the particle groups
87     in whenCollidingWith.
88
89     By default, no groups are specified.
90 */
91 /*!
92     \qmlproperty bool QtQuick.Particles2::Affector::enabled
93     If enabled is set to false, this affector will not affect any particles.
94
95     Usually this is used to conditionally turn an affector on or off.
96
97     Default value is true.
98 */
99 /*!
100     \qmlproperty bool QtQuick.Particles2::Affector::once
101     If once is set to true, this affector will only affect each particle
102     once in their lifetimes. If the affector normally simulates a continuous
103     effect over time, then it will simulate the effect of one second of time
104     the one instant it affects the particle.
105
106     Default value is false.
107 */
108 /*!
109     \qmlproperty Shape QtQuick.Particles2::Affector::shape
110     If a size has been defined, the shape property can be used to affect a
111     non-rectangular area.
112 */
113 /*!
114     \qmlsignal QtQuick.Particles2::Affector::onAffected(x, y)
115
116     This signal is emitted each time the affector actually affects a particle.
117
118     x,y are the coordinates of the affected particle, relative to the ParticleSystem.
119
120 */
121
122 /*!
123     \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle particle, real dt)
124
125     This handler is called when particles are selected to be affected.
126
127     dt is the time since the last time it was affected. Use dt to normalize
128     trajectory manipulations to real time.
129
130     Note that JS is slower to execute, so it is not recommended to use this in
131     high-volume particle systems.
132 */
133 /*!
134     \qmlsignal QtQuick.Particles2::Affector::affected(real x, real y)
135
136     This handler is called when a particle is selected to be affected. It will
137     only be called if signal is set to true.
138
139     x,y is the particles current position.
140 */
141 QQuickParticleAffector::QQuickParticleAffector(QQuickItem *parent) :
142     QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_onceOff(false), m_enabled(true)
143     , m_system(0), m_updateIntSet(false), m_shape(new QQuickParticleExtruder(this))
144 {
145 }
146
147 bool QQuickParticleAffector::isAffectedConnected()
148 {
149     static int idx = QObjectPrivate::get(this)->signalIndex("affected(qreal,qreal)");
150     return QObjectPrivate::get(this)->isSignalConnected(idx);
151 }
152
153
154 void QQuickParticleAffector::componentComplete()
155 {
156     if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
157         setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
158     QQuickItem::componentComplete();
159 }
160
161 bool QQuickParticleAffector::activeGroup(int g) {
162     if (m_updateIntSet){ //This can occur before group ids are properly assigned, but that resets the flag
163         m_groupIds.clear();
164         foreach (const QString &p, m_groups)
165             m_groupIds << m_system->groupIds[p];
166         m_updateIntSet = false;
167     }
168     return m_groupIds.isEmpty() || m_groupIds.contains(g);
169 }
170
171 bool QQuickParticleAffector::shouldAffect(QQuickParticleData* d)
172 {
173     if (!d)
174         return false;
175     if (activeGroup(d->group)){
176         if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))
177                 || !d->stillAlive())
178             return false;
179         //Need to have previous location for affected anyways
180         if (width() == 0 || height() == 0
181                 || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()), QPointF(d->curX(), d->curY()))){
182             if (m_whenCollidingWith.isEmpty() || isColliding(d)){
183                 return true;
184             }
185         }
186     }
187     return false;
188
189 }
190
191 void QQuickParticleAffector::postAffect(QQuickParticleData* d)
192 {
193     m_system->needsReset << d;
194     if (m_onceOff)
195         m_onceOffed << qMakePair(d->group, d->index);
196     if (isAffectedConnected())
197         emit affected(d->curX(), d->curY());
198 }
199
200 const qreal QQuickParticleAffector::simulationDelta = 0.020;
201 const qreal QQuickParticleAffector::simulationCutoff = 1.000;//If this goes above 1.0, then m_once behaviour needs special codepath
202
203 void QQuickParticleAffector::affectSystem(qreal dt)
204 {
205     if (!m_enabled)
206         return;
207     //If not reimplemented, calls affectParticle per particle
208     //But only on particles in targeted system/area
209     updateOffsets();//### Needed if an ancestor is transformed.
210     if (m_onceOff)
211         dt = 1.0;
212     foreach (QQuickParticleGroupData* gd, m_system->groupData) {
213         if (activeGroup(m_system->groupData.key(gd))) {
214             foreach (QQuickParticleData* d, gd->data) {
215                 if (shouldAffect(d)) {
216                     bool affected = false;
217                     qreal myDt = dt;
218                     if (!m_ignoresTime && myDt < simulationCutoff) {
219                         int realTime = m_system->timeInt;
220                         m_system->timeInt -= myDt * 1000.0;
221                         while (myDt > simulationDelta) {
222                             m_system->timeInt += simulationDelta * 1000.0;
223                             if (d->alive())//Only affect during the parts it was alive for
224                                 affected = affectParticle(d, simulationDelta) || affected;
225                             myDt -= simulationDelta;
226                         }
227                         m_system->timeInt = realTime;
228                     }
229                     if (myDt > 0.0)
230                         affected = affectParticle(d, myDt) || affected;
231                     if (affected)
232                         postAffect(d);
233                 }
234             }
235         }
236     }
237 }
238
239 bool QQuickParticleAffector::affectParticle(QQuickParticleData *, qreal )
240 {
241     return true;
242 }
243
244 void QQuickParticleAffector::reset(QQuickParticleData* pd)
245 {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
246     if (m_onceOff)
247         if (activeGroup(pd->group))
248             m_onceOffed.remove(qMakePair(pd->group, pd->index));
249 }
250
251 void QQuickParticleAffector::updateOffsets()
252 {
253     if (m_system)
254         m_offset = m_system->mapFromItem(this, QPointF(0, 0));
255 }
256
257 bool QQuickParticleAffector::isColliding(QQuickParticleData *d)
258 {
259     qreal myCurX = d->curX();
260     qreal myCurY = d->curY();
261     qreal myCurSize = d->curSize()/2;
262     foreach (const QString &group, m_whenCollidingWith){
263         foreach (QQuickParticleData* other, m_system->groupData[m_system->groupIds[group]]->data){
264             if (!other->stillAlive())
265                 continue;
266             qreal otherCurX = other->curX();
267             qreal otherCurY = other->curY();
268             qreal otherCurSize = other->curSize()/2;
269             if ((myCurX + myCurSize > otherCurX - otherCurSize
270                  && myCurX - myCurSize < otherCurX + otherCurSize)
271                  && (myCurY + myCurSize > otherCurY - otherCurSize
272                      && myCurY - myCurSize < otherCurY + otherCurSize))
273                 return true;
274         }
275     }
276     return false;
277 }
278
279 QT_END_NAMESPACE