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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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