6674cd564d1314135d016f25a88e78bd9c016abd
[profile/ivi/qtdeclarative.git] / src / quick / particles / qquickturbulence.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the Declarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickturbulence_p.h"
43 #include "qquickparticlepainter_p.h"//TODO: Why was this needed again?
44 #include <cmath>
45 #include <cstdlib>
46 #include <QDebug>
47 QT_BEGIN_NAMESPACE
48
49 /*!
50     \qmlclass Turbulence QQuickTurbulenceAffector
51     \inqmlmodule QtQuick.Particles 2
52     \inherits Affector
53     \brief Turbulence provides fluid like forces based on a noise image.
54
55     The Turbulence Element scales the noise source over the area it affects,
56     and uses the curl of that source to generate force vectors.
57
58     Turbulence requires a fixed size. Unlike other affectors, a 0x0 Turbulence element
59     will affect no particles.
60
61     The source should be relatively smooth black and white noise, such as perlin noise.
62 */
63 /*!
64     \qmlproperty real QtQuick.Particles2::Turbulence::strength
65
66     The magnitude of the velocity vector at any point varies between zero and
67     the square root of two. It will then be multiplied by strength to get the
68     velocity per second for the particles affected by the turbulence.
69 */
70 /*!
71     \qmlproperty url QtQuick.Particles2::Turbulence::noiseSource
72
73     The source image to generate the turbulence from. It will be scaled to the size of the element,
74     so equal or larger sizes will give better results. Tweaking this image is the only way to tweak
75     behavior such as where vortices are or how many exist.
76
77     The source should be a relatively smooth black and white noise image, such as perlin noise.
78     A default image will be used if none is provided.
79 */
80
81 QQuickTurbulenceAffector::QQuickTurbulenceAffector(QQuickItem *parent) :
82     QQuickParticleAffector(parent),
83     m_strength(10), m_lastT(0), m_gridSize(0), m_field(0), m_vectorField(0), m_inited(false)
84 {
85 }
86
87 void QQuickTurbulenceAffector::geometryChanged(const QRectF &, const QRectF &)
88 {
89     initializeGrid();
90 }
91
92 QQuickTurbulenceAffector::~QQuickTurbulenceAffector()
93 {
94     if (m_field) {
95         for (int i=0; i<m_gridSize; i++)
96             free(m_field[i]);
97         free(m_field);
98     }
99     if (m_vectorField) {
100         for (int i=0; i<m_gridSize; i++)
101             free(m_vectorField[i]);
102         free(m_vectorField);
103     }
104 }
105
106 void QQuickTurbulenceAffector::initializeGrid()
107 {
108     if (!m_inited)
109         return;
110
111     int arg = qMax(width(), height());
112     if (m_gridSize != arg) {
113         if (m_field){ //deallocate and then reallocate grid
114             for (int i=0; i<m_gridSize; i++)
115                 free(m_field[i]);
116             free(m_field);
117             m_system = 0;
118         }
119         if (m_vectorField) {
120             for (int i=0; i<m_gridSize; i++)
121                 free(m_vectorField[i]);
122             free(m_vectorField);
123         }
124         m_gridSize = arg;
125     }
126
127     m_field = (qreal**)malloc(m_gridSize * sizeof(qreal*));
128     for (int i=0; i<m_gridSize; i++)
129         m_field[i] = (qreal*)malloc(m_gridSize * sizeof(qreal));
130     m_vectorField = (QPointF**)malloc(m_gridSize * sizeof(QPointF*));
131     for (int i=0; i<m_gridSize; i++)
132         m_vectorField[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF));
133
134     QImage image;
135     if (!m_noiseSource.isEmpty())
136         image = QImage(m_noiseSource.toLocalFile()).scaled(QSize(m_gridSize, m_gridSize));
137     if (image.isNull())
138         image = QImage(QStringLiteral(":particleresources/noise.png")).scaled(QSize(m_gridSize, m_gridSize));
139
140     for (int i=0; i<m_gridSize; i++)
141         for (int j=0; j<m_gridSize; j++)
142             m_field[i][j] = qRed(image.pixel(QPoint(i,j)));//Red as proxy for Value
143     for (int i=0; i<m_gridSize; i++){
144         for (int j=0; j<m_gridSize; j++){
145             m_vectorField[i][j].setX(boundsRespectingField(i,j) - boundsRespectingField(i,j-1));
146             m_vectorField[i][j].setY(boundsRespectingField(i-1,j) - boundsRespectingField(i,j));
147         }
148     }
149 }
150
151 qreal QQuickTurbulenceAffector::boundsRespectingField(int x, int y)
152 {
153     if (x < 0)
154         x = 0;
155     if (x >= m_gridSize)
156         x = m_gridSize - 1;
157     if (y < 0)
158         y = 0;
159     if (y >= m_gridSize)
160         y = m_gridSize - 1;
161     return m_field[x][y];
162 }
163
164 void QQuickTurbulenceAffector::ensureInit()
165 {
166     if (m_inited)
167         return;
168     m_inited = true;
169     initializeGrid();
170 }
171
172 void QQuickTurbulenceAffector::affectSystem(qreal dt)
173 {
174     if (!m_system || !m_enabled)
175         return;
176     ensureInit();
177     if (!m_gridSize)
178         return;
179
180     updateOffsets();//### Needed if an ancestor is transformed.
181
182     QRect boundsRect(0,0,m_gridSize,m_gridSize);
183     foreach (QQuickParticleGroupData *gd, m_system->groupData){
184         if (!activeGroup(m_system->groupData.key(gd)))
185             continue;
186         foreach (QQuickParticleData *d, gd->data){
187             if (!shouldAffect(d))
188                 continue;
189             QPoint pos = (QPointF(d->curX(), d->curY()) - m_offset).toPoint();
190             if (!boundsRect.contains(pos,true))//Need to redo bounds checking due to quantization.
191                 continue;
192             qreal fx = 0.0;
193             qreal fy = 0.0;
194             fx += m_vectorField[pos.x()][pos.y()].x() * m_strength;
195             fy += m_vectorField[pos.x()][pos.y()].y() * m_strength;
196             if (fx || fy){
197                 d->setInstantaneousVX(d->curVX()+ fx * dt);
198                 d->setInstantaneousVY(d->curVY()+ fy * dt);
199                 postAffect(d);
200             }
201         }
202     }
203 }
204
205 QT_END_NAMESPACE