aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/particles/qsgparticleaffector.cpp
blob: 4cb3e49403c0febf566a809177d768e060e2e311 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Declarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsgparticleaffector_p.h"
#include <QDebug>
QT_BEGIN_NAMESPACE
/*!
    \qmlclass Affector QSGParticleAffector
    \inqmlmodule QtQuick.Particles 2
    \brief Affector elements can alter the attributes of logical particles at any point in their lifetime.

    The base Affector does not alter any attributes, but can be used to emit a signal
    when a particle meets certain conditions.

    If an affector has a defined size, then it will only affect particles within its size and position on screen.
*/
/*!
    \qmlproperty ParticleSystem QtQuick.Particles2::Affector::system
    This is the system which will be affected by the element.
    If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it.
*/
/*!
    \qmlproperty list<string> QtQuick.Particles2::Affector::particles
    Which logical particle groups will be affected.

    If empty, it will affect all particles.
*/
/*!
    \qmlproperty list<string> QtQuick.Particles2::Affector::collisionParticles
    If any logical particle groups are specified here, then the affector
    will only be triggered if the particle being examined intersects with
    a particle of one of these groups.

    By default, no groups are specified.
*/
/*!
    \qmlproperty bool QtQuick.Particles2::Affector::active
    If active is set to false, this affector will not affect any particles.

    Usually this is used to conditionally turn an affector on or off.

    Default value is true.
*/
/*!
    \qmlproperty bool QtQuick.Particles2::Affector::onceOff
    If onceOff is set to true, this affector will only affect each particle
    once in their lifetimes.

    Default value is false.
*/
/*!
    \qmlproperty Shape QtQuick.Particles2::Affector::shape
    If a size has been defined, the shape property can be used to affect a
    non-rectangular area.
*/
/*!
    \qmlproperty bool QtQuick.Particles2::Affector::signal
    If this is set to true, then an affected(x,y) signal will be emitted each
    time this affector would affect a particle.

    For Affector only, this will happen irrespective of whether any changes
    are made to the particle, for other Affectors the signal will only fire
    if the particle is actually affected.

    Default value is false.
*/

//TODO: Document particle 'type'
/*!
    \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle, dt)

    This handler is called when a particle is selected to be affected.

    dt is the time since the last time it was affected. Use dt to normalize
    trajectory manipulations to real time.
*/
QSGParticleAffector::QSGParticleAffector(QSGItem *parent) :
    QSGItem(parent), m_needsReset(false), m_system(0), m_active(true)
  , m_updateIntSet(false), m_shape(new QSGParticleExtruder(this)), m_signal(false)
{
    connect(this, SIGNAL(systemChanged(QSGParticleSystem*)),
            this, SLOT(updateOffsets()));
    connect(this, SIGNAL(xChanged()),
            this, SLOT(updateOffsets()));
    connect(this, SIGNAL(yChanged()),
            this, SLOT(updateOffsets()));//TODO: in componentComplete and all relevant signals
}

bool QSGParticleAffector::isAffectConnected()
{
    static int idx = QObjectPrivate::get(this)->signalIndex("affectParticle(QDeclarativeV8Handle, qreal)");
    return QObjectPrivate::get(this)->isSignalConnected(idx);
}

void QSGParticleAffector::componentComplete()
{
    if (!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
        setSystem(qobject_cast<QSGParticleSystem*>(parentItem()));
    if (!m_system)
        qWarning() << "Affector created without a particle system specified";//TODO: useful QML warnings, like line number?
    QSGItem::componentComplete();
}

void QSGParticleAffector::affectSystem(qreal dt)
{
    if (!m_active)
        return;
    //If not reimplemented, calls affect particle per particle
    //But only on particles in targeted system/area
    if (m_updateIntSet){
        m_groups.clear();
        foreach (const QString &p, m_particles)
            m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned?
        m_updateIntSet = false;
    }
    foreach (QSGParticleGroupData* gd, m_system->m_groupData){
        foreach (QSGParticleData* d, gd->data){
            if (!d)
                continue;
            if (m_groups.isEmpty() || m_groups.contains(d->group)){
                if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))
                        || !d->stillAlive())
                    continue;
                //Need to have previous location for affected. if signal || shape might be faster?
                QPointF curPos = QPointF(d->curX(), d->curY());
                if (width() == 0 || height() == 0
                        || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){
                    if (m_collisionParticles.isEmpty() || isColliding(d)){
                        if (affectParticle(d, dt)){
                            m_system->m_needsReset << d;
                            if (m_onceOff)
                                m_onceOffed << qMakePair(d->group, d->index);
                            if (m_signal)
                                emit affected(curPos.x(), curPos.y());
                        }
                    }
                }
            }
        }
    }
}

bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt)
{
    if (isAffectConnected()){
        emit affectParticle(d->v8Value(), dt);
        return true;
    }
    return m_signal;//If signalling, then we always 'null affect' it.
}

void QSGParticleAffector::reset(QSGParticleData* pd)
{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
    if (m_onceOff)
        if (m_groups.isEmpty() || m_groups.contains(pd->group))
            m_onceOffed.remove(qMakePair(pd->group, pd->index));
}

void QSGParticleAffector::updateOffsets()
{
    if (m_system)
        m_offset = m_system->mapFromItem(this, QPointF(0, 0));
}

bool QSGParticleAffector::isColliding(QSGParticleData *d)
{
    qreal myCurX = d->curX();
    qreal myCurY = d->curY();
    qreal myCurSize = d->curSize()/2;
    foreach (const QString &group, m_collisionParticles){
        foreach (QSGParticleData* other, m_system->m_groupData[m_system->m_groupIds[group]]->data){
            if (!other->stillAlive())
                continue;
            qreal otherCurX = other->curX();
            qreal otherCurY = other->curY();
            qreal otherCurSize = other->curSize()/2;
            if ((myCurX + myCurSize > otherCurX - otherCurSize
                 && myCurX - myCurSize < otherCurX + otherCurSize)
                 && (myCurY + myCurSize > otherCurY - otherCurSize
                     && myCurY - myCurSize < otherCurY + otherCurSize))
                return true;
        }
    }
    return false;
}

QT_END_NAMESPACE