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