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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

86.49
/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)
74✔
41
    : Function(doc, Function::EFXType)
42
    , m_algorithm(EFX::Circle)
74✔
43
    , m_isRelative(false)
74✔
44
    , m_xFrequency(2)
74✔
45
    , m_yFrequency(3)
74✔
46
    , m_xPhase(M_PI / 2.0)
74✔
47
    , m_yPhase(0)
74✔
48
    , m_propagationMode(Parallel)
74✔
49
    , m_legacyFadeBus(Bus::invalid())
74✔
50
    , m_legacyHoldBus(Bus::invalid())
74✔
51
{
52
    updateRotationCache();
74✔
53
    setName(tr("New EFX"));
74✔
54
    setDuration(20000); // 20s
74✔
55

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

64
EFX::~EFX()
84✔
65
{
66
    while (m_fixtures.isEmpty() == false)
179✔
67
        delete m_fixtures.takeFirst();
31✔
68
}
84✔
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);
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);
101
    if (efx == NULL)
4✔
102
        return false;
103

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

107
    QListIterator <EFXFixture*> it(efx->m_fixtures);
3✔
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)
82✔
135
{
136
    Function::setDuration(ms);
82✔
137

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

141
    emit durationChanged(ms);
82✔
142
}
82✔
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,802✔
156
{
157
    return m_algorithm;
5,802✔
158
}
159

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

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

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

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

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

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

241
void EFX::preview(QPolygonF &polygon) const
35✔
242
{
243
    preview(polygon, Function::Forward, 0);
35✔
244
}
35✔
245

246
void EFX::previewFixtures(QVector <QPolygonF>& polygons) const
×
247
{
248
    polygons.resize(m_fixtures.size());
×
249
    for (int i = 0; i < m_fixtures.size(); ++i)
×
250
        preview(polygons[i], m_fixtures[i]->m_direction, m_fixtures[i]->m_startOffset);
×
251
}
×
252

253
void EFX::preview(QPolygonF &polygon, Function::Direction direction, int startOffset) const
35✔
254
{
255
    float stepCount = 128.0;
256
    int step = 0;
257
    float stepSize = 1.0 / (stepCount / (M_PI * 2.0));
258

259
    float i = 0;
260
    float x = 0;
35✔
261
    float y = 0;
35✔
262

263
    /* Reset the polygon to fill it with new values */
264
    polygon.clear();
35✔
265

266
    /* Draw a preview of the effect */
267
    for (step = 0; step < stepCount; step++)
4,515✔
268
    {
269
        calculatePoint(direction, startOffset, i, &x, &y);
4,480✔
270
        polygon << QPointF(x, y);
4,480✔
271
        i += stepSize;
4,480✔
272
    }
273
}
35✔
274

275
void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float *x, float *y) const
4,632✔
276
{
277
    iterator = calculateDirection(direction, iterator);
4,632✔
278
    iterator += convertOffset(startOffset + getAttributeValue(StartOffset));
4,632✔
279

280
    if (iterator >= M_PI * 2.0)
4,632✔
281
        iterator -= M_PI * 2.0;
8✔
282

283
    calculatePoint(iterator, x, y);
4,632✔
284
}
4,632✔
285

286
void EFX::rotateAndScale(float *x, float *y) const
4,647✔
287
{
288
    float xx = *x;
4,647✔
289
    float yy = *y;
4,647✔
290
    float w = getAttributeValue(Width);
4,647✔
291
    float h = getAttributeValue(Height);
4,647✔
292
    float fadeScale = 1.0;
293

294
    if (isRunning())
4,647✔
295
    {
296
        uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed();
152✔
297
        if (fadeIn > 0 && elapsed() <= fadeIn)
152✔
298
        {
299
            fadeScale = SCALE(float(elapsed()),
×
300
                              float(0), float(fadeIn),
301
                              float(0), float(1.0));
302
        }
303
    }
304

305
    *x = (getAttributeValue(XOffset)) + xx * m_cosR * (w * fadeScale) + yy * m_sinR * (h * fadeScale);
4,647✔
306
    *y = (getAttributeValue(YOffset)) + -xx * m_sinR * (w * fadeScale) + yy * m_cosR * (h * fadeScale);
4,647✔
307
}
4,647✔
308

