• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 6683238402

29 Oct 2023 12:10PM UTC coverage: 28.07%. Remained the same
6683238402

push

github

mcallegari
engine: fix build

15385 of 54809 relevant lines covered (28.07%)

20267.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.7
/engine/src/efx.cpp
1
/*
2
  Q Light Controller Plus
3
  efx.cpp
4

5
  Copyright (C) Heikki Junnila
6
                Massimo Callegari
7

8
  Licensed under the Apache License, Version 2.0 (the "License");
9
  you may not use this file except in compliance with the License.
10
  You may obtain a copy of the License at
11

12
      http://www.apache.org/licenses/LICENSE-2.0.txt
13

14
  Unless required by applicable law or agreed to in writing, software
15
  distributed under the License is distributed on an "AS IS" BASIS,
16
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
  See the License for the specific language governing permissions and
18
  limitations under the License.
19
*/
20

21
#include <QVector>
22
#include <QDebug>
23
#include <QList>
24
#include <QXmlStreamReader>
25
#include <QXmlStreamWriter>
26

27
#include <math.h>
28

29
#include "genericfader.h"
30
#include "mastertimer.h"
31
#include "qlcmacros.h"
32
#include "doc.h"
33
#include "efx.h"
34
#include "bus.h"
35

36
/*****************************************************************************
37
 * Initialization
38
 *****************************************************************************/
39

40
EFX::EFX(Doc* doc)
73✔
41
    : Function(doc, Function::EFXType)
42
    , m_algorithm(EFX::Circle)
43
    , m_isRelative(false)
44
    , m_xFrequency(2)
45
    , m_yFrequency(3)
46
    , m_xPhase(M_PI / 2.0)
47
    , m_yPhase(0)
48
    , m_propagationMode(Parallel)
49
    , m_legacyFadeBus(Bus::invalid())
146✔
50
    , m_legacyHoldBus(Bus::invalid())
73✔
51
{
52
    updateRotationCache();
73✔
53
    setName(tr("New EFX"));
73✔
54
    setDuration(20000); // 20s
73✔
55

56
    registerAttribute(tr("Width"), Function::LastWins, 0.0, 127.0, 127.0);
73✔
57
    registerAttribute(tr("Height"), Function::LastWins, 0.0, 127.0, 127.0);
73✔
58
    registerAttribute(tr("Rotation"), Function::LastWins, 0.0, 359.0, 0.0);
73✔
59
    registerAttribute(tr("X Offset"), Function::LastWins, 0.0, 255.0, 127.0);
73✔
60
    registerAttribute(tr("Y Offset"), Function::LastWins, 0.0, 255.0, 127.0);
73✔
61
    registerAttribute(tr("Start Offset"), Function::LastWins, 0.0, 359.0, 0.0);
73✔
62
}
73✔
63

64
EFX::~EFX()
156✔
65
{
66
    while (m_fixtures.isEmpty() == false)
104✔
67
        delete m_fixtures.takeFirst();
31✔
68
}
83✔
69

70
QIcon EFX::getIcon() const
×
71
{
72
    return QIcon(":/efx.png");
×
73
}
74

75
/*****************************************************************************
76
 * Copying
77
 *****************************************************************************/
78

79
Function* EFX::createCopy(Doc* doc, bool addToDoc)
1✔
80
{
81
    Q_ASSERT(doc != NULL);
1✔
82

83
    Function* copy = new EFX(doc);
1✔
84
    if (copy->copyFrom(this) == false)
1✔
85
    {
86
        delete copy;
×
87
        copy = NULL;
×
88
    }
89
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
90
    {
91
        delete copy;
×
92
        copy = NULL;
×
93
    }
94

95
    return copy;
1✔
96
}
97

98
bool EFX::copyFrom(const Function* function)
4✔
99
{
100
    const EFX* efx = qobject_cast<const EFX*> (function);
4✔
101
    if (efx == NULL)
4✔
102
        return false;
1✔
103

104
    while (m_fixtures.isEmpty() == false)
5✔
105
        delete m_fixtures.takeFirst();
2✔
106

107
    QListIterator <EFXFixture*> it(efx->m_fixtures);
6✔
108
    while (it.hasNext() == true)
9✔
109
    {
110
        EFXFixture* ef = new EFXFixture(this);
6✔
111
        ef->copyFrom(it.next());
6✔
112
        m_fixtures.append(ef);
6✔
113
    }
114

115
    m_propagationMode = efx->m_propagationMode;
3✔
116

117
    for (int i = 0; i < efx->attributes().count(); i++)
24✔
118
        adjustAttribute(efx->attributes().at(i).m_value, i);
21✔
119

120
    m_isRelative = efx->m_isRelative;
3✔
121

122
    updateRotationCache();
3✔
123

124
    m_xFrequency = efx->m_xFrequency;
3✔
125
    m_yFrequency = efx->m_yFrequency;
3✔
126
    m_xPhase = efx->m_xPhase;
3✔
127
    m_yPhase = efx->m_yPhase;
3✔
128

129
    m_algorithm = efx->m_algorithm;
3✔
130

131
    return Function::copyFrom(function);
3✔
132
}
133

134
void EFX::setDuration(uint ms)
81✔
135
{
136
    Function::setDuration(ms);
81✔
137

138
    for(int i = 0; i < m_fixtures.size(); ++i)
84✔
139
        m_fixtures[i]->durationChanged();
3✔
140

141
    emit durationChanged(ms);
81✔
142
}
81✔
143

