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

mcallegari / qlcplus / 14201252843

01 Apr 2025 04:43PM UTC coverage: 31.884% (-0.003%) from 31.887%
14201252843

push

github

web-flow
Merge pull request #1700 from 8-Lambda-8/efx_preview

EFX preview improvements

5 of 21 new or added lines in 3 files covered. (23.81%)

6 existing lines in 3 files now uncovered.

14705 of 46121 relevant lines covered (31.88%)

26423.33 hits per line

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

86.02
/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
22,698✔
156
{
157
    return m_algorithm;
22,698✔
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
    {
251
        int propagationOffset =
NEW
252
            (m_propagationMode == EFX::Asymmetric || m_propagationMode == EFX::Serial)
×
NEW
253
                ? 360 / m_fixtures.size() * i
×
254
                : 0;
255

NEW
256
        preview(polygons[i], m_fixtures[i]->m_direction,
×
NEW
257
                m_fixtures[i]->m_startOffset + propagationOffset);
×
258
    }
UNCOV
259
}
×
260

261
void EFX::preview(QPolygonF &polygon, Function::Direction direction, int startOffset) const
35✔
262
{
263
    float stepCount = 512.0;
264
    int step = 0;
265
    float stepSize = 1.0 / (stepCount / (M_PI * 2.0));
266

267
    float i = 0;
268
    float x = 0;
35✔
269
    float y = 0;
35✔
270

271
    /* Reset the polygon to fill it with new values */
272
    polygon.clear();
35✔
273

274
    /* Draw a preview of the effect */
275
    for (step = 0; step < stepCount; step++)
17,955✔
276
    {
277
        calculatePoint(direction, startOffset, i, &x, &y);
17,920✔
278
        polygon << QPointF(x, y);
17,920✔
279
        i += stepSize;
17,920✔
280
    }
281
}
35✔
282

283
void EFX::calculatePoint(Function::Direction direction, int startOffset, float iterator, float *x, float *y) const
18,072✔
284
{
285
    iterator = calculateDirection(direction, iterator);
18,072✔
286
    iterator += convertOffset(startOffset + getAttributeValue(StartOffset));
18,072✔
287

288
    if (iterator >= M_PI * 2.0)
18,072✔
289
        iterator -= M_PI * 2.0;
8✔
290

291
    calculatePoint(iterator, x, y);
18,072✔
292
}
18,072✔
293

294
void EFX::rotateAndScale(float *x, float *y) const
18,087✔
295
{
296
    float xx = *x;
18,087✔
297
    float yy = *y;
18,087✔
298
    float w = getAttributeValue(Width);
18,087✔
299
    float h = getAttributeValue(Height);
18,087✔
300
    float fadeScale = 1.0;
301

302
    if (isRunning())
18,087✔
303
    {
304
        uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed();
152✔
305
        if (fadeIn > 0 && elapsed() <= fadeIn)
152✔
306
        {
307
            fadeScale = SCALE(float(elapsed()),
×
308
                              float(0), float(fadeIn),
309
                              float(0), float(1.0));
310
        }
311
    }
312

313
    *x = (getAttributeValue(XOffset)) + xx * m_cosR * (w * fadeScale) + yy * m_sinR * (h * fadeScale);
18,087✔
314
    *y = (getAttributeValue(YOffset)) + -xx * m_sinR * (w * fadeScale) + yy * m_cosR * (h * fadeScale);
18,087✔
315
}
18,087✔
316

317
float EFX::calculateDirection(Function::Direction direction, float iterator) const
18,072✔
318
{
319
    if (direction == this->direction())
18,072✔
320
        return iterator;
321

322
    switch (algorithm())
4,608✔
323
    {
324
    default:
4,096✔
325
    case Circle:
326
    case Eight:
327
    case Line2:
328
    case Diamond:
329
    case Square:
330
    case SquareChoppy:
331
        case SquareTrue:
332
    case Leaf:
333
    case Lissajous:
334
        return (M_PI * 2.0) - iterator;
4,096✔
335
    case Line:
512✔
336
        return (iterator > M_PI) ? (iterator - M_PI) : (iterator + M_PI);
512✔
337
    }
338
}
339

