Doc: Fix external references
[profile/ivi/qtdeclarative.git] / src / particles / qquickparticleaffector.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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     \qmltype Affector
49     \instantiates QQuickParticleAffector
50     \inqmlmodule QtQuick.Particles 2
51     \brief Applies alterations to the attributes of logical particles at any
52     point in their lifetime
53     \ingroup qtquick-particles
54
55     The base Affector does not alter any attributes, but can be used to emit a signal
56     when a particle meets certain conditions.
57
58     If an affector has a defined size, then it will only affect particles within its size and position on screen.
59
60     Affectors have different performance characteristics to the other particle system elements. In particular,
61     they have some simplifications to try to maintain a simulation at real-time or faster. When running a system
62     with Affectors, irregular frame timings that grow too large ( > one second per frame) will cause the Affectors
63     to try and cut corners with a faster but less accurate simulation. If the system has multiple affectors the order
64     in which they are applied is not guaranteed, and when simulating larger time shifts they will simulate the whole
65     shift each, which can lead to different results compared to smaller time shifts.
66
67     Accurate simulation for large numbers of particles (hundreds) with multiple affectors may be possible on some hardware,
68     but on less capable hardware you should expect small irregularties in the simulation as simulates with worse granularity.
69 */
70 /*!
71     \qmlproperty ParticleSystem QtQuick.Particles2::Affector::system
72     This is the system which will be affected by the element.
73     If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it.
74 */
75 /*!
76     \qmlproperty list<string> QtQuick.Particles2::Affector::groups
77     Which logical particle groups will be affected.
78
79     If empty, it will affect all particles.
80 */
81 /*!
82     \qmlproperty list<string> QtQuick.Particles2::Affector::whenCollidingWith
83     If any logical particle groups are specified here, then the affector
84     will only be triggered if the particle being examined intersects with
85     a particle of one of these groups.
86
87     This is different from the groups property. The groups property selects which
88     particles might be examined, and if they meet other criteria (including being
89     within the bounds of the Affector, modified by shape) then they will be tested
90     again to see if they intersect with a particles from one of the particle groups
91     in whenCollidingWith.
92
93     By default, no groups are specified.
94 */
95 /*!
96     \qmlproperty bool QtQuick.Particles2::Affector::enabled
97     If enabled is set to false, this affector will not affect any particles.
98
99     Usually this is used to conditionally turn an affector on or off.
100
101     Default value is true.
102 */
103 /*!
104     \qmlproperty bool QtQuick.Particles2::Affector::once
105     If once is set to true, this affector will only affect each particle
106     once in their lifetimes. If the affector normally simulates a continuous
107     effect over time, then it will simulate the effect of one second of time
108     the one instant it affects the particle.
109
110     Default value is false.
111 */
112 /*!
113     \qmlproperty Shape QtQuick.Particles2::Affector::shape
114     If a size has been defined, the shape property can be used to affect a
115     non-rectangular area.
116 */
117 /*!
118     \qmlsignal QtQuick.Particles2::Affector::onAffected(real x, real y)
119
120     This handler is called when a particle is selected to be affected. It will not be called
121     if a particle is considered by the Affector but not actually altered in any way.
122
123     In the special case where an Affector has no possible effect (e.g. Affector {}), affected
124     will be emitted for all particles being considered if you connect to it. This allows you to
125     execute arbitrary code in response to particles (use the Affector::onAffectParticles
126     signal handler if you want to execute code which affects the particles
127     themselves). As this executes JavaScript code per particle, it is not recommended to use this
128     signal with a high-volume particle system.
129
130     x,y is the particle's current position.
131 */
132 QQuickParticleAffector::QQuickParticleAffector(QQuickItem *parent) :
133     QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_onceOff(false), m_enabled(true)
134     , m_system(0), m_updateIntSet(false), m_shape(new QQuickParticleExtruder(this))
135 {
136 }
137
138 bool QQuickParticleAffector::isAffectedConnected()
139 {
140     IS_SIGNAL_CONNECTED(this, QQuickParticleAffector, affected, (qreal,qreal));
141 }
142
143
144 void QQuickParticleAffector::componentComplete()
145 {
146     if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
147         setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
148     QQuickItem::componentComplete();
149 }
150
151 bool QQuickParticleAffector::activeGroup(int g) {
152     if (m_updateIntSet){ //This can occur before group ids are properly assigned, but that resets the flag
153         m_groupIds.clear();
154         foreach (const QString &p, m_groups)
155             m_groupIds << m_system->groupIds[p];
156         m_updateIntSet = false;
157     }
158     return m_groupIds.isEmpty() || m_groupIds.contains(g);
159 }
160
161 bool QQuickParticleAffector::shouldAffect(QQuickParticleData* d)
162 {
163     if (!d)
164         return false;
165     if (activeGroup(d->group)){
166         if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))
167                 || !d->stillAlive())
168             return false;
169         //Need to have previous location for affected anyways
170         if (width() == 0 || height() == 0
171                 || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()), QPointF(d->curX(), d->curY()))){
172             if (m_whenCollidingWith.isEmpty() || isColliding(d)){
173                 return true;
174             }
175         }
176     }
177     return false;
178
179 }
180
181 void QQuickParticleAffector::postAffect(QQuickParticleData* d)
182 {
183     m_system->needsReset << d;
184     if (m_onceOff)
185         m_onceOffed << qMakePair(d->group, d->index);
186     if (isAffectedConnected())
187         emit affected(d->curX(), d->curY());
188 }
189
190 const qreal QQuickParticleAffector::simulationDelta = 0.020;
191 const qreal QQuickParticleAffector::simulationCutoff = 1.000;//If this goes above 1.0, then m_once behaviour needs special codepath
192
193 void QQuickParticleAffector::affectSystem(qreal dt)
194 {
195     if (!m_enabled)
196         return;
197     //If not reimplemented, calls affectParticle per particle
198     //But only on particles in targeted system/area
199     updateOffsets();//### Needed if an ancestor is transformed.
200     if (m_onceOff)
201         dt = 1.0;
202     foreach (QQuickParticleGroupData* gd, m_system->groupData) {
203         if (activeGroup(m_system->groupData.key(gd))) {
204             foreach (QQuickParticleData* d, gd->data) {
205                 if (shouldAffect(d)) {
206                     bool affected = false;
207                     qreal myDt = dt;
208                     if (!m_ignoresTime && myDt < simulationCutoff) {
209                         int realTime = m_system->timeInt;
210                         m_system->timeInt -= myDt * 1000.0;
211                         while (myDt > simulationDelta) {
212                             m_system->timeInt += simulationDelta * 1000.0;
213                             if (d->alive())//Only affect during the parts it was alive for
214                                 affected = affectParticle(d, simulationDelta) || affected;
215                             myDt -= simulationDelta;
216                         }
217                         m_system->timeInt = realTime;
218                     }
219                     if (myDt > 0.0)
220                         affected = affectParticle(d, myDt) || affected;
221                     if (affected)
222                         postAffect(d);
223                 }
224             }
225         }
226     }
227 }
228
229 bool QQuickParticleAffector::affectParticle(QQuickParticleData *, qreal )
230 {
231     return true;
232 }
233
234 void QQuickParticleAffector::reset(QQuickParticleData* pd)
235 {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
236     if (m_onceOff)
237         if (activeGroup(pd->group))
238             m_onceOffed.remove(qMakePair(pd->group, pd->index));
239 }
240
241 void QQuickParticleAffector::updateOffsets()
242 {
243     if (m_system)
244         m_offset = m_system->mapFromItem(this, QPointF(0, 0));
245 }
246
247 bool QQuickParticleAffector::isColliding(QQuickParticleData *d)
248 {
249     qreal myCurX = d->curX();
250     qreal myCurY = d->curY();
251     qreal myCurSize = d->curSize()/2;
252     foreach (const QString &group, m_whenCollidingWith){
253         foreach (QQuickParticleData* other, m_system->groupData[m_system->groupIds[group]]->data){
254             if (!other->stillAlive())
255                 continue;
256             qreal otherCurX = other->curX();
257             qreal otherCurY = other->curY();
258             qreal otherCurSize = other->curSize()/2;
259             if ((myCurX + myCurSize > otherCurX - otherCurSize
260                  && myCurX - myCurSize < otherCurX + otherCurSize)
261                  && (myCurY + myCurSize > otherCurY - otherCurSize
262                      && myCurY - myCurSize < otherCurY + otherCurSize))
263                 return true;
264         }
265     }
266     return false;
267 }
268
269 QT_END_NAMESPACE