Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickparticleaffector.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtCore/qtconfigmacros.h>
5#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
6
8#include <QDebug>
9#include <private/qqmlglobal_p.h>
11
12/*!
13 \qmltype ParticleAffector
14//! \nativetype QQuickParticleAffector
15 \inqmlmodule QtQuick.Particles
16 \brief Applies alterations to the attributes of logical particles at any
17 point in their lifetime.
18 \ingroup qtquick-particles
19
20 The base ParticleAffector does not alter any attributes, but can be used to emit a signal
21 when a particle meets certain conditions.
22
23 If an affector has a defined size, then it will only affect particles within its size and
24position on screen.
25
26 Affectors have different performance characteristics to the other particle system elements. In
27particular, they have some simplifications to try to maintain a simulation at real-time or faster.
28When running a system with Affectors, irregular frame timings that grow too large ( > one second per
29frame) will cause the Affectors to try and cut corners with a faster but less accurate simulation.
30If the system has multiple affectors the order in which they are applied is not guaranteed, and when
31simulating larger time shifts they will simulate the whole shift each, which can lead to different
32results compared to smaller time shifts.
33
34 Accurate simulation for large numbers of particles (hundreds) with multiple affectors may be
35possible on some hardware, but on less capable hardware you should expect small irregularties in the
36simulation as simulates with worse granularity.
37*/
38/*!
39 \qmlproperty ParticleSystem QtQuick.Particles::ParticleAffector::system
40 This is the system which will be affected by the element.
41 If the ParticleAffector is a direct child of a ParticleSystem, it will automatically be associated with
42 it.
43*/
44/*!
45 \qmlproperty list<string> QtQuick.Particles::ParticleAffector::groups
46 Which logical particle groups will be affected.
47
48 If empty, it will affect all particles.
49*/
50/*!
51 \qmlproperty list<string> QtQuick.Particles::ParticleAffector::whenCollidingWith
52 If any logical particle groups are specified here, then the particle affector
53 will only be triggered if the particle being examined intersects with
54 a particle of one of these groups.
55
56 This is different from the groups property. The groups property selects which
57 particles might be examined, and if they meet other criteria (including being
58 within the bounds of the ParticleAffector, modified by shape) then they will be tested
59 again to see if they intersect with a particles from one of the particle groups
60 in whenCollidingWith.
61
62 By default, no groups are specified.
63*/
64/*!
65 \qmlproperty bool QtQuick.Particles::ParticleAffector::enabled
66 If enabled is set to false, this affector will not affect any particles.
67
68 Usually this is used to conditionally turn an affector on or off.
69
70 Default value is true.
71*/
72/*!
73 \qmlproperty bool QtQuick.Particles::ParticleAffector::once
74 If once is set to true, this affector will only affect each particle
75 once in their lifetimes. If the affector normally simulates a continuous
76 effect over time, then it will simulate the effect of one second of time
77 the one instant it affects the particle.
78
79 Default value is false.
80*/
81/*!
82 \qmlproperty Shape QtQuick.Particles::ParticleAffector::shape
83 If a size has been defined, the shape property can be used to affect a
84 non-rectangular area.
85*/
86/*!
87 \qmlsignal QtQuick.Particles::ParticleAffector::affected(real x, real y)
88
89 This signal is emitted when a particle is selected to be affected. It will not be emitted
90 if a particle is considered by the ParticleAffector but not actually altered in any way.
91
92 In the special case where an ParticleAffector has no possible effect (e.g. ParticleAffector {}), this signal
93 will be emitted for all particles being considered if you connect to it. This allows you to
94 execute arbitrary code in response to particles (use the ParticleAffector::onAffectParticles
95 signal handler if you want to execute code which affects the particles
96 themselves). As this executes JavaScript code per particle, it is not recommended to use this
97 signal with a high-volume particle system.
98
99 (\a {x}, \a {y}) is the particle's current position.
100*/
101QQuickParticleAffector::QQuickParticleAffector(QQuickItem *parent) :
102 QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_onceOff(false), m_enabled(true)
103 , m_system(nullptr), m_updateIntSet(false), m_shape(new QQuickParticleExtruder(this))
104{
105}
106
107bool QQuickParticleAffector::isAffectedConnected()
108{
109 IS_SIGNAL_CONNECTED(this, QQuickParticleAffector, affected, (qreal,qreal));
110}
111
112
113void QQuickParticleAffector::componentComplete()
114{
115 if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
116 setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
117 QQuickItem::componentComplete();
118}
119
120bool QQuickParticleAffector::activeGroup(int g) {
121 if (!m_system)
122 return false;
123
124 if (m_updateIntSet){ //This can occur before group ids are properly assigned, but that resets the flag
125 m_groupIds.clear();
126 foreach (const QString &p, m_groups)
127 m_groupIds << m_system->groupIds[p];
128 m_updateIntSet = false;
129 }
130 return m_groupIds.isEmpty() || m_groupIds.contains(g);
131}
132
133bool QQuickParticleAffector::shouldAffect(QQuickParticleData* d)
134{
135 if (!d)
136 return false;
137 if (!m_system)
138 return false;
139
140 if (activeGroup(d->groupId)){
141 if ((m_onceOff && m_onceOffed.contains(std::make_pair(d->groupId, d->index)))
142 || !d->stillAlive(m_system))
143 return false;
144 //Need to have previous location for affected anyways
145 if (width() == 0 || height() == 0
146 || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()), QPointF(d->curX(m_system), d->curY(m_system)))){
147 if (m_whenCollidingWith.isEmpty() || isColliding(d)){
148 return true;
149 }
150 }
151 }
152 return false;
153
154}
155
156void QQuickParticleAffector::postAffect(QQuickParticleData* d)
157{
158 if (!m_system)
159 return;
160
161 m_system->needsReset << d;
162 if (m_onceOff)
163 m_onceOffed << std::make_pair(d->groupId, d->index);
164 if (isAffectedConnected())
165 emit affected(d->curX(m_system), d->curY(m_system));
166}
167
168const qreal QQuickParticleAffector::simulationDelta = 0.020;
169const qreal QQuickParticleAffector::simulationCutoff = 1.000;//If this goes above 1.0, then m_once behaviour needs special codepath
170
171void QQuickParticleAffector::affectSystem(qreal dt)
172{
173 if (!m_enabled)
174 return;
175 if (!m_system)
176 return;
177
178 //If not reimplemented, calls affectParticle per particle
179 //But only on particles in targeted system/area
180 updateOffsets();//### Needed if an ancestor is transformed.
181 if (m_onceOff)
182 dt = 1.0;
183 for (QQuickParticleGroupData* gd : std::as_const(m_system->groupData)) {
184 if (activeGroup(gd->index)) {
185 for (QQuickParticleData* d : std::as_const(gd->data)) {
186 if (shouldAffect(d)) {
187 bool affected = false;
188 qreal myDt = dt;
189 if (!m_ignoresTime && myDt < simulationCutoff) {
190 int realTime = m_system->timeInt;
191 m_system->timeInt -= myDt * 1000.0;
192 while (myDt > simulationDelta) {
193 m_system->timeInt += simulationDelta * 1000.0;
194 if (d->alive(m_system))//Only affect during the parts it was alive for
195 affected = affectParticle(d, simulationDelta) || affected;
196 myDt -= simulationDelta;
197 }
198 m_system->timeInt = realTime;
199 }
200 if (myDt > 0.0)
201 affected = affectParticle(d, myDt) || affected;
202 if (affected)
203 postAffect(d);
204 }
205 }
206 }
207 }
208}
209
210bool QQuickParticleAffector::affectParticle(QQuickParticleData *, qreal )
211{
212 return true;
213}
214
215void QQuickParticleAffector::reset(QQuickParticleData* pd)
216{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
217 if (m_onceOff)
218 if (activeGroup(pd->groupId))
219 m_onceOffed.remove(std::make_pair(pd->groupId, pd->index));
220}
221
222void QQuickParticleAffector::updateOffsets()
223{
224 if (m_system)
225 m_offset = m_system->mapFromItem(this, QPointF(0, 0));
226}
227
228bool QQuickParticleAffector::isColliding(QQuickParticleData *d) const
229{
230 if (!m_system)
231 return false;
232
233 qreal myCurX = d->curX(m_system);
234 qreal myCurY = d->curY(m_system);
235 qreal myCurSize = d->curSize(m_system) / 2;
236 foreach (const QString &group, m_whenCollidingWith){
237 foreach (QQuickParticleData* other, m_system->groupData[m_system->groupIds[group]]->data){
238 if (!other->stillAlive(m_system))
239 continue;
240 qreal otherCurX = other->curX(m_system);
241 qreal otherCurY = other->curY(m_system);
242 qreal otherCurSize = other->curSize(m_system) / 2;
243 if ((myCurX + myCurSize > otherCurX - otherCurSize
244 && myCurX - myCurSize < otherCurX + otherCurSize)
245 && (myCurY + myCurSize > otherCurY - otherCurSize
246 && myCurY - myCurSize < otherCurY + otherCurSize))
247 return true;
248 }
249 }
250 return false;
251}
252
253QT_END_NAMESPACE
254
255#include "moc_qquickparticleaffector_p.cpp"
Combined button and popup list for selecting options.