340
// this function should map from 0..M_PI * 2 -> -1..1
341
void EFX::calculatePoint(float iterator, float *x, float *y) const
18,072✔
342
{
343
    switch (algorithm())
18,072✔
344
    {
345
    default:
5,272✔
346
    case Circle:
347
        *x = cos(iterator + M_PI_2);
5,272✔
348
        *y = cos(iterator);
5,272✔
349
        break;
5,272✔
350

351
    case Eight:
1,536✔
352
        *x = cos((iterator * 2) + M_PI_2);
1,536✔
353
        *y = cos(iterator);
1,536✔
354
        break;
1,536✔
355

356
    case Line:
357
        *x = cos(iterator);
1,536✔
358
        *y = cos(iterator);
1,536✔
359
        break;
1,536✔
360

361
    case Line2:
1,536✔
362
        *x = iterator / M_PI - 1;
1,536✔
363
        *y = iterator / M_PI - 1;
1,536✔
364
        break;
1,536✔
365

366
    case Diamond:
1,536✔
367
        *x = pow(cos(iterator - M_PI_2), 3);
1,536✔
368
        *y = pow(cos(iterator), 3);
1,536✔
369
        break;
1,536✔
370

371
    case Square:
1,536✔
372
        if (iterator < M_PI / 2)
1,536✔
373
        {
374
            *x = (iterator * 2 / M_PI) * 2 - 1;
385✔
375
            *y = 1;
385✔
376
        }
377
        else if (M_PI / 2 <= iterator && iterator < M_PI)
1,151✔
378
        {
379
            *x = 1;
384✔
380
            *y = (1 - (iterator - M_PI / 2) * 2 / M_PI) * 2 - 1;
384✔
381
        }
382
        else if (M_PI <= iterator && iterator < M_PI * 3 / 2)
767✔
383
        {
384
            *x = (1 - (iterator - M_PI) * 2 / M_PI) * 2 - 1;
384✔
385
            *y = -1;
384✔
386
        }
387
        else // M_PI * 3 / 2 <= iterator
388
        {
389
            *x = -1;
383✔
390
            *y = ((iterator - M_PI * 3 / 2) * 2 / M_PI) * 2 - 1;
383✔
391
        }
392
        break;
393

394
    case SquareChoppy:
395
        *x = round(cos(iterator));
1,536✔
396
        *y = round(sin(iterator));
1,536✔
397
        break;
1,536✔
398
                
399
        case SquareTrue:
512✔
400
        if (iterator < M_PI / 2)
512✔
401
        {
402
            *x = 1;
128✔
403
            *y = 1;
128✔
404
        }
405
        else if (M_PI / 2 <= iterator && iterator < M_PI)
384✔
406
        {
407
            *x = 1;
128✔
408
            *y = -1;
128✔
409
        }
410
        else if (M_PI <= iterator && iterator < M_PI * 3 / 2)
256✔
411
        {
412
            *x = -1;
128✔
413
            *y = -1;
128✔
414
        }
415
        else // M_PI * 3 / 2 <= iterator
416
        {
417
            *x = -1;
128✔
418
            *y = 1;
128✔
419
        }
420
                break;
421

422
    case Leaf:
1,536✔
423
        *x = pow(cos(iterator + M_PI_2), 5);
1,536✔
424
        *y = cos(iterator);
1,536✔
425
        break;
1,536✔
426

427
    case Lissajous:
1,536✔
428
        {
429
            if (m_xFrequency > 0)
1,536✔
430
                *x = cos((m_xFrequency * iterator) - m_xPhase);
1,536✔
431
            else
432
            {
433
                float iterator0 = ((iterator + m_xPhase) / M_PI);
×
434
                int fff = iterator0;
×
435
                iterator0 -= (fff - fff % 2);
×
436
                float forward = 1 - floor(iterator0); // 1 when forward
×
437
                float backward = 1 - forward; // 1 when backward
×
438
                iterator0 = iterator0 - floor(iterator0);
×
439
                *x = (forward * iterator0 + backward * (1 - iterator0)) * 2 - 1;
×
440
            }
441
            if (m_yFrequency > 0)
1,536✔
442
                *y = cos((m_yFrequency * iterator) - m_yPhase);
1,536✔
443
            else
444
            {
445
                float iterator0 = ((iterator + m_yPhase) / M_PI);
×
446
                int fff = iterator0;
×
447
                iterator0 -= (fff - fff % 2);
×
448
                float forward = 1 - floor(iterator0); // 1 when forward
×
449
                float backward = 1 - forward; // 1 when backward
×
450
                iterator0 = iterator0 - floor(iterator0);
×
451
                *y = (forward * iterator0 + backward * (1 - iterator0)) * 2 - 1;
×
452
            }
453
        }
454
        break;
455
    }
456

457
    rotateAndScale(x, y);
18,072✔
458
}
18,072✔
459