309
float EFX::calculateDirection(Function::Direction direction, float iterator) const
4,632✔
310
{
311
    if (direction == this->direction())
4,632✔
312
        return iterator;
313

314
    switch (algorithm())
1,152✔
315
    {
316
    default:
1,024✔
317
    case Circle:
318
    case Eight:
319
    case Line2:
320
    case Diamond:
321
    case Square:
322
    case SquareChoppy:
323
        case SquareTrue:
324
    case Leaf:
325
    case Lissajous:
326
        return (M_PI * 2.0) - iterator;
1,024✔
327
    case Line:
128✔
328
        return (iterator > M_PI) ? (iterator - M_PI) : (iterator + M_PI);
128✔
329
    }
330
}
331

332
// this function should map from 0..M_PI * 2 -> -1..1
333
void EFX::calculatePoint(float iterator, float *x, float *y) const
4,632✔
334
{
335
    switch (algorithm())
4,632✔
336
    {
337
    default:
1,432✔
338
    case Circle:
339
        *x = cos(iterator + M_PI_2);
1,432✔
340
        *y = cos(iterator);
1,432✔
341
        break;
1,432✔
342

343
    case Eight:
384✔
344
        *x = cos((iterator * 2) + M_PI_2);
384✔
345
        *y = cos(iterator);
384✔
346
        break;
384✔
347

348
    case Line:
349
        *x = cos(iterator);
384✔
350
        *y = cos(iterator);
384✔
351
        break;
384✔
352

353
    case Line2:
384✔
354
        *x = iterator / M_PI - 1;
384✔
355
        *y = iterator / M_PI - 1;
384✔
356
        break;
384✔
357

358
    case Diamond:
384✔
359
        *x = pow(cos(iterator - M_PI_2), 3);
384✔
360
        *y = pow(cos(iterator), 3);
384✔
361
        break;
384✔
362

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

386
    case SquareChoppy:
387
        *x = round(cos(iterator));
384✔
388
        *y = round(sin(iterator));
384✔
389
        break;
384✔
390
                
391
        case SquareTrue:
128✔
392
        if (iterator < M_PI / 2)
128✔
393
        {
394
            *x = 1;
32✔
395
            *y = 1;
32✔
396
        }
397
        else if (M_PI / 2 <= iterator && iterator < M_PI)
96✔
398
        {
399
            *x = 1;
33✔
400
            *y = -1;
33✔
401
        }
402
        else if (M_PI <= iterator && iterator < M_PI * 3 / 2)
63✔
403
        {
404
            *x = -1;
32✔
405
            *y = -1;
32✔
406
        }
407
        else // M_PI * 3 / 2 <= iterator
408
        {
409
            *x = -1;
31✔
410
            *y = 1;
31✔
411
        }
412
                break;
413

414
    case Leaf:
384✔
415
        *x = pow(cos(iterator + M_PI_2), 5);
384✔
416
        *y = cos(iterator);
384✔
417
        break;
384✔
418

419
    case Lissajous:
384✔
420
        {
421
            if (m_xFrequency > 0)
384✔
422
                *x = cos((m_xFrequency * iterator) - m_xPhase);
384✔
423
            else
424
            {
425
                float iterator0 = ((iterator + m_xPhase) / M_PI);
×
426
                int fff = iterator0;
×
427
                iterator0 -= (fff - fff % 2);
×
428
                float forward = 1 - floor(iterator0); // 1 when forward
×
429
                float backward = 1 - forward; // 1 when backward
×
430
                iterator0 = iterator0 - floor(iterator0);
×
431
                *x = (forward * iterator0 + backward * (1 - iterator0)) * 2 - 1;
×
432
            }
433
            if (m_yFrequency > 0)
384✔
434
                *y = cos((m_yFrequency * iterator) - m_yPhase);
384✔
435
            else
436
            {
437
                float iterator0 = ((iterator + m_yPhase) / M_PI);
×
438
                int fff = iterator0;
×
439
                iterator0 -= (fff - fff % 2);
×
440
                float forward = 1 - floor(iterator0); // 1 when forward
×
441
                float backward = 1 - forward; // 1 when backward
×
442
                iterator0 = iterator0 - floor(iterator0);
×
443
                *y = (forward * iterator0 + backward * (1 - iterator0)) * 2 - 1;
×
444
            }
445
        }
446
        break;
447
    }
448

449
    rotateAndScale(x, y);
4,632✔
450
}
4,632✔
451