144
uint EFX::loopDuration() const
664✔
145
{
146
    uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed();
664✔
147

148
    return duration() - fadeIn;
664✔
149
}
150

151
/*****************************************************************************
152
 * Algorithm
153
 *****************************************************************************/
154

155
EFX::Algorithm EFX::algorithm() const
5,673✔
156
{
157
    return m_algorithm;
5,673✔
158
}
159

160
void EFX::setAlgorithm(EFX::Algorithm algo)
35✔
161
{
162
    if (algo == m_algorithm)
35✔
163
        return;
×
164

165
    if (algo >= EFX::Circle && algo <= EFX::Lissajous)
35✔
166
        m_algorithm = algo;
34✔
167
    else
168
        m_algorithm = EFX::Circle;
1✔
169

170
    emit changed(this->id());
35✔
171
}
172

173
QStringList EFX::algorithmList()
1✔
174
{
175
    QStringList list;
1✔
176
    list << algorithmToString(EFX::Circle);
1✔
177
    list << algorithmToString(EFX::Eight);
1✔
178
    list << algorithmToString(EFX::Line);
1✔
179
    list << algorithmToString(EFX::Line2);
1✔
180
    list << algorithmToString(EFX::Diamond);
1✔
181
    list << algorithmToString(EFX::Square);
1✔
182
    list << algorithmToString(EFX::SquareChoppy);
1✔
183
    list << algorithmToString(EFX::Leaf);
1✔
184
    list << algorithmToString(EFX::Lissajous);
1✔
185
    return list;
1✔
186
}
187

188
QString EFX::algorithmToString(EFX::Algorithm algo)
11✔
189
{
190
    switch (algo)
11✔
191
    {
192
        default:
2✔
193
        case EFX::Circle:
194
            return QString(KXMLQLCEFXCircleAlgorithmName);
2✔
195
        case EFX::Eight:
1✔
196
            return QString(KXMLQLCEFXEightAlgorithmName);
1✔
197
        case EFX::Line:
1✔
198
            return QString(KXMLQLCEFXLineAlgorithmName);
1✔
199
        case EFX::Line2:
1✔
200
            return QString(KXMLQLCEFXLine2AlgorithmName);
1✔
201
        case EFX::Diamond:
1✔
202
            return QString(KXMLQLCEFXDiamondAlgorithmName);
1✔
203
        case EFX::Square:
1✔
204
            return QString(KXMLQLCEFXSquareAlgorithmName);
1✔
205
        case EFX::SquareChoppy:
1✔
206
            return QString(KXMLQLCEFXSquareChoppyAlgorithmName);
1✔
207
        case EFX::Leaf:
1✔
208
            return QString(KXMLQLCEFXLeafAlgorithmName);
1✔
209
        case EFX::Lissajous:
2✔
210
            return QString(KXMLQLCEFXLissajousAlgorithmName);
2✔
211
    }
212
}
213

214
EFX::Algorithm EFX::stringToAlgorithm(const QString& str)
13✔
215
{
216
    if (str == QString(KXMLQLCEFXEightAlgorithmName))
13✔
217
        return EFX::Eight;
1✔
218
    else if (str == QString(KXMLQLCEFXLineAlgorithmName))
12✔
219
        return EFX::Line;
1✔
220
    else if (str == QString(KXMLQLCEFXLine2AlgorithmName))
11✔
221
        return EFX::Line2;
1✔
222
    else if (str == QString(KXMLQLCEFXDiamondAlgorithmName))
10✔
223
        return EFX::Diamond;
4✔
224
    else if (str == QString(KXMLQLCEFXSquareAlgorithmName))
6✔
225
        return EFX::Square;
1✔
226
    else if (str == QString(KXMLQLCEFXSquareChoppyAlgorithmName))
5✔
227
        return EFX::SquareChoppy;
1✔
228
    else if (str == QString(KXMLQLCEFXLeafAlgorithmName))
4✔
229
        return EFX::Leaf;
1✔
230
    else if (str == QString(KXMLQLCEFXLissajousAlgorithmName))
3✔
231
        return EFX::Lissajous;
1✔
232
    else
233
        return EFX::Circle;
2✔
234
}
235

236
void EFX::preview(QPolygonF &polygon) const
34✔
237
{
238
    preview(polygon, Function::Forward, 0);
34✔
239
}
34✔
240

241
void EFX::previewFixtures(QVector <QPolygonF>& polygons) const
×
242
{
243
    polygons.resize(m_fixtures.size());
×
244
    for (int i = 0; i < m_fixtures.size(); ++i)
×
245
        preview(polygons[i], m_fixtures[i]->m_direction, m_fixtures[i]->m_startOffset);
×
246
}
×
247

248
void EFX::preview(QPolygonF &polygon, Function::Direction direction, int startOffset) const
34✔
249
{
250
    float stepCount = 128.0;
34✔
251
    int step = 0;
34✔
252
    float stepSize = 1.0 / (stepCount / (M_PI * 2.0));
34✔
253

254
    float i = 0;
34✔
255
    float x = 0;
34✔
256
    float y = 0;
34✔
257

258
    /* Reset the polygon to fill it with new values */
259
    polygon.clear();
34✔
260

261
    /* Draw a preview of the effect */
262
    for (step = 0; step < stepCount; step++)
4,386✔
263
    {
264
        calculatePoint(direction, startOffset, i, &x, &y);
4,352✔
265
        polygon << QPointF(x, y);
4,352✔
266
        i += stepSize;
4,352✔
267
    }
268
}
34✔
269