460
/*****************************************************************************
461
 * Width
462
 *****************************************************************************/
463

464
void EFX::setWidth(int width)
28✔
465
{
466
    adjustAttribute(static_cast<double> (CLAMP(width, 0, 127)), Width);
28✔
467
    emit changed(this->id());
28✔
468
}
28✔
469

470
int EFX::width() const
13✔
471
{
472
    return static_cast<int> (attributes().at(Width).m_value);
13✔
473
}
474

475
/*****************************************************************************
476
 * Height
477
 *****************************************************************************/
478

479
void EFX::setHeight(int height)
28✔
480
{
481
    adjustAttribute(static_cast<double> (CLAMP(height, 0, 127)), Height);
28✔
482
    emit changed(this->id());
28✔
483
}
28✔
484

485
int EFX::height() const
13✔
486
{
487
    return static_cast<int> (attributes().at(Height).m_value);
13✔
488
}
489

490
/*****************************************************************************
491
 * Rotation
492
 *****************************************************************************/
493

494
void EFX::setRotation(int rot)
27✔
495
{
496
    adjustAttribute(CLAMP(rot, 0, 359), Rotation);
27✔
497
    updateRotationCache();
27✔
498
    emit changed(this->id());
27✔
499
}
27✔
500

501
int EFX::rotation() const
13✔
502
{
503
    return static_cast<int> (attributes().at(Rotation).m_value);
13✔
504
}
505

506
void EFX::updateRotationCache()
206✔
507
{
508
    double r = M_PI/180 * getAttributeValue(Rotation);
206✔
509
    m_cosR = cos(r);
206✔
510
    m_sinR = sin(r);
206✔
511
}
206✔
512

513
/*****************************************************************************
514
 * Start Offset
515
 *****************************************************************************/
516

517
void EFX::setStartOffset(int startOffset)
1✔
518
{
519
    adjustAttribute(CLAMP(startOffset, 0, 359), StartOffset);
1✔
520
    emit changed(this->id());
1✔
521
}
1✔
522

523
int EFX::startOffset() const
2✔
524
{
525
    return static_cast<int> (attributes().at(StartOffset).m_value);
2✔
526
}
527

528
float EFX::convertOffset(int offset) const
18,072✔
529
{
530
    return M_PI/180 * (offset % 360);
18,072✔
531
}
532

533
/*****************************************************************************
534
 * Is Relative
535
 *****************************************************************************/
536

537
void EFX::setIsRelative(bool isRelative)
1✔
538
{
539
    m_isRelative = isRelative;
1✔
540
    emit changed(this->id());
1✔
541
}
1✔
542

543
bool EFX::isRelative() const
312✔
544
{
545
    return m_isRelative;
312✔
546
}
547

548
/*****************************************************************************
549
 * Offset
550
 *****************************************************************************/
551

552
void EFX::setXOffset(int offset)
29✔
553
{
554
    adjustAttribute(static_cast<double> (CLAMP(offset, 0, (int)UCHAR_MAX)), XOffset);
29✔
555
    emit changed(this->id());
29✔
556
}
29✔
557

558
int EFX::xOffset() const
14✔
559
{
560
    return static_cast<int> (attributes().at(XOffset).m_value);
14✔
561
}
562

563
void EFX::setYOffset(int offset)
29✔
564
{
565
    adjustAttribute(static_cast<double> (CLAMP(offset, 0, (int)UCHAR_MAX)), YOffset);
29✔
566
    emit changed(this->id());
29✔
567
}
29✔
568

569
int EFX::yOffset() const
13✔
570
{
571
    return static_cast<int> (attributes().at(YOffset).m_value);
13✔
572
}
573

574
/*****************************************************************************
575
 * Frequency
576
 *****************************************************************************/
577

578
void EFX::setXFrequency(int freq)
18✔
579
{
580
    m_xFrequency = static_cast<float> (CLAMP(freq, 0, 32));
18✔
581
    emit changed(this->id());
18✔
582
}
18✔
583

584
int EFX::xFrequency() const
19✔
585
{
586
    return static_cast<int> (m_xFrequency);
19✔
587
}
588