452
/*****************************************************************************
453
 * Width
454
 *****************************************************************************/
455

456
void EFX::setWidth(int width)
28✔
457
{
458
    adjustAttribute(static_cast<double> (CLAMP(width, 0, 127)), Width);
28✔
459
    emit changed(this->id());
28✔
460
}
28✔
461

462
int EFX::width() const
13✔
463
{
464
    return static_cast<int> (attributes().at(Width).m_value);
13✔
465
}
466

467
/*****************************************************************************
468
 * Height
469
 *****************************************************************************/
470

471
void EFX::setHeight(int height)
28✔
472
{
473
    adjustAttribute(static_cast<double> (CLAMP(height, 0, 127)), Height);
28✔
474
    emit changed(this->id());
28✔
475
}
28✔
476

477
int EFX::height() const
13✔
478
{
479
    return static_cast<int> (attributes().at(Height).m_value);
13✔
480
}
481

482
/*****************************************************************************
483
 * Rotation
484
 *****************************************************************************/
485

486
void EFX::setRotation(int rot)
27✔
487
{
488
    adjustAttribute(CLAMP(rot, 0, 359), Rotation);
27✔
489
    updateRotationCache();
27✔
490
    emit changed(this->id());
27✔
491
}
27✔
492

493
int EFX::rotation() const
13✔
494
{
495
    return static_cast<int> (attributes().at(Rotation).m_value);
13✔
496
}
497

498
void EFX::updateRotationCache()
206✔
499
{
500
    double r = M_PI/180 * getAttributeValue(Rotation);
206✔
501
    m_cosR = cos(r);
206✔
502
    m_sinR = sin(r);
206✔
503
}
206✔
504

505
/*****************************************************************************
506
 * Start Offset
507
 *****************************************************************************/
508

509
void EFX::setStartOffset(int startOffset)
1✔
510
{
511
    adjustAttribute(CLAMP(startOffset, 0, 359), StartOffset);
1✔
512
    emit changed(this->id());
1✔
513
}
1✔
514

515
int EFX::startOffset() const
2✔
516
{
517
    return static_cast<int> (attributes().at(StartOffset).m_value);
2✔
518
}
519

520
float EFX::convertOffset(int offset) const
4,632✔
521
{
522
    return M_PI/180 * (offset % 360);
4,632✔
523
}
524

525
/*****************************************************************************
526
 * Is Relative
527
 *****************************************************************************/
528

529
void EFX::setIsRelative(bool isRelative)
1✔
530
{
531
    m_isRelative = isRelative;
1✔
532
    emit changed(this->id());
1✔
533
}
1✔
534

535
bool EFX::isRelative() const
312✔
536
{
537
    return m_isRelative;
312✔
538
}
539

540
/*****************************************************************************
541
 * Offset
542
 *****************************************************************************/
543

544
void EFX::setXOffset(int offset)
29✔
545
{
546
    adjustAttribute(static_cast<double> (CLAMP(offset, 0, (int)UCHAR_MAX)), XOffset);
29✔
547
    emit changed(this->id());
29✔
548
}
29✔
549

550
int EFX::xOffset() const
14✔
551
{
552
    return static_cast<int> (attributes().at(XOffset).m_value);
14✔
553
}
554

555
void EFX::setYOffset(int offset)
29✔
556
{
557
    adjustAttribute(static_cast<double> (CLAMP(offset, 0, (int)UCHAR_MAX)), YOffset);
29✔
558
    emit changed(this->id());
29✔
559
}
29✔
560

561
int EFX::yOffset() const
13✔
562
{
563
    return static_cast<int> (attributes().at(YOffset).m_value);
13✔
564
}
565

566
/*****************************************************************************
567
 * Frequency
568
 *****************************************************************************/
569

570
void EFX::setXFrequency(int freq)
18✔
571
{
572
    m_xFrequency = static_cast<float> (CLAMP(freq, 0, 32));
18✔
573
    emit changed(this->id());
18✔
574
}
18✔
575

576
int EFX::xFrequency() const
19✔
577
{
578
    return static_cast<int> (m_xFrequency);
19✔
579
}
580

581
void EFX::setYFrequency(int freq)
18✔
582
{
583
    m_yFrequency = static_cast<float> (CLAMP(freq, 0, 32));
18✔
584
    emit changed(this->id());
18✔
585
}
18✔
586