270
void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float* x, float* y) const
4,504✔
271
{
272
    iterator = calculateDirection(direction, iterator);
4,504✔
273
    iterator += convertOffset(startOffset + getAttributeValue(StartOffset));
4,504✔
274

275
    if (iterator >= M_PI * 2.0)
4,504✔
276
        iterator -= M_PI * 2.0;
8✔
277

278
    calculatePoint(iterator, x, y);
4,504✔
279
}
4,504✔
280

281
void EFX::rotateAndScale(float* x, float* y) const
4,519✔
282
{
283
    float xx = *x;
4,519✔
284
    float yy = *y;
4,519✔
285
    float w = getAttributeValue(Width);
4,519✔
286
    float h = getAttributeValue(Height);
4,519✔
287
    float fadeScale = 1.0;
4,519✔
288

289
    if (isRunning())
4,519✔
290
    {
291
        uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed();
152✔
292
        if (fadeIn > 0 && elapsed() <= fadeIn)
152✔
293
        {
294
            fadeScale = SCALE(float(elapsed()),
×
295
                              float(0), float(fadeIn),
296
                              float(0), float(1.0));
297
        }
298
    }
299

300
    *x = (getAttributeValue(XOffset)) + xx * m_cosR * (w * fadeScale) + yy * m_sinR * (h * fadeScale);
4,519✔
301
    *y = (getAttributeValue(YOffset)) + -xx * m_sinR * (w * fadeScale) + yy * m_cosR * (h * fadeScale);
4,519✔
302
}
4,519✔
303

304
float EFX::calculateDirection(Function::Direction direction, float iterator) const
4,504✔
305
{
306
    if (direction == this->direction())
4,504✔
307
        return iterator;
3,352✔
308

309
    switch (algorithm())
1,152✔
310
    {
311
    default:
1,024✔
312
    case Circle:
313
    case Eight:
314
    case Line2:
315
    case Diamond:
316
    case Square:
317
    case SquareChoppy:
318
    case Leaf:
319
    case Lissajous:
320
        return (M_PI * 2.0) - iterator;
1,024✔
321
    case Line:
128✔
322
        return (iterator > M_PI) ? (iterator - M_PI) : (iterator + M_PI);
128✔
323
    }
324
}
325

326
// this function should map from 0..M_PI * 2 -> -1..1
327
void EFX::calculatePoint(float iterator, float* x, float* y) const
4,504✔
328
{
329
    switch (algorithm())
4,504✔
330
    {
331
    default:
1,432✔
332
    case Circle:
333
        *x = cos(iterator + M_PI_2);
1,432✔
334
        *y = cos(iterator);
1,432✔
335
        break;
1,432✔
336

337
    case Eight:
384✔
338
        *x = cos((iterator * 2) + M_PI_2);
384✔
339
        *y = cos(iterator);
384✔
340
        break;
384✔
341

342
    case Line:
384✔
343
        *x = cos(iterator);
384✔
344
        *y = cos(iterator);
384✔
345
        break;
384✔
346

347
    case Line2:
384✔
348
        *x = iterator / M_PI - 1;
384✔
349
        *y = iterator / M_PI - 1;
384✔
350
        break;
384✔
351

352
    case Diamond:
384✔
353
        *x = pow(cos(iterator - M_PI_2), 3);
384✔
354
        *y = pow(cos(iterator), 3);
384✔
355
        break;
384✔
356

357
    case Square:
384✔
358
        if (iterator < M_PI / 2)
384✔
359
        {
360
            *x = (iterator * 2 / M_PI) * 2 - 1;
96✔
361
            *y = 1;
96✔
362
        }
363
        else if (M_PI / 2 <= iterator && iterator < M_PI)
288✔
364
        {
365
            *x = 1;
98✔
366
            *y = (1 - (iterator - M_PI / 2) * 2 / M_PI) * 2 - 1;
98✔
367
        }
368
        else if (M_PI <= iterator && iterator < M_PI * 3 / 2)
190✔
369
        {
370
            *x = (1 - (iterator - M_PI) * 2 / M_PI) * 2 - 1;
97✔
371
            *y = -1;
97✔
372
        }
373
        else // M_PI * 3 / 2 <= iterator
374
        {
375
            *x = -1;
93✔
376
            *y = ((iterator - M_PI * 3 / 2) * 2 / M_PI) * 2 - 1;
93✔
377
        }
378
        break;
384✔
379

380
    case SquareChoppy:
384✔
381
        *x = round(cos(iterator));
384✔
382
        *y = round(sin(iterator));
384✔
383
        break;
384✔
384

385
    case Leaf:
384✔
386
        *x = pow(cos(iterator + M_PI_2), 5);
384✔
387
        *y = cos(iterator);
384✔
388
        break;
384✔
389

390
    case Lissajous:
384✔
391
        {
392
            if (m_xFrequency > 0)
384✔
393
                *x = cos((m_xFrequency * iterator) - m_xPhase);
384✔
394
            else
395
            {
396
                float iterator0 = ((iterator + m_xPhase) / M_PI);
×
397
                int fff = iterator0;
×
398
                iterator0 -= (fff - fff % 2);
×
399
                float forward = 1 - floor(iterator0); // 1 when forward
×
400
                float backward = 1 - forward; // 1 when backward
×
401
                iterator0 = iterator0 - floor(iterator0);
×
402
                *x = (forward * iterator0 + backward * (1 - iterator0)) * 2 - 1;
×
403
            }
404
            if (m_yFrequency > 0)
384✔
405
                *y = cos((m_yFrequency * iterator) - m_yPhase);
384✔
406
            else
407
            {
408
                float iterator0 = ((iterator + m_yPhase) / M_PI);
×
409
                int fff = iterator0;
×
410
                iterator0 -= (fff - fff % 2);
×
411
                float forward = 1 - floor(iterator0); // 1 when forward
×
412
                float backward = 1 - forward; // 1 when backward
×
413
                iterator0 = iterator0 - floor(iterator0);
×
414
                *y = (forward * iterator0 + backward * (1 - iterator0)) * 2 - 1;
×
415
            }
416
        }
417
        break;
384✔
418
    }
419

420
    rotateAndScale(x, y);
4,504✔
421
}
4,504✔
422

