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 "qquickturbulence_p.h"
5#include "qquickparticlepainter_p.h"//TODO: Why was this needed again?
6#include <cmath>
7#include <cstdlib>
8#include <QDebug>
9#include <QQmlFile>
10QT_BEGIN_NAMESPACE
11
12/*!
13 \qmltype Turbulence
14 \instantiates QQuickTurbulenceAffector
15 \inqmlmodule QtQuick.Particles
16 \ingroup qtquick-particles
17 \inherits Affector
18 \brief Provides fluid-like forces from a noise image.
19
20 The Turbulence Element scales the noise source over the area it affects,
21 and uses the curl of that source to generate force vectors.
22
23 Turbulence requires a fixed size. Unlike other affectors, a 0x0 Turbulence element
24 will affect no particles.
25
26 The source should be relatively smooth black and white noise, such as perlin noise.
27*/
28/*!
29 \qmlproperty real QtQuick.Particles::Turbulence::strength
30
31 The magnitude of the velocity vector at any point varies between zero and
32 the square root of two. It will then be multiplied by strength to get the
33 velocity per second for the particles affected by the turbulence.
34*/
35/*!
36 \qmlproperty url QtQuick.Particles::Turbulence::noiseSource
37
38 The source image to generate the turbulence from. It will be scaled to the size of the element,
39 so equal or larger sizes will give better results. Tweaking this image is the only way to tweak
40 behavior such as where vortices are or how many exist.
41
42 The source should be a relatively smooth black and white noise image, such as perlin noise.
43 A default image will be used if none is provided.
44*/
45
46QQuickTurbulenceAffector::QQuickTurbulenceAffector(QQuickItem *parent) :
47 QQuickParticleAffector(parent),
48 m_strength(10), m_gridSize(0), m_field(nullptr), m_vectorField(nullptr), m_inited(false)
49{
50}
51
52void QQuickTurbulenceAffector::geometryChange(const QRectF &, const QRectF &)
53{
54 initializeGrid();
55}
56
57QQuickTurbulenceAffector::~QQuickTurbulenceAffector()
58{
59 if (m_field) {
60 for (int i=0; i<m_gridSize; i++)
61 free(ptr: m_field[i]);
62 free(ptr: m_field);
63 }
64 if (m_vectorField) {
65 for (int i=0; i<m_gridSize; i++)
66 free(ptr: m_vectorField[i]);
67 free(ptr: m_vectorField);
68 }
69}
70
71void QQuickTurbulenceAffector::initializeGrid()
72{
73 if (!m_inited)
74 return;
75
76 int arg = qMax(a: width(), b: height());
77 if (m_gridSize != arg) {
78 if (m_field){ //deallocate and then reallocate grid
79 for (int i=0; i<m_gridSize; i++)
80 free(ptr: m_field[i]);
81 free(ptr: m_field);
82 }
83 if (m_vectorField) {
84 for (int i=0; i<m_gridSize; i++)
85 free(ptr: m_vectorField[i]);
86 free(ptr: m_vectorField);
87 }
88 m_gridSize = arg;
89 }
90
91 m_field = (qreal**)malloc(size: m_gridSize * sizeof(qreal*));
92 for (int i=0; i<m_gridSize; i++)
93 m_field[i] = (qreal*)malloc(size: m_gridSize * sizeof(qreal));
94 m_vectorField = (QPointF**)malloc(size: m_gridSize * sizeof(QPointF*));
95 for (int i=0; i<m_gridSize; i++)
96 m_vectorField[i] = (QPointF*)malloc(size: m_gridSize * sizeof(QPointF));
97
98 QImage image;
99 if (!m_noiseSource.isEmpty())
100 image = QImage(QQmlFile::urlToLocalFileOrQrc(m_noiseSource)).scaled(s: QSize(m_gridSize, m_gridSize));
101 if (image.isNull())
102 image = QImage(QStringLiteral(":particleresources/noise.png")).scaled(s: QSize(m_gridSize, m_gridSize));
103
104 for (int i=0; i<m_gridSize; i++)
105 for (int j=0; j<m_gridSize; j++)
106 m_field[i][j] = qGray(rgb: image.pixel(pt: QPoint(i,j)));
107 for (int i=0; i<m_gridSize; i++){
108 for (int j=0; j<m_gridSize; j++){
109 m_vectorField[i][j].setX(boundsRespectingField(x: i-1,y: j) - boundsRespectingField(x: i,y: j));
110 m_vectorField[i][j].setY(boundsRespectingField(x: i,y: j) - boundsRespectingField(x: i,y: j-1));
111 }
112 }
113}
114
115qreal QQuickTurbulenceAffector::boundsRespectingField(int x, int y)
116{
117 if (x < 0)
118 x = 0;
119 if (x >= m_gridSize)
120 x = m_gridSize - 1;
121 if (y < 0)
122 y = 0;
123 if (y >= m_gridSize)
124 y = m_gridSize - 1;
125 return m_field[x][y];
126}
127
128void QQuickTurbulenceAffector::ensureInit()
129{
130 if (m_inited)
131 return;
132 m_inited = true;
133 initializeGrid();
134}
135
136void QQuickTurbulenceAffector::affectSystem(qreal dt)
137{
138 if (!m_system || !m_enabled)
139 return;
140 ensureInit();
141 if (!m_gridSize)
142 return;
143
144 updateOffsets();//### Needed if an ancestor is transformed.
145
146 QRect boundsRect(0,0,m_gridSize,m_gridSize);
147 for (QQuickParticleGroupData *gd : m_system->groupData) {
148 if (!activeGroup(g: gd->index))
149 continue;
150 foreach (QQuickParticleData *d, gd->data){
151 if (!shouldAffect(datum: d))
152 continue;
153 QPoint pos = (QPointF(d->curX(particleSystem: m_system), d->curY(particleSystem: m_system)) - m_offset).toPoint();
154 if (!boundsRect.contains(p: pos,proper: true))//Need to redo bounds checking due to quantization.
155 continue;
156 qreal fx = 0.0;
157 qreal fy = 0.0;
158 fx += m_vectorField[pos.x()][pos.y()].x() * m_strength;
159 fy += m_vectorField[pos.x()][pos.y()].y() * m_strength;
160 if (fx || fy){
161 d->setInstantaneousVX(vx: d->curVX(particleSystem: m_system)+ fx * dt, particleSystem: m_system);
162 d->setInstantaneousVY(vy: d->curVY(particleSystem: m_system)+ fy * dt, particleSystem: m_system);
163 postAffect(datum: d);
164 }
165 }
166 }
167}
168
169QT_END_NAMESPACE
170
171#include "moc_qquickturbulence_p.cpp"
172

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