587
int EFX::yFrequency() const
18✔
588
{
589
    return static_cast<int> (m_yFrequency);
18✔
590
}
591

592
bool EFX::isFrequencyEnabled()
5✔
593
{
594
    if (m_algorithm == EFX::Lissajous)
5✔
595
        return true;
596
    else
597
        return false;
3✔
598
}
599

600
/*****************************************************************************
601
 * Phase
602
 *****************************************************************************/
603

604
void EFX::setXPhase(int phase)
20✔
605
{
606
    m_xPhase = static_cast<float> (CLAMP(phase, 0, 359)) * M_PI / 180.0;
20✔
607
    emit changed(this->id());
20✔
608
}
20✔
609

610
int EFX::xPhase() const
21✔
611
{
612
    return static_cast<int> (floor((m_xPhase * 180.0 / M_PI) + 0.5));
21✔
613
}
614

615
void EFX::setYPhase(int phase)
20✔
616
{
617
    m_yPhase = static_cast<float> (CLAMP(phase, 0, 359)) * M_PI / 180.0;
20✔
618
    emit changed(this->id());
20✔
619
}
20✔
620

621
int EFX::yPhase() const
20✔
622
{
623
    return static_cast<int> (floor((m_yPhase * 180.0 / M_PI) + 0.5));
20✔
624
}
625

626
bool EFX::isPhaseEnabled() const
5✔
627
{
628
    if (m_algorithm == EFX::Lissajous)
5✔
629
        return true;
630
    else
631
        return false;
3✔
632
}
633

634
/*****************************************************************************
635
 * Fixtures
636
 *****************************************************************************/
637

638
bool EFX::addFixture(EFXFixture* ef)
32✔
639
{
640
    Q_ASSERT(ef != NULL);
641

642
    /* Search for an existing fixture with the same ID and append at last but do
643
     * not prevent multiple entries because a fixture can have multiple efx. */
644
    //! @todo Prevent multiple entries using head & mode
645
    int i;
646
    for (i = 0; i < m_fixtures.size (); i++)
60✔
647
    {
648
        if (m_fixtures[i]->head() == ef->head())
28✔
649
        {
650
            m_fixtures.insert(i, ef);
×
651
            break;
×
652
        }
653
    }
654

655
    /* If not inserted, put the EFXFixture object into our list */
656
    if (i >= m_fixtures.size())
32✔
657
        m_fixtures.append(ef);
32✔
658

659
    emit changed(this->id());
32✔
660

661
    return true;
32✔
662
}
663

664
bool EFX::addFixture(quint32 fxi, int head)
×
665
{
666
    EFXFixture *ef = new EFXFixture(this);
×
667
    GroupHead gHead(fxi, head);
×
668
    ef->setHead(gHead);
×
669

670
    return addFixture(ef);
×
671
}
×
672

673
bool EFX::removeFixture(EFXFixture* ef)
2✔
674
{
675
    Q_ASSERT(ef != NULL);
676

677
    if (m_fixtures.removeAll(ef) > 0)
2✔
678
    {
679
        emit changed(this->id());
1✔
680
        return true;
1✔
681
    }
682
    else
683
    {
684
        return false;
685
    }
686
}
687

688
bool EFX::removeFixture(quint32 fxi, int head)
2✔
689
{
690
    for (int i = 0; i < m_fixtures.count(); i++)
2✔
691
    {
692
        EFXFixture *ef = m_fixtures.at(i);
1✔
693
        if (ef->head().fxi == fxi && ef->head().head == head)
1✔
694
        {
695
            m_fixtures.removeAt(i);
1✔
696
            return true;
1✔
697
        }
698
    }
699

700
    return false;
701
}
702

703
void EFX::removeAllFixtures()
×
704
{
705
    m_fixtures.clear();
×
706
    emit changed(this->id());
×
707
}
×
708

709
bool EFX::raiseFixture(EFXFixture* ef)
3✔
710
{
711
    Q_ASSERT(ef != NULL);
712

713
    int index = m_fixtures.indexOf(ef);
3✔
714
    if (index > 0)
3✔
715
    {
716
        m_fixtures.move(index, index - 1);
1✔
717
        emit changed(this->id());
1✔
718
        return true;
1✔
719
    }
720
    else
721
    {
722
        return false;
723
    }
724
}
725