423
/*****************************************************************************
424
 * Width
425
 *****************************************************************************/
426

427
void EFX::setWidth(int width)
28✔
428
{
429
    adjustAttribute(static_cast<double> (CLAMP(width, 0, 127)), Width);
28✔
430
    emit changed(this->id());
28✔
431
}
28✔
432

433
int EFX::width() const
13✔
434
{
435
    return static_cast<int> (attributes().at(Width).m_value);
13✔
436
}
437

438
/*****************************************************************************
439
 * Height
440
 *****************************************************************************/
441

442
void EFX::setHeight(int height)
28✔
443
{
444
    adjustAttribute(static_cast<double> (CLAMP(height, 0, 127)), Height);
28✔
445
    emit changed(this->id());
28✔
446
}
28✔
447

448
int EFX::height() const
13✔
449
{
450
    return static_cast<int> (attributes().at(Height).m_value);
13✔
451
}
452

453
/*****************************************************************************
454
 * Rotation
455
 *****************************************************************************/
456

457
void EFX::setRotation(int rot)
27✔
458
{
459
    adjustAttribute(CLAMP(rot, 0, 359), Rotation);
27✔
460
    updateRotationCache();
27✔
461
    emit changed(this->id());
27✔
462
}
27✔
463

464
int EFX::rotation() const
13✔
465
{
466
    return static_cast<int> (attributes().at(Rotation).m_value);
13✔
467
}
468

469
void EFX::updateRotationCache()
205✔
470
{
471
    double r = M_PI/180 * getAttributeValue(Rotation);
205✔
472
    m_cosR = cos(r);
205✔
473
    m_sinR = sin(r);
205✔
474
}
205✔
475

476
/*****************************************************************************
477
 * Start Offset
478
 *****************************************************************************/
479

480
void EFX::setStartOffset(int startOffset)
1✔
481
{
482
    adjustAttribute(CLAMP(startOffset, 0, 359), StartOffset);
1✔
483
    emit changed(this->id());
1✔
484
}
1✔
485

486
int EFX::startOffset() const
2✔
487
{
488
    return static_cast<int> (attributes().at(StartOffset).m_value);
2✔
489
}
490

491
float EFX::convertOffset(int offset) const
4,504✔
492
{
493
    return M_PI/180 * (offset % 360);
4,504✔
494
}
495

496
/*****************************************************************************
497
 * Is Relative
498
 *****************************************************************************/
499

500
void EFX::setIsRelative(bool isRelative)
1✔
501
{
502
    m_isRelative = isRelative;
1✔
503
    emit changed(this->id());
1✔
504
}
1✔
505

506
bool EFX::isRelative() const
312✔
507
{
508
    return m_isRelative;
312✔
509
}
510

511
/*****************************************************************************
512
 * Offset
513
 *****************************************************************************/
514

515
void EFX::setXOffset(int offset)
29✔
516
{
517
    adjustAttribute(static_cast<double> (CLAMP(offset, 0, (int)UCHAR_MAX)), XOffset);
29✔
518
    emit changed(this->id());
29✔
519
}
29✔
520

521
int EFX::xOffset() const
14✔
522
{
523
    return static_cast<int> (attributes().at(XOffset).m_value);
14✔
524
}
525

526
void EFX::setYOffset(int offset)
29✔
527
{
528
    adjustAttribute(static_cast<double> (CLAMP(offset, 0, (int)UCHAR_MAX)), YOffset);
29✔
529
    emit changed(this->id());
29✔
530
}
29✔
531

532
int EFX::yOffset() const
13✔
533
{
534
    return static_cast<int> (attributes().at(YOffset).m_value);
13✔
535
}
536

537
/*****************************************************************************
538
 * Frequency
539
 *****************************************************************************/
540

541
void EFX::setXFrequency(int freq)
18✔
542
{
543
    m_xFrequency = static_cast<float> (CLAMP(freq, 0, 32));
18✔
544
    emit changed(this->id());
18✔
545
}
18✔
546

547
int EFX::xFrequency() const
19✔
548
{
549
    return static_cast<int> (m_xFrequency);
19✔
550
}
551

552
void EFX::setYFrequency(int freq)
18✔
553
{
554
    m_yFrequency = static_cast<float> (CLAMP(freq, 0, 32));
18✔
555
    emit changed(this->id());
18✔
556
}
18✔
557

558
int EFX::yFrequency() const
18✔
559
{
560
    return static_cast<int> (m_yFrequency);
18✔
561
}
562