589
void EFX::setYFrequency(int freq)
18✔
590
{
591
    m_yFrequency = static_cast<float> (CLAMP(freq, 0, 32));
18✔
592
    emit changed(this->id());
18✔
593
}
18✔
594

595
int EFX::yFrequency() const
18✔
596
{
597
    return static_cast<int> (m_yFrequency);
18✔
598
}
599

600
bool EFX::isFrequencyEnabled()
5✔
601
{
602
    if (m_algorithm == EFX::Lissajous)
5✔
603
        return true;
604
    else
605
        return false;
3✔
606
}
607

608
/*****************************************************************************
609
 * Phase
610
 *****************************************************************************/
611

612
void EFX::setXPhase(int phase)
20✔
613
{
614
    m_xPhase = static_cast<float> (CLAMP(phase, 0, 359)) * M_PI / 180.0;
20✔
615
    emit changed(this->id());
20✔
616
}
20✔
617

618
int EFX::xPhase() const
21✔
619
{
620
    return static_cast<int> (floor((m_xPhase * 180.0 / M_PI) + 0.5));
21✔
621
}
622

623
void EFX::setYPhase(int phase)
20✔
624
{
625
    m_yPhase = static_cast<float> (CLAMP(phase, 0, 359)) * M_PI / 180.0;
20✔
626
    emit changed(this->id());
20✔
627
}
20✔
628

629
int EFX::yPhase() const
20✔
630
{
631
    return static_cast<int> (floor((m_yPhase * 180.0 / M_PI) + 0.5));
20✔
632
}
633

634
bool EFX::isPhaseEnabled() const
5✔
635
{
636
    if (m_algorithm == EFX::Lissajous)
5✔
637
        return true;
638
    else
639
        return false;
3✔
640
}
641

642
/*****************************************************************************
643
 * Fixtures
644
 *****************************************************************************/
645

646
bool EFX::addFixture(EFXFixture* ef)
32✔
647
{
648
    Q_ASSERT(ef != NULL);
649

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

663
    /* If not inserted, put the EFXFixture object into our list */
664
    if (i >= m_fixtures.size())
32✔
665
        m_fixtures.append(ef);
32✔
666

667
    emit changed(this->id());
32✔
668

669
    return true;
32✔
670
}
671

672
bool EFX::addFixture(quint32 fxi, int head)
×
673
{
674
    EFXFixture *ef = new EFXFixture(this);
×
675
    GroupHead gHead(fxi, head);
×
676
    ef->setHead(gHead);
×
677

678
    return addFixture(ef);
×
679
}
×
680

681
bool EFX::removeFixture(EFXFixture* ef)
2✔
682
{
683
    Q_ASSERT(ef != NULL);
684

685
    if (m_fixtures.removeAll(ef) > 0)
2✔
686
    {
687
        emit changed(this->id());
1✔
688
        return true;
1✔
689
    }
690
    else
691
    {
692
        return false;
693
    }
694
}
695

696
bool EFX::removeFixture(quint32 fxi, int head)
2✔
697
{
698
    for (int i = 0; i < m_fixtures.count(); i++)
2✔
699
    {
700
        EFXFixture *ef = m_fixtures.at(i);
1✔
701
        if (ef->head().fxi == fxi && ef->head().head == head)
1✔
702
        {
703
            m_fixtures.removeAt(i);
1✔
704
            return true;
1✔
705
        }
706
    }
707

708
    return false;
709
}
710

711
void EFX::removeAllFixtures()
×
712
{
713
    m_fixtures.clear();
×
714
    emit changed(this->id());
×
715
}
×
716

717
bool EFX::raiseFixture(EFXFixture* ef)
3✔
718
{
719
    Q_ASSERT(ef != NULL);
720

721
    int index = m_fixtures.indexOf(ef);
3✔
722
    if (index > 0)
3✔
723
    {
724
        m_fixtures.move(index, index - 1);
1✔
725
        emit changed(this->id());
1✔
726
        return true;
1✔
727
    }
728
    else
729
    {
730
        return false;
731
    }
732
}
733

734
bool EFX::lowerFixture(EFXFixture* ef)
3✔
735
{
736
    int index = m_fixtures.indexOf(ef);
3✔
737
    if (index < (m_fixtures.count() - 1))
3✔
738
    {
739
        m_fixtures.move(index, index + 1);
1✔
740
        emit changed(this->id());
1✔
741
        return true;
1✔
742
    }
743
    else
744
    {
745
        return false;
746
    }
747
}
748