726
bool EFX::lowerFixture(EFXFixture* ef)
3✔
727
{
728
    int index = m_fixtures.indexOf(ef);
3✔
729
    if (index < (m_fixtures.count() - 1))
3✔
730
    {
731
        m_fixtures.move(index, index + 1);
1✔
732
        emit changed(this->id());
1✔
733
        return true;
1✔
734
    }
735
    else
736
    {
737
        return false;
738
    }
739
}
740

741
const QList <EFXFixture*> EFX::fixtures() const
80✔
742
{
743
    return m_fixtures;
80✔
744
}
745

746
EFXFixture *EFX::fixture(quint32 id, int headIndex)
4✔
747
{
748
    foreach (EFXFixture *ef, m_fixtures)
10✔
749
    {
750
        if (ef->head().fxi == id && ef->head().head == headIndex)
9✔
751
            return ef;
752
    }
753

754
    return NULL;
1✔
755
}
756

757
QList<quint32> EFX::components()
×
758
{
759
    QList<quint32> ids;
760

761
    foreach (EFXFixture *ef, m_fixtures)
×
762
    {
763
        if (ids.contains(ef->head().fxi) == false)
×
764
            ids.append(ef->head().fxi);
×
765
    }
766

767
    return ids;
×
768
}
×
769

770
void EFX::slotFixtureRemoved(quint32 fxi_id)
5✔
771
{
772
    /* Remove the destroyed fixture from our list */
773
    QMutableListIterator <EFXFixture*> it(m_fixtures);
5✔
774
    while (it.hasNext() == true)
11✔
775
    {
776
        it.next();
777

778
        if (it.value()->head().fxi == fxi_id)
9✔
779
        {
780
            delete it.value();
3✔
781
            it.remove();
3✔
782
            break;
783
        }
784
    }
785
}
5✔
786

787
/*****************************************************************************
788
 * Fixture propagation mode
789
 *****************************************************************************/
790

791
void EFX::setPropagationMode(PropagationMode mode)
9✔
792
{
793
    m_propagationMode = mode;
9✔
794
    emit changed(this->id());
9✔
795
}
9✔
796

797
EFX::PropagationMode EFX::propagationMode() const
471✔
798
{
799
    return m_propagationMode;
471✔
800
}
801

802
QString EFX::propagationModeToString(PropagationMode mode)
6✔
803
{
804
    if (mode == Serial)
6✔
805
        return QString(KXMLQLCEFXPropagationModeSerial);
2✔
806
    else if (mode == Asymmetric)
4✔
807
        return QString(KXMLQLCEFXPropagationModeAsymmetric);
1✔
808
    else
809
        return QString(KXMLQLCEFXPropagationModeParallel);
3✔
810
}
811

812
EFX::PropagationMode EFX::stringToPropagationMode(QString str)
7✔
813
{
814
    if (str == QString(KXMLQLCEFXPropagationModeSerial))
7✔
815
        return Serial;
816
    else if (str == QString(KXMLQLCEFXPropagationModeAsymmetric))
3✔
817
        return Asymmetric;
818
    else
819
        return Parallel;
2✔
820
}
821

822
/*****************************************************************************
823
 * Load & Save
824
 *****************************************************************************/
825