563
bool EFX::isFrequencyEnabled()
5✔
564
{
565
    if (m_algorithm == EFX::Lissajous)
5✔
566
        return true;
2✔
567
    else
568
        return false;
3✔
569
}
570

571
/*****************************************************************************
572
 * Phase
573
 *****************************************************************************/
574

575
void EFX::setXPhase(int phase)
20✔
576
{
577
    m_xPhase = static_cast<float> (CLAMP(phase, 0, 359)) * M_PI / 180.0;
20✔
578
    emit changed(this->id());
20✔
579
}
20✔
580

581
int EFX::xPhase() const
21✔
582
{
583
    return static_cast<int> (floor((m_xPhase * 180.0 / M_PI) + 0.5));
21✔
584
}
585

586
void EFX::setYPhase(int phase)
20✔
587
{
588
    m_yPhase = static_cast<float> (CLAMP(phase, 0, 359)) * M_PI / 180.0;
20✔
589
    emit changed(this->id());
20✔
590
}
20✔
591

592
int EFX::yPhase() const
20✔
593
{
594
    return static_cast<int> (floor((m_yPhase * 180.0 / M_PI) + 0.5));
20✔
595
}
596

597
bool EFX::isPhaseEnabled() const
5✔
598
{
599
    if (m_algorithm == EFX::Lissajous)
5✔
600
        return true;
2✔
601
    else
602
        return false;
3✔
603
}
604

605
/*****************************************************************************
606
 * Fixtures
607
 *****************************************************************************/
608

609
bool EFX::addFixture(EFXFixture* ef)
32✔
610
{
611
    Q_ASSERT(ef != NULL);
32✔
612

613
    /* Search for an existing fixture with the same ID and append at last but do
614
     * not prevent multiple entries because a fixture can have multiple efx. */
615
    //! @todo Prevent multiple entries using head & mode
616
    int i;
617
    for(i = 0; i < m_fixtures.size (); i++)
60✔
618
    {
619
        if (m_fixtures[i]->head() == ef->head())
28✔
620
        {
621
            m_fixtures.insert(i, ef);
×
622
            break;
×
623
        }
624
    }
625

626
    /* If not inserted, put the EFXFixture object into our list */
627
    if(i >= m_fixtures.size())
32✔
628
        m_fixtures.append(ef);
32✔
629

630
    emit changed(this->id());
32✔
631

632
    return true;
32✔
633
}
634

635
bool EFX::addFixture(quint32 fxi, int head)
×
636
{
637
    EFXFixture *ef = new EFXFixture(this);
×
638
    GroupHead gHead(fxi, head);
×
639
    ef->setHead(gHead);
×
640

641
    return addFixture(ef);
×
642
}
643

644
bool EFX::removeFixture(EFXFixture* ef)
2✔
645
{
646
    Q_ASSERT(ef != NULL);
2✔
647

648
    if (m_fixtures.removeAll(ef) > 0)
2✔
649
    {
650
        emit changed(this->id());
1✔
651
        return true;
1✔
652
    }
653
    else
654
    {
655
        return false;
1✔
656
    }
657
}
658

659
bool EFX::removeFixture(quint32 fxi, int head)
2✔
660
{
661
    for (int i = 0; i < m_fixtures.count(); i++)
2✔
662
    {
663
        EFXFixture *ef = m_fixtures.at(i);
1✔
664
        if (ef->head().fxi == fxi && ef->head().head == head)
1✔
665
        {
666
            m_fixtures.removeAt(i);
1✔
667
            return true;
1✔
668
        }
669
    }
670

671
    return false;
1✔
672
}
673

674
void EFX::removeAllFixtures()
×
675
{
676
    m_fixtures.clear();
×
677
    emit changed(this->id());
×
678
}
×
679

680
bool EFX::raiseFixture(EFXFixture* ef)
3✔
681
{
682
    Q_ASSERT(ef != NULL);
3✔
683

684
    int index = m_fixtures.indexOf(ef);
3✔
685
    if (index > 0)
3✔
686
    {
687
        m_fixtures.move(index, index - 1);
1✔
688
        emit changed(this->id());
1✔
689
        return true;
1✔
690
    }
691
    else
692
    {
693
        return false;
2✔
694
    }
695
}
696

697
bool EFX::lowerFixture(EFXFixture* ef)
3✔
698
{
699
    int index = m_fixtures.indexOf(ef);
3✔
700
    if (index < (m_fixtures.count() - 1))
3✔
701
    {
702
        m_fixtures.move(index, index + 1);
1✔
703
        emit changed(this->id());
1✔
704
        return true;
1✔
705
    }
706
    else
707
    {
708
        return false;
2✔
709
    }
710
}
711

712
const QList <EFXFixture*> EFX::fixtures() const
80✔
713
{
714
    return m_fixtures;
80✔
715
}
716

717
EFXFixture *EFX::fixture(quint32 id, int headIndex)
4✔
718
{
719
    foreach (EFXFixture *ef, m_fixtures)
16✔
720
    {
721
        if (ef->head().fxi == id && ef->head().head == headIndex)
9✔
722
            return ef;
3✔
723
    }
724

725
    return NULL;
1✔
726
}
727

728
QList<quint32> EFX::components()
×
729
{
730
    QList<quint32> ids;
×
731

732
    foreach (EFXFixture *ef, m_fixtures)
×
733
    {
734
        if (ids.contains(ef->head().fxi) == false)
×
735
            ids.append(ef->head().fxi);
×
736
    }
737

738
    return ids;
×
739
}
740