749
const QList <EFXFixture*> EFX::fixtures() const
80✔
750
{
751
    return m_fixtures;
80✔
752
}
753

754
EFXFixture *EFX::fixture(quint32 id, int headIndex)
4✔
755
{
756
    foreach (EFXFixture *ef, m_fixtures)
10✔
757
    {
758
        if (ef->head().fxi == id && ef->head().head == headIndex)
9✔
759
            return ef;
760
    }
761

762
    return NULL;
1✔
763
}
764

765
QList<quint32> EFX::components()
×
766
{
767
    QList<quint32> ids;
768

769
    foreach (EFXFixture *ef, m_fixtures)
×
770
    {
771
        if (ids.contains(ef->head().fxi) == false)
×
772
            ids.append(ef->head().fxi);
×
773
    }
774

775
    return ids;
×
776
}
×
777

778
void EFX::slotFixtureRemoved(quint32 fxi_id)
5✔
779
{
780
    /* Remove the destroyed fixture from our list */
781
    QMutableListIterator <EFXFixture*> it(m_fixtures);
5✔
782
    while (it.hasNext() == true)
11✔
783
    {
784
        it.next();
785

786
        if (it.value()->head().fxi == fxi_id)
9✔
787
        {
788
            delete it.value();
3✔
789
            it.remove();
3✔
790
            break;
791
        }
792
    }
793
}
5✔
794

795
/*****************************************************************************
796
 * Fixture propagation mode
797
 *****************************************************************************/
798

799
void EFX::setPropagationMode(PropagationMode mode)
9✔
800
{
801
    m_propagationMode = mode;
9✔
802
    emit changed(this->id());
9✔
803
}
9✔
804

805
EFX::PropagationMode EFX::propagationMode() const
471✔
806
{
807
    return m_propagationMode;
471✔
808
}
809

810
QString EFX::propagationModeToString(PropagationMode mode)
6✔
811
{
812
    if (mode == Serial)
6✔
813
        return QString(KXMLQLCEFXPropagationModeSerial);
2✔
814
    else if (mode == Asymmetric)
4✔
815
        return QString(KXMLQLCEFXPropagationModeAsymmetric);
1✔
816
    else
817
        return QString(KXMLQLCEFXPropagationModeParallel);
3✔
818
}
819

820
EFX::PropagationMode EFX::stringToPropagationMode(QString str)
7✔
821
{
822
    if (str == QString(KXMLQLCEFXPropagationModeSerial))
7✔
823
        return Serial;
824
    else if (str == QString(KXMLQLCEFXPropagationModeAsymmetric))
3✔
825
        return Asymmetric;
826
    else
827
        return Parallel;
2✔
828
}
829

830
/*****************************************************************************
831
 * Load & Save
832
 *****************************************************************************/
833