826
bool EFX::saveXML(QXmlStreamWriter *doc)
2✔
827
{
828
    Q_ASSERT(doc != NULL);
829

830
    /* Function tag */
831
    doc->writeStartElement(KXMLQLCFunction);
2✔
832

833
    /* Common attributes */
834
    saveXMLCommon(doc);
2✔
835

836
    /* Fixtures */
837
    QListIterator <EFXFixture*> it(m_fixtures);
2✔
838
    while (it.hasNext() == true)
5✔
839
        it.next()->saveXML(doc);
3✔
840

841
    /* Propagation mode */
842
    doc->writeTextElement(KXMLQLCEFXPropagationMode, propagationModeToString(m_propagationMode));
2✔
843

844
    /* Speeds */
845
    saveXMLSpeed(doc);
2✔
846
    /* Direction */
847
    saveXMLDirection(doc);
2✔
848
    /* Run order */
849
    saveXMLRunOrder(doc);
2✔
850

851
    /* Algorithm */
852
    doc->writeTextElement(KXMLQLCEFXAlgorithm, algorithmToString(algorithm()));
2✔
853
    /* Width */
854
    doc->writeTextElement(KXMLQLCEFXWidth, QString::number(width()));
2✔
855
    /* Height */
856
    doc->writeTextElement(KXMLQLCEFXHeight, QString::number(height()));
2✔
857
    /* Rotation */
858
    doc->writeTextElement(KXMLQLCEFXRotation, QString::number(rotation()));
2✔
859
    /* StartOffset */
860
    doc->writeTextElement(KXMLQLCEFXStartOffset, QString::number(startOffset()));
2✔
861
    /* IsRelative */
862
    doc->writeTextElement(KXMLQLCEFXIsRelative, QString::number(isRelative() ? 1 : 0));
4✔
863

864
    /********************************************
865
     * X-Axis
866
     ********************************************/
867
    doc->writeStartElement(KXMLQLCEFXAxis);
2✔
868
    doc->writeAttribute(KXMLQLCFunctionName, KXMLQLCEFXX);
2✔
869

870
    /* Offset */
871
    doc->writeTextElement(KXMLQLCEFXOffset, QString::number(xOffset()));
2✔
872
    /* Frequency */
873
    doc->writeTextElement(KXMLQLCEFXFrequency, QString::number(xFrequency()));
2✔
874
    /* Phase */
875
    doc->writeTextElement(KXMLQLCEFXPhase, QString::number(xPhase()));
2✔
876

877
    /* End the (X) <Axis> tag */
878
    doc->writeEndElement();
2✔
879

880
    /********************************************
881
     * Y-Axis
882
     ********************************************/
883
    doc->writeStartElement(KXMLQLCEFXAxis);
2✔
884
    doc->writeAttribute(KXMLQLCFunctionName, KXMLQLCEFXY);
2✔
885

886
    /* Offset */
887
    doc->writeTextElement(KXMLQLCEFXOffset, QString::number(yOffset()));
2✔
888
    /* Frequency */
889
    doc->writeTextElement(KXMLQLCEFXFrequency, QString::number(yFrequency()));
2✔
890
    /* Phase */
891
    doc->writeTextElement(KXMLQLCEFXPhase, QString::number(yPhase()));
2✔
892

893
    /* End the (Y) <Axis> tag */
894
    doc->writeEndElement();
2✔
895

896
    /* End the <Function> tag */
897
    doc->writeEndElement();
2✔
898

899
    return true;
2✔
900
}
901

902
bool EFX::loadXML(QXmlStreamReader &root)
5✔
903
{
904
    if (root.name() != KXMLQLCFunction)
10✔
905
    {
906
        qWarning() << "Function node not found!";
1✔
907
        return false;
1✔
908
    }
909

910
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::EFXType))
8✔
911
    {
912
        qWarning("Function is not an EFX!");
1✔
913
        return false;
1✔
914
    }
915

916
    /* Load EFX contents */
917
    while (root.readNextStartElement())
45✔
918
    {
919
        if (root.name() == KXMLQLCBus)
84✔
920
        {
921
            /* Bus */
922
            QString str = root.attributes().value(KXMLQLCBusRole).toString();
3✔
923
            if (str == KXMLQLCBusFade)
3✔
924
                m_legacyFadeBus = root.readElementText().toUInt();
2✔
925
            else if (str == KXMLQLCBusHold)
1✔
926
                m_legacyHoldBus = root.readElementText().toUInt();
1✔
927
        }
3✔
928
        else if (root.name() == KXMLQLCFunctionSpeed)
78✔
929
        {
930
            loadXMLSpeed(root);
1✔
931
        }
932
        else if (root.name() == KXMLQLCEFXFixture)
76✔
933
        {
934
            EFXFixture* ef = new EFXFixture(this);
9✔
935
            ef->loadXML(root);
9✔
936
            if (ef->head().isValid())
9✔
937
            {
938
                if (addFixture(ef) == false)
9✔
939
                    delete ef;
×
940
            }
941
        }
942
        else if (root.name() == KXMLQLCEFXPropagationMode)
58✔
943
        {
944
            /* Propagation mode */
945
            setPropagationMode(stringToPropagationMode(root.readElementText()));
3✔
946
        }
947
        else if (root.name() == KXMLQLCEFXAlgorithm)
52✔
948
        {
949
            /* Algorithm */
950
            setAlgorithm(stringToAlgorithm(root.readElementText()));
3✔
951
        }
952
        else if (root.name() == KXMLQLCFunctionDirection)
46✔
953
        {
954
            loadXMLDirection(root);
3✔
955
        }
956
        else if (root.name() == KXMLQLCFunctionRunOrder)
40✔
957
        {
958
            loadXMLRunOrder(root);
3✔
959
        }
960
        else if (root.name() == KXMLQLCEFXWidth)
34✔
961
        {
962
            /* Width */
963
            setWidth(root.readElementText().toInt());
3✔
964
        }
965
        else if (root.name() == KXMLQLCEFXHeight)
28✔
966
        {
967
            /* Height */
968
            setHeight(root.readElementText().toInt());
3✔
969
        }
970
        else if (root.name() == KXMLQLCEFXRotation)
22✔
971
        {
972
            /* Rotation */
973
            setRotation(root.readElementText().toInt());
3✔
974
        }
975
        else if (root.name() == KXMLQLCEFXStartOffset)
16✔
976
        {
977
            /* StartOffset */
978
            setStartOffset(root.readElementText().toInt());
×
979
        }
980
        else if (root.name() == KXMLQLCEFXIsRelative)
16✔
981
        {
982
            /* IsRelative */
983
            setIsRelative(root.readElementText().toInt() != 0);
×
984
        }
985
        else if (root.name() == KXMLQLCEFXAxis)
16✔
986
        {
987
            /* Axes */
988
            loadXMLAxis(root);
6✔
989
        }
990
        else
991
        {
992
            qWarning() << "Unknown EFX tag:" << root.name();
2✔
993
            root.skipCurrentElement();
2✔
994
        }
995
    }