741
void EFX::slotFixtureRemoved(quint32 fxi_id)
5✔
742
{
743
    /* Remove the destroyed fixture from our list */
744
    QMutableListIterator <EFXFixture*> it(m_fixtures);
5✔
745
    while (it.hasNext() == true)
11✔
746
    {
747
        it.next();
9✔
748

749
        if (it.value()->head().fxi == fxi_id)
9✔
750
        {
751
            delete it.value();
3✔
752
            it.remove();
3✔
753
            break;
3✔
754
        }
755
    }
756
}
5✔
757

758
/*****************************************************************************
759
 * Fixture propagation mode
760
 *****************************************************************************/
761

762
void EFX::setPropagationMode(PropagationMode mode)
9✔
763
{
764
    m_propagationMode = mode;
9✔
765
    emit changed(this->id());
9✔
766
}
9✔
767

768
EFX::PropagationMode EFX::propagationMode() const
471✔
769
{
770
    return m_propagationMode;
471✔
771
}
772

773
QString EFX::propagationModeToString(PropagationMode mode)
6✔
774
{
775
    if (mode == Serial)
6✔
776
        return QString(KXMLQLCEFXPropagationModeSerial);
2✔
777
    else if (mode == Asymmetric)
4✔
778
        return QString(KXMLQLCEFXPropagationModeAsymmetric);
1✔
779
    else
780
        return QString(KXMLQLCEFXPropagationModeParallel);
3✔
781
}
782

783
EFX::PropagationMode EFX::stringToPropagationMode(QString str)
7✔
784
{
785
    if (str == QString(KXMLQLCEFXPropagationModeSerial))
7✔
786
        return Serial;
4✔
787
    else if (str == QString(KXMLQLCEFXPropagationModeAsymmetric))
3✔
788
        return Asymmetric;
1✔
789
    else
790
        return Parallel;
2✔
791
}
792

793
/*****************************************************************************
794
 * Load & Save
795
 *****************************************************************************/
796

797
bool EFX::saveXML(QXmlStreamWriter *doc)
2✔
798
{
799
    Q_ASSERT(doc != NULL);
2✔
800

801
    /* Function tag */
802
    doc->writeStartElement(KXMLQLCFunction);
2✔
803

804
    /* Common attributes */
805
    saveXMLCommon(doc);
2✔
806

807
    /* Fixtures */
808
    QListIterator <EFXFixture*> it(m_fixtures);
2✔
809
    while (it.hasNext() == true)
5✔
810
        it.next()->saveXML(doc);
3✔
811

812
    /* Propagation mode */
813
    doc->writeTextElement(KXMLQLCEFXPropagationMode, propagationModeToString(m_propagationMode));
2✔
814

815
    /* Speeds */
816
    saveXMLSpeed(doc);
2✔
817
    /* Direction */
818
    saveXMLDirection(doc);
2✔
819
    /* Run order */
820
    saveXMLRunOrder(doc);
2✔
821

822
    /* Algorithm */
823
    doc->writeTextElement(KXMLQLCEFXAlgorithm, algorithmToString(algorithm()));
2✔
824
    /* Width */
825
    doc->writeTextElement(KXMLQLCEFXWidth, QString::number(width()));
2✔
826
    /* Height */
827
    doc->writeTextElement(KXMLQLCEFXHeight, QString::number(height()));
2✔
828
    /* Rotation */
829
    doc->writeTextElement(KXMLQLCEFXRotation, QString::number(rotation()));
2✔
830
    /* StartOffset */
831
    doc->writeTextElement(KXMLQLCEFXStartOffset, QString::number(startOffset()));
2✔
832
    /* IsRelative */
833
    doc->writeTextElement(KXMLQLCEFXIsRelative, QString::number(isRelative() ? 1 : 0));
2✔
834

835
    /********************************************
836
     * X-Axis
837
     ********************************************/
838
    doc->writeStartElement(KXMLQLCEFXAxis);
2✔
839
    doc->writeAttribute(KXMLQLCFunctionName, KXMLQLCEFXX);
2✔
840

841
    /* Offset */
842
    doc->writeTextElement(KXMLQLCEFXOffset, QString::number(xOffset()));
2✔
843
    /* Frequency */
844
    doc->writeTextElement(KXMLQLCEFXFrequency, QString::number(xFrequency()));
2✔
845
    /* Phase */
846
    doc->writeTextElement(KXMLQLCEFXPhase, QString::number(xPhase()));
2✔
847

848
    /* End the (X) <Axis> tag */
849
    doc->writeEndElement();
2✔
850

851
    /********************************************
852
     * Y-Axis
853
     ********************************************/
854
    doc->writeStartElement(KXMLQLCEFXAxis);
2✔
855
    doc->writeAttribute(KXMLQLCFunctionName, KXMLQLCEFXY);
2✔
856

857
    /* Offset */
858
    doc->writeTextElement(KXMLQLCEFXOffset, QString::number(yOffset()));
2✔
859
    /* Frequency */
860
    doc->writeTextElement(KXMLQLCEFXFrequency, QString::number(yFrequency()));
2✔
861
    /* Phase */
862
    doc->writeTextElement(KXMLQLCEFXPhase, QString::number(yPhase()));
2✔
863

864
    /* End the (Y) <Axis> tag */
865
    doc->writeEndElement();
2✔
866

867
    /* End the <Function> tag */
868
    doc->writeEndElement();
2✔
869

870
    return true;
4✔
871
}
872