834
bool EFX::saveXML(QXmlStreamWriter *doc)
2✔
835
{
836
    Q_ASSERT(doc != NULL);
837

838
    /* Function tag */
839
    doc->writeStartElement(KXMLQLCFunction);
2✔
840

841
    /* Common attributes */
842
    saveXMLCommon(doc);
2✔
843

844
    /* Fixtures */
845
    QListIterator <EFXFixture*> it(m_fixtures);
2✔
846
    while (it.hasNext() == true)
5✔
847
        it.next()->saveXML(doc);
3✔
848

849
    /* Propagation mode */
850
    doc->writeTextElement(KXMLQLCEFXPropagationMode, propagationModeToString(m_propagationMode));
2✔
851

852
    /* Speeds */
853
    saveXMLSpeed(doc);
2✔
854
    /* Direction */
855
    saveXMLDirection(doc);
2✔
856
    /* Run order */
857
    saveXMLRunOrder(doc);
2✔
858

859
    /* Algorithm */
860
    doc->writeTextElement(KXMLQLCEFXAlgorithm, algorithmToString(algorithm()));
2✔
861
    /* Width */
862
    doc->writeTextElement(KXMLQLCEFXWidth, QString::number(width()));
2✔
863
    /* Height */
864
    doc->writeTextElement(KXMLQLCEFXHeight, QString::number(height()));
2✔
865
    /* Rotation */
866
    doc->writeTextElement(KXMLQLCEFXRotation, QString::number(rotation()));
2✔
867
    /* StartOffset */
868
    doc->writeTextElement(KXMLQLCEFXStartOffset, QString::number(startOffset()));
2✔
869
    /* IsRelative */
870
    doc->writeTextElement(KXMLQLCEFXIsRelative, QString::number(isRelative() ? 1 : 0));
4✔
871

872
    /********************************************
873
     * X-Axis
874
     ********************************************/
875
    doc->writeStartElement(KXMLQLCEFXAxis);
2✔
876
    doc->writeAttribute(KXMLQLCFunctionName, KXMLQLCEFXX);
2✔
877

878
    /* Offset */
879
    doc->writeTextElement(KXMLQLCEFXOffset, QString::number(xOffset()));
2✔
880
    /* Frequency */
881
    doc->writeTextElement(KXMLQLCEFXFrequency, QString::number(xFrequency()));
2✔
882
    /* Phase */
883
    doc->writeTextElement(KXMLQLCEFXPhase, QString::number(xPhase()));
2✔
884

885
    /* End the (X) <Axis> tag */
886
    doc->writeEndElement();
2✔
887

888
    /********************************************
889
     * Y-Axis
890
     ********************************************/
891
    doc->writeStartElement(KXMLQLCEFXAxis);
2✔
892
    doc->writeAttribute(KXMLQLCFunctionName, KXMLQLCEFXY);
2✔
893

894
    /* Offset */
895
    doc->writeTextElement(KXMLQLCEFXOffset, QString::number(yOffset()));
2✔
896
    /* Frequency */
897
    doc->writeTextElement(KXMLQLCEFXFrequency, QString::number(yFrequency()));
2✔
898
    /* Phase */
899
    doc->writeTextElement(KXMLQLCEFXPhase, QString::number(yPhase()));
2✔
900

901
    /* End the (Y) <Axis> tag */
902
    doc->writeEndElement();
2✔
903

904
    /* End the <Function> tag */
905
    doc->writeEndElement();
2✔
906

907
    return true;
2✔
908
}
909

910
bool EFX::loadXML(QXmlStreamReader &root)
5✔
911
{
912
    if (root.name() != KXMLQLCFunction)
10✔
913
    {
914
        qWarning() << "Function node not found!";
1✔
915
        return false;
1✔
916
    }
917

918
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::EFXType))
8✔
919
    {
920
        qWarning("Function is not an EFX!");
1✔
921
        return false;
1✔
922
    }
923

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

1005
    return true;
1006
}
1007

1008
bool EFX::loadXMLAxis(QXmlStreamReader &root)
10✔
1009
{
1010
    int frequency = 0;
1011
    int offset = 0;
1012
    int phase = 0;
1013
    QString axis;
1014

1015
    if (root.name() != KXMLQLCEFXAxis)
20✔
1016
    {
1017
        qWarning() << "EFX axis node not found!";
1✔
1018
        return false;
1✔
1019
    }
1020

1021
    /* Get the axis name */
1022
    axis = root.attributes().value(KXMLQLCFunctionName).toString();
9✔
1023

1024
    /* Load axis contents */
1025
    while (root.readNextStartElement())
37✔
1026
    {
1027
        if (root.name() == KXMLQLCEFXOffset)
56✔
1028
            offset = root.readElementText().toInt();
9✔
1029
        else if (root.name() == KXMLQLCEFXFrequency)
38✔
1030
            frequency = root.readElementText().toInt();
9✔
1031
        else if (root.name() == KXMLQLCEFXPhase)
20✔
1032
            phase = root.readElementText().toInt();
9✔
1033
        else
1034
        {
1035
            qWarning() << "Unknown EFX axis tag:" << root.name();
1✔
1036
            root.skipCurrentElement();
1✔
1037
        }
1038
    }
1039

1040
    if (axis == KXMLQLCEFXY)
9✔
1041
    {
1042
        setYOffset(offset);
4✔
1043
        setYFrequency(frequency);
4✔
1044
        setYPhase(phase);
4✔
1045
        return true;
1046
    }
1047
    else if (axis == KXMLQLCEFXX)
5✔
1048
    {
1049
        setXOffset(offset);
4✔
1050
        setXFrequency(frequency);
4✔
1051
        setXPhase(phase);
4✔
1052
        return true;
1053
    }
1054
    else
1055
    {
1056
        qWarning() << "Unknown EFX axis:" << axis;
1✔
1057
        return false;
1✔
1058
    }
1059
}
10✔
1060