996

997
    return true;
998
}
999

1000
bool EFX::loadXMLAxis(QXmlStreamReader &root)
10✔
1001
{
1002
    int frequency = 0;
1003
    int offset = 0;
1004
    int phase = 0;
1005
    QString axis;
1006

1007
    if (root.name() != KXMLQLCEFXAxis)
20✔
1008
    {
1009
        qWarning() << "EFX axis node not found!";
1✔
1010
        return false;
1✔
1011
    }
1012

1013
    /* Get the axis name */
1014
    axis = root.attributes().value(KXMLQLCFunctionName).toString();
9✔
1015

1016
    /* Load axis contents */
1017
    while (root.readNextStartElement())
37✔
1018
    {
1019
        if (root.name() == KXMLQLCEFXOffset)
56✔
1020
            offset = root.readElementText().toInt();
9✔
1021
        else if (root.name() == KXMLQLCEFXFrequency)
38✔
1022
            frequency = root.readElementText().toInt();
9✔
1023
        else if (root.name() == KXMLQLCEFXPhase)
20✔
1024
            phase = root.readElementText().toInt();
9✔
1025
        else
1026
        {
1027
            qWarning() << "Unknown EFX axis tag:" << root.name();
1✔
1028
            root.skipCurrentElement();
1✔
1029
        }
1030
    }
1031

1032
    if (axis == KXMLQLCEFXY)
9✔
1033
    {
1034
        setYOffset(offset);
4✔
1035
        setYFrequency(frequency);
4✔
1036
        setYPhase(phase);
4✔
1037
        return true;
1038
    }
1039
    else if (axis == KXMLQLCEFXX)
5✔
1040
    {
1041
        setXOffset(offset);
4✔
1042
        setXFrequency(frequency);
4✔
1043
        setXPhase(phase);
4✔
1044
        return true;
1045
    }
1046
    else
1047
    {
1048
        qWarning() << "Unknown EFX axis:" << axis;
1✔
1049
        return false;
1✔
1050
    }
1051
}
10✔
1052

1053
void EFX::postLoad()
1✔
1054
{
1055
    // Map legacy bus speeds to fixed speed values
1056
    if (m_legacyFadeBus != Bus::invalid())
1✔
1057
    {
1058
        quint32 value = Bus::instance()->value(m_legacyFadeBus);
1✔
1059
        setFadeInSpeed((value / MasterTimer::frequency()) * 1000);
1✔
1060
        setFadeOutSpeed((value / MasterTimer::frequency()) * 1000);
1✔
1061
    }
1062

1063
    if (m_legacyHoldBus != Bus::invalid())
1✔
1064
    {
1065
        quint32 value = Bus::instance()->value(m_legacyHoldBus);
1✔
1066
        setDuration((value / MasterTimer::frequency()) * 1000);
1✔
1067
    }
1068
}
1✔
1069

1070
/*****************************************************************************
1071
 * Running
1072
 *****************************************************************************/
1073
QSharedPointer<GenericFader> EFX::getFader(QList<Universe *> universes, quint32 universeID)
×
1074
{
1075
    // get the universe Fader first. If doesn't exist, create it
1076
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
×
1077
    if (fader.isNull())
×
1078
    {
1079
        fader = universes[universeID]->requestFader();
×
1080
        fader->adjustIntensity(getAttributeValue(Intensity));
×
1081
        fader->setBlendMode(blendMode());
×
1082
        fader->setName(name());
×
1083
        fader->setParentFunctionID(id());
×
1084
        fader->setHandleSecondary(true);
×
1085
        m_fadersMap[universeID] = fader;
×
1086
    }
1087

1088
    return fader;
×
1089
}
1090

1091
void EFX::preRun(MasterTimer* timer)
5✔
1092
{
1093
    int serialNumber = 0;
1094

1095
    QListIterator <EFXFixture*> it(m_fixtures);
5✔
1096
    while (it.hasNext() == true)
10✔
1097
    {
1098
        EFXFixture *ef = it.next();
5✔
1099
        Q_ASSERT(ef != NULL);
1100
        ef->setSerialNumber(serialNumber++);
5✔
1101
    }
1102

1103
    Function::preRun(timer);
5✔
1104
}
5✔
1105

1106
void EFX::write(MasterTimer *timer, QList<Universe*> universes)
×
1107
{
1108
    Q_UNUSED(timer);
1109

1110
    if (isPaused())
×
1111
        return;
×
1112

1113
    int done = 0;
1114

1115
    QListIterator <EFXFixture*> it(m_fixtures);
×
1116
    while (it.hasNext() == true)
×
1117
    {
1118
        EFXFixture *ef = it.next();
×
1119
        if (ef->isDone() == false)
×
1120
        {
1121
            QSharedPointer<GenericFader> fader = getFader(universes, ef->universe());
×
1122
            ef->nextStep(universes, fader);
×
1123
        }
1124
        else
1125
        {
1126
            done++;
×
1127
        }
1128
    }
1129

1130
    incrementElapsed();
×
1131

1132
    /* Check for stop condition */
1133
    if (done == m_fixtures.count())
×
1134
        stop(FunctionParent::master());
×
1135
}
1136

1137
void EFX::postRun(MasterTimer *timer, QList<Universe *> universes)
5✔
1138
{
1139
    /* Reset all fixtures */
1140
    QListIterator <EFXFixture*> it(m_fixtures);
5✔
1141
    while (it.hasNext() == true)
10✔
1142
    {
1143
        EFXFixture* ef(it.next());
5✔
1144

1145
        /* Run the EFX's stop scene for Loop & PingPong modes */
1146
        if (runOrder() != SingleShot)
5✔
1147
            ef->stop();
4✔
1148
        ef->reset();
5✔
1149
    }
1150

1151
    dismissAllFaders();
5✔
1152

1153
    Function::postRun(timer, universes);
5✔
1154
}
5✔
1155

1156
/*****************************************************************************
1157
 * Intensity
1158
 *****************************************************************************/
1159

1160
int EFX::adjustAttribute(qreal fraction, int attributeId)
165✔
1161
{
1162
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
165✔
1163

1164
    switch (attrIndex)
165✔
1165
    {
1166
        case Intensity:
2✔
1167
        {
1168
            foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
4✔
1169
            {
1170
                if (!fader.isNull())
×
1171
                    fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
1172
            }
1173
        }
1174
        break;
2✔
1175

1176
        case Height:
102✔
1177
        case Width:
1178
        case XOffset:
1179
        case YOffset:
1180
        case Rotation:
1181
            updateRotationCache();
102✔
1182
        break;
102✔
1183
    }
1184

1185
    return attrIndex;
165✔
1186
}
1187

1188
/*************************************************************************
1189
 * Blend mode
1190
 *************************************************************************/
1191

1192
void EFX::setBlendMode(Universe::BlendMode mode)
1✔
1193
{
1194
    if (mode == blendMode())
1✔
1195
        return;
1196

1197
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
1198
    {
1199
        if (!fader.isNull())
×
1200
            fader->setBlendMode(mode);
×
1201
    }
1202

1203
    Function::setBlendMode(mode);
×
1204
}
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

© 2025 Coveralls, Inc