873
bool EFX::loadXML(QXmlStreamReader &root)
5✔
874
{
875
    if (root.name() != KXMLQLCFunction)
5✔
876
    {
877
        qWarning() << "Function node not found!";
1✔
878
        return false;
1✔
879
    }
880

881
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::EFXType))
4✔
882
    {
883
        qWarning("Function is not an EFX!");
1✔
884
        return false;
1✔
885
    }
886

887
    /* Load EFX contents */
888
    while (root.readNextStartElement())
45✔
889
    {
890
        if (root.name() == KXMLQLCBus)
42✔
891
        {
892
            /* Bus */
893
            QString str = root.attributes().value(KXMLQLCBusRole).toString();
9✔
894
            if (str == KXMLQLCBusFade)
3✔
895
                m_legacyFadeBus = root.readElementText().toUInt();
2✔
896
            else if (str == KXMLQLCBusHold)
1✔
897
                m_legacyHoldBus = root.readElementText().toUInt();
1✔
898
        }
899
        else if (root.name() == KXMLQLCFunctionSpeed)
39✔
900
        {
901
            loadXMLSpeed(root);
1✔
902
        }
903
        else if (root.name() == KXMLQLCEFXFixture)
38✔
904
        {
905
            EFXFixture* ef = new EFXFixture(this);
9✔
906
            ef->loadXML(root);
9✔
907
            if (ef->head().isValid())
9✔
908
            {
909
                if (addFixture(ef) == false)
9✔
910
                    delete ef;
×
911
            }
912
        }
913
        else if (root.name() == KXMLQLCEFXPropagationMode)
29✔
914
        {
915
            /* Propagation mode */
916
            setPropagationMode(stringToPropagationMode(root.readElementText()));
3✔
917
        }
918
        else if (root.name() == KXMLQLCEFXAlgorithm)
26✔
919
        {
920
            /* Algorithm */
921
            setAlgorithm(stringToAlgorithm(root.readElementText()));
3✔
922
        }
923
        else if (root.name() == KXMLQLCFunctionDirection)
23✔
924
        {
925
            loadXMLDirection(root);
3✔
926
        }
927
        else if (root.name() == KXMLQLCFunctionRunOrder)
20✔
928
        {
929
            loadXMLRunOrder(root);
3✔
930
        }
931
        else if (root.name() == KXMLQLCEFXWidth)
17✔
932
        {
933
            /* Width */
934
            setWidth(root.readElementText().toInt());
3✔
935
        }
936
        else if (root.name() == KXMLQLCEFXHeight)
14✔
937
        {
938
            /* Height */
939
            setHeight(root.readElementText().toInt());
3✔
940
        }
941
        else if (root.name() == KXMLQLCEFXRotation)
11✔
942
        {
943
            /* Rotation */
944
            setRotation(root.readElementText().toInt());
3✔
945
        }
946
        else if (root.name() == KXMLQLCEFXStartOffset)
8✔
947
        {
948
            /* StartOffset */
949
            setStartOffset(root.readElementText().toInt());
×
950
        }
951
        else if (root.name() == KXMLQLCEFXIsRelative)
8✔
952
        {
953
            /* IsRelative */
954
            setIsRelative(root.readElementText().toInt() != 0);
×
955
        }
956
        else if (root.name() == KXMLQLCEFXAxis)
8✔
957
        {
958
            /* Axes */
959
            loadXMLAxis(root);
6✔
960
        }
961
        else
962
        {
963
            qWarning() << "Unknown EFX tag:" << root.name();
2✔
964
            root.skipCurrentElement();
2✔
965
        }
966
    }
967

968
    return true;
3✔
969
}
970

971
bool EFX::loadXMLAxis(QXmlStreamReader &root)
10✔
972
{
973
    int frequency = 0;
10✔
974
    int offset = 0;
10✔
975
    int phase = 0;
10✔
976
    QString axis;
20✔
977

978
    if (root.name() != KXMLQLCEFXAxis)
10✔
979
    {
980
        qWarning() << "EFX axis node not found!";
1✔
981
        return false;
1✔
982
    }
983

984
    /* Get the axis name */
985
    axis = root.attributes().value(KXMLQLCFunctionName).toString();
9✔
986

987
    /* Load axis contents */
988
    while (root.readNextStartElement())
37✔
989
    {
990
        if (root.name() == KXMLQLCEFXOffset)
28✔
991
            offset = root.readElementText().toInt();
9✔
992
        else if (root.name() == KXMLQLCEFXFrequency)
19✔
993
            frequency = root.readElementText().toInt();
9✔
994
        else if (root.name() == KXMLQLCEFXPhase)
10✔
995
            phase = root.readElementText().toInt();
9✔
996
        else
997
        {
998
            qWarning() << "Unknown EFX axis tag:" << root.name();
1✔
999
            root.skipCurrentElement();
1✔
1000
        }
1001
    }
1002

1003
    if (axis == KXMLQLCEFXY)
9✔
1004
    {
1005
        setYOffset(offset);
4✔
1006
        setYFrequency(frequency);
4✔
1007
        setYPhase(phase);
4✔
1008
        return true;
4✔
1009
    }
1010
    else if (axis == KXMLQLCEFXX)
5✔
1011
    {
1012
        setXOffset(offset);
4✔
1013
        setXFrequency(frequency);
4✔
1014
        setXPhase(phase);
4✔
1015
        return true;
4✔
1016
    }
1017
    else
1018
    {
1019
        qWarning() << "Unknown EFX axis:" << axis;
1✔
1020
        return false;
1✔
1021
    }
1022
}
1023

