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 "qquickcustomaffector_p.h" |
5 | |
6 | #include <private/qquickv4particledata_p.h> |
7 | #include <private/qqmlglobal_p.h> |
8 | |
9 | #include <QtCore/qdebug.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \qmltype Affector |
15 | \nativetype QQuickCustomAffector |
16 | \inqmlmodule QtQuick.Particles |
17 | \brief Applies alterations to the attributes of logical particles at any |
18 | point in their lifetime. |
19 | \inherits ParticleAffector |
20 | \ingroup qtquick-particles |
21 | |
22 | Custom Affector manipulates the properties of the particles directly in |
23 | JavaScript. |
24 | */ |
25 | |
26 | /*! |
27 | \qmlsignal QtQuick.Particles::Affector::affectParticles(Array particles, real dt) |
28 | |
29 | This signal is emitted when particles are selected to be affected. |
30 | \a particles is an array of particle objects which can be directly |
31 | manipulated. |
32 | |
33 | \a dt is the time since the last time it was affected. Use \a dt to |
34 | normalize trajectory manipulations to real time. |
35 | |
36 | \note JavaScript is slower to execute, so it is not recommended to use |
37 | this in high-volume particle systems. |
38 | */ |
39 | |
40 | /*! |
41 | \qmlproperty StochasticDirection QtQuick.Particles::Affector::position |
42 | |
43 | Affected particles will have their position set to this direction, |
44 | relative to the ParticleSystem. When interpreting directions as points, |
45 | imagine it as an arrow with the base at the 0,0 of the ParticleSystem and the |
46 | tip at where the specified position will be. |
47 | */ |
48 | |
49 | /*! |
50 | \qmlproperty StochasticDirection QtQuick.Particles::Affector::velocity |
51 | |
52 | Affected particles will have their velocity set to this direction. |
53 | */ |
54 | |
55 | |
56 | /*! |
57 | \qmlproperty StochasticDirection QtQuick.Particles::Affector::acceleration |
58 | |
59 | Affected particles will have their acceleration set to this direction. |
60 | */ |
61 | |
62 | |
63 | /*! |
64 | \qmlproperty bool QtQuick.Particles::Affector::relative |
65 | |
66 | Whether the affected particles have their existing position/velocity/acceleration added |
67 | to the new one. |
68 | |
69 | Default is true. |
70 | */ |
71 | QQuickCustomAffector::QQuickCustomAffector(QQuickItem *parent) : |
72 | QQuickParticleAffector(parent) |
73 | , m_position(&m_nullVector) |
74 | , m_velocity(&m_nullVector) |
75 | , m_acceleration(&m_nullVector) |
76 | , m_relative(true) |
77 | { |
78 | } |
79 | |
80 | bool QQuickCustomAffector::isAffectConnected() |
81 | { |
82 | IS_SIGNAL_CONNECTED( |
83 | this, QQuickCustomAffector, affectParticles, |
84 | (const QList<QQuickV4ParticleData> &, qreal)); |
85 | } |
86 | |
87 | void QQuickCustomAffector::affectSystem(qreal dt) |
88 | { |
89 | //Acts a bit differently, just emits affected for everyone it might affect, when the only thing is connecting to affected(x,y) |
90 | bool justAffected = (m_acceleration == &m_nullVector |
91 | && m_velocity == &m_nullVector |
92 | && m_position == &m_nullVector |
93 | && isAffectedConnected()); |
94 | if (!isAffectConnected() && !justAffected) { |
95 | QQuickParticleAffector::affectSystem(dt); |
96 | return; |
97 | } |
98 | if (!m_enabled) |
99 | return; |
100 | updateOffsets(); |
101 | |
102 | QList<QQuickParticleData*> toAffect; |
103 | for (const QQuickParticleGroupData *gd : std::as_const(t&: m_system->groupData)) { |
104 | if (activeGroup(g: gd->index)) { |
105 | for (QQuickParticleData *d : gd->data) { |
106 | if (shouldAffect(datum: d)) { |
107 | toAffect << d; |
108 | } |
109 | } |
110 | } |
111 | } |
112 | |
113 | if (toAffect.isEmpty()) |
114 | return; |
115 | |
116 | if (justAffected) { |
117 | for (const QQuickParticleData *d : std::as_const(t&: toAffect)) {//Not postAffect to avoid saying the particle changed |
118 | if (m_onceOff) |
119 | m_onceOffed << qMakePair(value1: d->groupId, value2: d->index); |
120 | emit affected(x: d->curX(particleSystem: m_system), y: d->curY(particleSystem: m_system)); |
121 | } |
122 | return; |
123 | } |
124 | |
125 | if (m_onceOff) |
126 | dt = 1.0; |
127 | |
128 | QList<QQuickV4ParticleData> particles; |
129 | particles.reserve(asize: toAffect.size()); |
130 | for (QQuickParticleData *data: std::as_const(t&: toAffect)) |
131 | particles.push_back(t: data->v4Value(particleSystem: m_system)); |
132 | |
133 | const auto doAffect = [&](qreal dt) { |
134 | affectProperties(particles: toAffect, dt); |
135 | emit affectParticles(particles, dt); |
136 | }; |
137 | |
138 | if (dt >= simulationCutoff || dt <= simulationDelta) { |
139 | doAffect(dt); |
140 | } else { |
141 | int realTime = m_system->timeInt; |
142 | m_system->timeInt -= dt * 1000.0; |
143 | while (dt > simulationDelta) { |
144 | m_system->timeInt += simulationDelta * 1000.0; |
145 | dt -= simulationDelta; |
146 | doAffect(simulationDelta); |
147 | } |
148 | m_system->timeInt = realTime; |
149 | if (dt > 0.0) |
150 | doAffect(dt); |
151 | } |
152 | |
153 | for (QQuickParticleData *d : std::as_const(t&: toAffect)) |
154 | if (d->update == 1.0) |
155 | postAffect(datum: d); |
156 | } |
157 | |
158 | bool QQuickCustomAffector::affectParticle(QQuickParticleData *d, qreal dt) |
159 | { |
160 | //This does the property based affecting, called by superclass if signal isn't hooked up. |
161 | bool changed = false; |
162 | QPointF curPos(d->curX(particleSystem: m_system), d->curY(particleSystem: m_system)); |
163 | |
164 | if (m_acceleration != &m_nullVector){ |
165 | QPointF pos = m_acceleration->sample(from: curPos); |
166 | QPointF curAcc = QPointF(d->curAX(), d->curAY()); |
167 | if (m_relative) { |
168 | pos *= dt; |
169 | pos += curAcc; |
170 | } |
171 | if (pos != curAcc) { |
172 | d->setInstantaneousAX(ax: pos.x(), particleSystem: m_system); |
173 | d->setInstantaneousAY(ay: pos.y(), particleSystem: m_system); |
174 | changed = true; |
175 | } |
176 | } |
177 | |
178 | if (m_velocity != &m_nullVector){ |
179 | QPointF pos = m_velocity->sample(from: curPos); |
180 | QPointF curVel = QPointF(d->curVX(particleSystem: m_system), d->curVY(particleSystem: m_system)); |
181 | if (m_relative) { |
182 | pos *= dt; |
183 | pos += curVel; |
184 | } |
185 | if (pos != curVel) { |
186 | d->setInstantaneousVX(vx: pos.x(), particleSystem: m_system); |
187 | d->setInstantaneousVY(vy: pos.y(), particleSystem: m_system); |
188 | changed = true; |
189 | } |
190 | } |
191 | |
192 | if (m_position != &m_nullVector){ |
193 | QPointF pos = m_position->sample(from: curPos); |
194 | if (m_relative) { |
195 | pos *= dt; |
196 | pos += curPos; |
197 | } |
198 | if (pos != curPos) { |
199 | d->setInstantaneousX(x: pos.x(), particleSystem: m_system); |
200 | d->setInstantaneousY(y: pos.y(), particleSystem: m_system); |
201 | changed = true; |
202 | } |
203 | } |
204 | |
205 | return changed; |
206 | } |
207 | |
208 | void QQuickCustomAffector::affectProperties(const QList<QQuickParticleData*> &particles, qreal dt) |
209 | { |
210 | for (QQuickParticleData *d : particles) |
211 | if ( affectParticle(d, dt) ) |
212 | d->update = 1.0; |
213 | } |
214 | |
215 | QT_END_NAMESPACE |
216 | |
217 | #include "moc_qquickcustomaffector_p.cpp" |
218 | |