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

source code of qtdeclarative/src/particles/qquickcustomaffector.cpp