1061
void EFX::postLoad()
1✔
1062
{
1063
    // Map legacy bus speeds to fixed speed values
1064
    if (m_legacyFadeBus != Bus::invalid())
1✔
1065
    {
1066
        quint32 value = Bus::instance()->value(m_legacyFadeBus);
1✔
1067
        setFadeInSpeed((value / MasterTimer::frequency()) * 1000);
1✔
1068
        setFadeOutSpeed((value / MasterTimer::frequency()) * 1000);
1✔
1069
    }
1070

1071
    if (m_legacyHoldBus != Bus::invalid())
1✔
1072
    {
1073
        quint32 value = Bus::instance()->value(m_legacyHoldBus);
1✔
1074
        setDuration((value / MasterTimer::frequency()) * 1000);
1✔
1075
    }
1076
}
1✔
1077

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

1096
    return fader;
×
1097
}
1098

1099
void EFX::preRun(MasterTimer* timer)
5✔
1100
{
1101
    int serialNumber = 0;
1102

1103
    QListIterator <EFXFixture*> it(m_fixtures);
5✔
1104
    while (it.hasNext() == true)
10✔
1105
    {
1106
        EFXFixture *ef = it.next();
5✔
1107
        Q_ASSERT(ef != NULL);
1108
        ef->setSerialNumber(serialNumber++);
5✔
1109
    }
1110

1111
    Function::preRun(timer);
5✔
1112
}
5✔
1113

1114
void EFX::write(MasterTimer *timer, QList<Universe*> universes)
×
1115
{
1116
    Q_UNUSED(timer);
1117

1118
    if (isPaused())
×
1119
        return;
×
1120

1121
    int done = 0;
1122

1123
    QListIterator <EFXFixture*> it(m_fixtures);
×
1124
    while (it.hasNext() == true)
×
1125
    {
1126
        EFXFixture *ef = it.next();
×
1127
        if (ef->isDone() == false)
×
1128
        {
1129
            QSharedPointer<GenericFader> fader = getFader(universes, ef->universe());
×
1130
            ef->nextStep(universes, fader);
×
1131
        }
1132
        else
1133
        {
1134
            done++;
×
1135
        }
1136
    }
1137

1138
    incrementElapsed();
×
1139

1140
    /* Check for stop condition */
1141
    if (done == m_fixtures.count())
×
1142
        stop(FunctionParent::master());
×
1143
}
1144

1145
void EFX::postRun(MasterTimer *timer, QList<Universe *> universes)
5✔
1146
{
1147
    /* Reset all fixtures */
1148
    QListIterator <EFXFixture*> it(m_fixtures);
5✔
1149
    while (it.hasNext() == true)
10✔
1150
    {
1151
        EFXFixture* ef(it.next());
5✔
1152

1153
        /* Run the EFX's stop scene for Loop & PingPong modes */
1154
        if (runOrder() != SingleShot)
5✔
1155
            ef->stop();
4✔
1156
        ef->reset();
5✔
1157
    }
1158

1159
    dismissAllFaders();
5✔
1160

1161
    Function::postRun(timer, universes);
5✔
1162
}
5✔
1163

1164
/*****************************************************************************
1165
 * Intensity
1166
 *****************************************************************************/
1167

1168
int EFX::adjustAttribute(qreal fraction, int attributeId)
165✔
1169
{
1170
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
165✔
1171

1172
    switch (attrIndex)
165✔
1173
    {
1174
        case Intensity:
2✔
1175
        {
1176
            foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
4✔
1177
            {
1178
                if (!fader.isNull())
×
1179
                    fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
1180
            }
1181
        }
1182
        break;
2✔
1183

1184
        case Height:
102✔
1185
        case Width:
1186
        case XOffset:
1187
        case YOffset:
1188
        case Rotation:
1189
            updateRotationCache();
102✔
1190
        break;
102✔
1191
    }
1192

1193
    return attrIndex;
165✔
1194
}
1195

1196
/*************************************************************************
1197
 * Blend mode
1198
 *************************************************************************/
1199

1200
void EFX::setBlendMode(Universe::BlendMode mode)
1✔
1201
{
1202
    if (mode == blendMode())
1✔
1203
        return;
1204

1205
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
1206
    {
1207
        if (!fader.isNull())
×
1208
            fader->setBlendMode(mode);
×
1209
    }
1210

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