1024
void EFX::postLoad()
1✔
1025
{
1026
    // Map legacy bus speeds to fixed speed values
1027
    if (m_legacyFadeBus != Bus::invalid())
1✔
1028
    {
1029
        quint32 value = Bus::instance()->value(m_legacyFadeBus);
1✔
1030
        setFadeInSpeed((value / MasterTimer::frequency()) * 1000);
1✔
1031
        setFadeOutSpeed((value / MasterTimer::frequency()) * 1000);
1✔
1032
    }
1033

1034
    if (m_legacyHoldBus != Bus::invalid())
1✔
1035
    {
1036
        quint32 value = Bus::instance()->value(m_legacyHoldBus);
1✔
1037
        setDuration((value / MasterTimer::frequency()) * 1000);
1✔
1038
    }
1039
}
1✔
1040

1041
/*****************************************************************************
1042
 * Running
1043
 *****************************************************************************/
1044
QSharedPointer<GenericFader> EFX::getFader(QList<Universe *> universes, quint32 universeID)
×
1045
{
1046
    // get the universe Fader first. If doesn't exist, create it
1047
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
×
1048
    if (fader.isNull())
×
1049
    {
1050
        fader = universes[universeID]->requestFader();
×
1051
        fader->adjustIntensity(getAttributeValue(Intensity));
×
1052
        fader->setBlendMode(blendMode());
×
1053
        fader->setName(name());
×
1054
        fader->setParentFunctionID(id());
×
1055
        m_fadersMap[universeID] = fader;
×
1056
    }
1057

1058
    return fader;
×
1059
}
1060

1061
void EFX::preRun(MasterTimer* timer)
5✔
1062
{
1063
    int serialNumber = 0;
5✔
1064

1065
    QListIterator <EFXFixture*> it(m_fixtures);
10✔
1066
    while (it.hasNext() == true)
10✔
1067
    {
1068
        EFXFixture* ef = it.next();
5✔
1069
        Q_ASSERT(ef != NULL);
5✔
1070
        ef->setSerialNumber(serialNumber++);
5✔
1071
    }
1072

1073
    Function::preRun(timer);
5✔
1074
}
5✔
1075

1076
void EFX::write(MasterTimer *timer, QList<Universe*> universes)
×
1077
{
1078
    Q_UNUSED(timer);
1079

1080
    if (isPaused())
×
1081
        return;
×
1082

1083
    int done = 0;
×
1084

1085
    QListIterator <EFXFixture*> it(m_fixtures);
×
1086
    while (it.hasNext() == true)
×
1087
    {
1088
        EFXFixture *ef = it.next();
×
1089
        if (ef->isDone() == false)
×
1090
        {
1091
            QSharedPointer<GenericFader> fader = getFader(universes, ef->universe());
×
1092
            ef->nextStep(universes, fader);
×
1093
        }
1094
        else
1095
        {
1096
            done++;
×
1097
        }
1098
    }
1099

1100
    incrementElapsed();
×
1101

1102
    /* Check for stop condition */
1103
    if (done == m_fixtures.count())
×
1104
        stop(FunctionParent::master());
×
1105
}
1106

1107
void EFX::postRun(MasterTimer *timer, QList<Universe *> universes)
5✔
1108
{
1109
    /* Reset all fixtures */
1110
    QListIterator <EFXFixture*> it(m_fixtures);
5✔
1111
    while (it.hasNext() == true)
10✔
1112
    {
1113
        EFXFixture* ef(it.next());
5✔
1114

1115
        /* Run the EFX's stop scene for Loop & PingPong modes */
1116
        if (runOrder() != SingleShot)
5✔
1117
            ef->stop();
4✔
1118
        ef->reset();
5✔
1119
    }
1120

1121
    dismissAllFaders();
5✔
1122

1123
    Function::postRun(timer, universes);
5✔
1124
}
5✔
1125

1126
/*****************************************************************************
1127
 * Intensity
1128
 *****************************************************************************/
1129

1130
int EFX::adjustAttribute(qreal fraction, int attributeId)
165✔
1131
{
1132
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
165✔
1133

1134
    switch (attrIndex)
165✔
1135
    {
1136
        case Intensity:
2✔
1137
        {
1138
            foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
4✔
1139
            {
1140
                if (!fader.isNull())
×
1141
                    fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
1142
            }
1143
        }
1144
        break;
2✔
1145

1146
        case Height:
102✔
1147
        case Width:
1148
        case XOffset:
1149
        case YOffset:
1150
        case Rotation:
1151
            updateRotationCache();
102✔
1152
        break;
102✔
1153
    }
1154

1155
    return attrIndex;
165✔
1156
}
1157

1158
/*************************************************************************
1159
 * Blend mode
1160
 *************************************************************************/
1161

1162
void EFX::setBlendMode(Universe::BlendMode mode)
1✔
1163
{
1164
    if (mode == blendMode())
1✔
1165
        return;
1✔
1166

1167
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
1168
    {
1169
        if (!fader.isNull())
×
1170
            fader->setBlendMode(mode);
×
1171
    }
1172

1173
    Function::setBlendMode(mode);
×
1174
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc