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

mcallegari / qlcplus / 28530951808

01 Jul 2026 04:03PM UTC coverage: 35.287% (+0.01%) from 35.277%
28530951808

push

github

mcallegari
engine: fix EFX test unit

18560 of 52598 relevant lines covered (35.29%)

40989.4 hits per line

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

61.88
/engine/src/efxfixture.cpp
1
/*
2
  Q Light Controller Plus
3
  efxfixture.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 <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QDebug>
24
#include <math.h>
25

26
#include "genericfader.h"
27
#include "fadechannel.h"
28
#include "mastertimer.h"
29
#include "efxfixture.h"
30
#include "qlcmacros.h"
31
#include "function.h"
32
#include "universe.h"
33
#include "gradient.h"
34
#include "efx.h"
35
#include "doc.h"
36

37
/*****************************************************************************
38
 * Initialization
39
 *****************************************************************************/
40
QImage EFXFixture::m_rgbGradient = QImage();
41

42
EFXFixture::EFXFixture(const EFX* parent)
54✔
43
    : m_parent(parent)
54✔
44
    , m_head()
54✔
45
    , m_universe(Universe::invalid())
54✔
46
    , m_direction(Function::Forward)
54✔
47
    , m_startOffset(0)
54✔
48
    , m_mode(EFXFixture::PanTilt)
54✔
49

50
    , m_serialNumber(0)
54✔
51
    , m_runTimeDirection(Function::Forward)
54✔
52
    , m_done(false)
54✔
53
    , m_started(false)
54✔
54
    , m_elapsed(0)
54✔
55
    , m_currentAngle(0)
54✔
56

57
    , m_firstMsbChannel(QLCChannel::invalid())
54✔
58
    , m_firstLsbChannel(QLCChannel::invalid())
54✔
59
    , m_secondMsbChannel(QLCChannel::invalid())
54✔
60
    , m_secondLsbChannel(QLCChannel::invalid())
54✔
61

62
    , m_intensityMsbChannel(QLCChannel::invalid())
54✔
63
    , m_intensityLsbChannel(QLCChannel::invalid())
54✔
64
{
65
    Q_ASSERT(parent != NULL);
54✔
66

67
    if (m_rgbGradient.isNull())
54✔
68
        m_rgbGradient = Gradient::getRGBGradient(256, 256);
3✔
69
}
54✔
70

71
void EFXFixture::copyFrom(const EFXFixture* ef)
7✔
72
{
73
    // Don't copy m_parent because it is already assigned in constructor and might
74
    // be different than $ef's
75
    m_head = ef->m_head;
7✔
76
    m_universe = ef->m_universe;
7✔
77
    m_direction = ef->m_direction;
7✔
78
    m_startOffset = ef->m_startOffset;
7✔
79
    m_mode = ef->m_mode;
7✔
80

81
    m_serialNumber = ef->m_serialNumber;
7✔
82
    m_runTimeDirection = ef->m_runTimeDirection;
7✔
83
    m_done = ef->m_done;
7✔
84
    m_started = ef->m_started;
7✔
85
    m_elapsed = ef->m_elapsed;
7✔
86
    m_currentAngle = ef->m_currentAngle;
7✔
87
}
7✔
88

89
EFXFixture::~EFXFixture()
53✔
90
{
91
}
53✔
92

93
/****************************************************************************
94
 * Public properties
95
 ****************************************************************************/
96

97
void EFXFixture::setHead(GroupHead const & head)
44✔
98
{
99
    m_head = head;
44✔
100

101
    Fixture *fxi = doc()->fixture(head.fxi);
44✔
102
    if (fxi == NULL)
44✔
103
        return;
30✔
104

105
    m_universe = fxi->universe();
14✔
106

107
    QList<Mode> modes;
14✔
108

109
    if (fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head.head) != QLCChannel::invalid() ||
16✔
110
        fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head.head) != QLCChannel::invalid())
2✔
111
        modes << PanTilt;
14✔
112

113
    if (fxi->masterIntensityChannel() != QLCChannel::invalid() ||
16✔
114
        fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head.head) != QLCChannel::invalid())
2✔
115
        modes << Dimmer;
12✔
116

117
    if (fxi->rgbChannels(head.head).size() >= 3)
14✔
118
        modes << RGB;
2✔
119

120
    if (!modes.contains(m_mode))
14✔
121
    {
122
        if (modes.size() > 0)
×
123
            m_mode = modes[0];
×
124
    }
125
}
14✔
126

127
GroupHead const & EFXFixture::head() const
941✔
128
{
129
    return m_head;
941✔
130
}
131

132
void EFXFixture::setDirection(Function::Direction dir)
18✔
133
{
134
    m_direction = dir;
18✔
135
    m_runTimeDirection = dir;
18✔
136
}
18✔
137

138
Function::Direction EFXFixture::direction() const
13✔
139
{
140
    return m_direction;
13✔
141
}
142

143
void EFXFixture::setStartOffset(int startOffset)
5✔
144
{
145
    m_startOffset = CLAMP(startOffset, 0, 359);
5✔
146
}
5✔
147

148
int EFXFixture::startOffset() const
9✔
149
{
150
    return m_startOffset;
9✔
151
}
152

153
void EFXFixture::setMode(Mode mode)
×
154
{
155
    m_mode = mode;
×
156
}
×
157

158
EFXFixture::Mode EFXFixture::mode() const
4✔
159
{
160
    return m_mode;
4✔
161
}
162

163
quint32 EFXFixture::universe() const
156✔
164
{
165
    return m_universe;
156✔
166
}
167

168
bool EFXFixture::isValid() const
158✔
169
{
170
    Fixture *fxi = doc()->fixture(head().fxi);
158✔
171

172
    if (fxi == NULL)
158✔
173
        return false;
2✔
174
    else if (head().head >= fxi->heads())
156✔
175
        return false;
×
176
    else if (m_mode == PanTilt &&
468✔
177
             fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head) == QLCChannel::invalid() && // Maybe a device can pan OR tilt
156✔
178
             fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head) == QLCChannel::invalid())   // but not both
×
179
        return false;
×
180
    else if (m_mode == Dimmer &&
312✔
181
             fxi->masterIntensityChannel() == QLCChannel::invalid() &&
156✔
182
             fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head) == QLCChannel::invalid())
×
183
        return false;
×
184
    else if (m_mode == RGB && fxi->rgbChannels(head().head).size () == 0)
156✔
185
        return false;
×
186
    else
187
        return true;
156✔
188
}
189

190
void EFXFixture::durationChanged()
3✔
191
{
192
    if (m_elapsed == 0)
3✔
193
        return;
3✔
194

195
    // To avoid jumps when changing duration,
196
    // the elapsed time is rescaled to the
197
    // new duration.
198
    m_elapsed = SCALE(float(m_currentAngle),
×
199
            float(0), float(M_PI * 2),
200
            float(0), float(m_parent->loopDuration()));
201

202
    // Serial or Asymmetric propagation mode:
203
    // we must subtract the offset from the current position
204
    uint offset = timeOffset();
×
205
    if (offset)
×
206
    {
207
        if (m_elapsed < offset)
×
208
            m_elapsed += m_parent->loopDuration();
×
209
        m_elapsed -= offset;
×
210
    }
211
}
212

213
QStringList EFXFixture::modeList() const
×
214
{
215
    Fixture* fxi = doc()->fixture(head().fxi);
×
216
    Q_ASSERT(fxi != NULL);
×
217

218
    QStringList modes;
×
219

220
    if (fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head) != QLCChannel::invalid() ||
×
221
       fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head) != QLCChannel::invalid())
×
222
        modes << KXMLQLCEFXFixtureModePanTilt;
×
223

224
    if (fxi->masterIntensityChannel() != QLCChannel::invalid() ||
×
225
       fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head) != QLCChannel::invalid())
×
226
        modes << KXMLQLCEFXFixtureModeDimmer;
×
227

228
    if (fxi->rgbChannels(head().head).size() >= 3)
×
229
        modes << KXMLQLCEFXFixtureModeRGB;
×
230

231
    return modes;
×
232
}
×
233

234
QString EFXFixture::modeToString(Mode mode)
×
235
{
236
    switch (mode)
×
237
    {
238
        default:
×
239
        case PanTilt:
240
            return QString(KXMLQLCEFXFixtureModePanTilt);
×
241
        case Dimmer:
×
242
            return QString(KXMLQLCEFXFixtureModeDimmer);
×
243
        case RGB:
×
244
            return QString(KXMLQLCEFXFixtureModeRGB);
×
245
    }
246
}
247

248
EFXFixture::Mode EFXFixture::stringToMode(const QString& str)
×
249
{
250
    if (str == QString(KXMLQLCEFXFixtureModePanTilt))
×
251
        return PanTilt;
×
252
    else if (str == QString(KXMLQLCEFXFixtureModeDimmer))
×
253
        return Dimmer;
×
254
    else if (str == QString(KXMLQLCEFXFixtureModeRGB))
×
255
        return RGB;
×
256
    else
257
        return PanTilt;
×
258
}
259

260
/*****************************************************************************
261
 * Load & Save
262
 *****************************************************************************/
263

264
bool EFXFixture::loadXML(QXmlStreamReader &root)
13✔
265
{
266
    if (root.name() != KXMLQLCEFXFixture)
13✔
267
    {
268
        qWarning("EFX Fixture node not found!");
1✔
269
        return false;
1✔
270
    }
271

272
    GroupHead head;
12✔
273
    head.head = 0;
12✔
274

275
    /* New file format contains sub tags */
276
    while (root.readNextStartElement())
38✔
277
    {
278
        if (root.name() == KXMLQLCEFXFixtureID)
26✔
279
        {
280
            /* Fixture ID */
281
            head.fxi = root.readElementText().toInt();
12✔
282
        }
283
        else if (root.name() == KXMLQLCEFXFixtureHead)
14✔
284
        {
285
            /* Fixture Head */
286
            head.head = root.readElementText().toInt();
1✔
287
        }
288
        else if (root.name() == KXMLQLCEFXFixtureMode)
13✔
289
        {
290
            /* Fixture Mode */
291
            setMode ((Mode) root.readElementText().toInt());
×
292
        }
293
        else if (root.name() == KXMLQLCEFXFixtureDirection)
13✔
294
        {
295
            /* Direction */
296
            Function::Direction dir = Function::stringToDirection(root.readElementText());
12✔
297
            setDirection(dir);
12✔
298
        }
299
        else if (root.name() == KXMLQLCEFXFixtureStartOffset)
1✔
300
        {
301
            /* Start offset */
302
            setStartOffset(root.readElementText().toInt());
×
303
        }
304
        else if (root.name() == KXMLQLCEFXFixtureIntensity)
1✔
305
        {
306
            /* Intensity - LEGACY */
307
            root.skipCurrentElement();
×
308
        }
309
        else
310
        {
311
            qWarning() << "Unknown EFX Fixture tag:" << root.name();
1✔
312
            root.skipCurrentElement();
1✔
313
        }
314
    }
315

316
    if (head.fxi != Fixture::invalidId())
12✔
317
       setHead(head);
12✔
318

319
    return true;
12✔
320
}
12✔
321

322
bool EFXFixture::saveXML(QXmlStreamWriter *doc) const
4✔
323
{
324
    Q_ASSERT(doc != NULL);
4✔
325

326
    /* EFXFixture */
327
    doc->writeStartElement(KXMLQLCEFXFixture);
8✔
328

329
    /* Fixture ID */
330
    doc->writeTextElement(KXMLQLCEFXFixtureID, QString("%1").arg(head().fxi));
8✔
331
    /* Fixture Head */
332
    doc->writeTextElement(KXMLQLCEFXFixtureHead, QString("%1").arg(head().head));
8✔
333
    /* Mode */
334
    doc->writeTextElement(KXMLQLCEFXFixtureMode, QString::number(mode()));
8✔
335
    /* Direction */
336
    doc->writeTextElement(KXMLQLCEFXFixtureDirection, Function::directionToString(m_direction));
8✔
337
    /* Start offset */
338
    doc->writeTextElement(KXMLQLCEFXFixtureStartOffset, QString::number(startOffset()));
8✔
339

340
    /* End the <Fixture> tag */
341
    doc->writeEndElement();
4✔
342

343
    return true;
4✔
344
}
345

346
/****************************************************************************
347
 * Run-time properties
348
 ****************************************************************************/
349

350
const Doc* EFXFixture::doc() const
521✔
351
{
352
    Q_ASSERT(m_parent != NULL);
521✔
353
    return m_parent->doc();
521✔
354
}
355

356
void EFXFixture::setSerialNumber(int number)
13✔
357
{
358
    m_serialNumber = number;
13✔
359
}
13✔
360

361
int EFXFixture::serialNumber() const
2✔
362
{
363
    return m_serialNumber;
2✔
364
}
365

366
void EFXFixture::reset()
10✔
367
{
368
    m_done = false;
10✔
369
    m_runTimeDirection = m_direction;
10✔
370
    m_started = false;
10✔
371
    m_elapsed = 0;
10✔
372
    m_currentAngle = 0;
10✔
373
}
10✔
374

375
bool EFXFixture::isDone() const
206✔
376
{
377
    return m_done;
206✔
378
}
379

380
uint EFXFixture::timeOffset() const
152✔
381
{
382
    if (m_parent->propagationMode() == EFX::Asymmetric ||
304✔
383
        m_parent->propagationMode() == EFX::Serial)
152✔
384
    {
385
        return m_parent->loopDuration() / (m_parent->fixtures().size() + 1) * serialNumber();
×
386
    }
387
    else
388
    {
389
        return 0;
152✔
390
    }
391
}
392

393
/*****************************************************************************
394
 * Running
395
 *****************************************************************************/
396

397
void EFXFixture::start(QSharedPointer<GenericFader> fader)
7✔
398
{
399
    Fixture *fxi = doc()->fixture(head().fxi);
7✔
400

401
    /* Cache channels to reduce processing while running */
402
    switch (m_mode)
7✔
403
    {
404
        case PanTilt:
7✔
405
        {
406
            m_firstMsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, head().head);
7✔
407
            m_firstLsbChannel = fxi->channelNumber(QLCChannel::Pan, QLCChannel::LSB, head().head);
7✔
408
            m_secondMsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, head().head);
7✔
409
            m_secondLsbChannel = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, head().head);
7✔
410

411
            /* Check for non-contiguous channels */
412
            if ((m_firstLsbChannel != QLCChannel::invalid() && m_firstLsbChannel - m_firstMsbChannel != 1) ||
13✔
413
                (m_secondLsbChannel != QLCChannel::invalid() && m_secondLsbChannel - m_secondMsbChannel != 1))
6✔
414
            {
415
                fader->setHandleSecondary(false);
1✔
416
            }
417

418
            /* When dimmer control is enabled, cache the intensity channel too so
419
               the EFX can drive the dimmer from the tilt (see nextStep). */
420
            if (m_parent->dimmerControlEnabled())
7✔
421
            {
422
                m_intensityMsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head);
×
423
                if (m_intensityMsbChannel != QLCChannel::invalid())
×
424
                    m_intensityLsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::LSB, head().head);
×
425
                else
426
                    m_intensityMsbChannel = fxi->masterIntensityChannel();
×
427
            }
428
        }
429
        break;
7✔
430

431
        case RGB:
×
432
        break;
×
433

434
        case Dimmer:
×
435
        {
436
            m_firstMsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::MSB, head().head);
×
437
            if (m_firstMsbChannel != QLCChannel::invalid())
×
438
            {
439
                m_firstLsbChannel = fxi->channelNumber(QLCChannel::Intensity, QLCChannel::LSB, head().head);
×
440

441
                /* Check for non-contiguous channels */
442
                if (m_firstLsbChannel != QLCChannel::invalid() && m_firstLsbChannel - m_firstMsbChannel != 1)
×
443
                {
444
                    fader->setHandleSecondary(false);
×
445
                }
446
            }
447
            else
448
            {
449
                m_firstMsbChannel = fxi->masterIntensityChannel();
×
450
            }
451
        }
452
        break;
×
453
    }
454
    m_started = true;
7✔
455
}
7✔
456

457
void EFXFixture::stop()
5✔
458
{
459
    m_started = false;
5✔
460
}
5✔
461

462
void EFXFixture::nextStep(QList<Universe *> universes, QSharedPointer<GenericFader> fader)
202✔
463
{
464
    // Nothing to do
465
    if (m_parent->loopDuration() == 0)
202✔
466
        return;
50✔
467

468
    // Bail out without doing anything if this fixture is ready (after single-shot)
469
    // or it has no pan&tilt channels (not valid).
470
    if (m_done == true || isValid() == false)
152✔
471
        return;
×
472

473
    m_elapsed += MasterTimer::tick();
152✔
474

475
    // Check time wrapping
476
    if (m_elapsed > m_parent->loopDuration())
152✔
477
    {
478
        if (m_parent->runOrder() == Function::PingPong)
2✔
479
        {
480
            /* Reverse direction for ping-pong EFX. */
481
            if (m_runTimeDirection == Function::Forward)
×
482
                m_runTimeDirection = Function::Backward;
×
483
            else
484
                m_runTimeDirection = Function::Forward;
×
485
        }
486
        else if (m_parent->runOrder() == Function::SingleShot)
2✔
487
        {
488
            /* De-initialize the fixture and mark as ready. */
489
            m_done = true;
1✔
490
            stop();
1✔
491
        }
492

493
        m_elapsed = 0;
2✔
494
    }
495

496
    // Bail out without doing anything if this fixture is waiting for its turn.
497
    if (m_parent->propagationMode() == EFX::Serial && m_elapsed < timeOffset() && !m_started)
152✔
498
        return;
×
499

500
    // Fade in
501
    if (m_started == false)
152✔
502
        start(fader);
3✔
503

504
    // Scale from elapsed time in relation to overall duration to a point in a circle
505
    uint pos = (m_elapsed + timeOffset()) % m_parent->loopDuration();
152✔
506
    m_currentAngle = SCALE(float(pos),
152✔
507
                           float(0), float(m_parent->loopDuration()),
508
                           float(0), float(M_PI * 2));
509

510
    float valX = 0;
152✔
511
    float valY = 0;
152✔
512

513
    m_parent->calculatePoint(m_runTimeDirection, m_startOffset, m_currentAngle, &valX, &valY);
152✔
514

515
    /* Set target values on faders/universes */
516
    switch (m_mode)
152✔
517
    {
518
        case PanTilt:
152✔
519
            setPointPanTilt(universes, fader, valX, valY);
152✔
520
            /* When dimmer control is enabled, drive the dimmer from the EFX cycle
521
               angle, phased by this fixture's StartOffset. See EFX::dimmerLevel(). */
522
            if (m_parent->dimmerControlEnabled())
152✔
523
                setPointIntensity(universes, fader,
×
524
                                  m_parent->dimmerLevel(m_parent->convertOffset(m_startOffset),
×
525
                                                        m_currentAngle));
526
        break;
152✔
527

528
        case RGB:
×
529
            setPointRGB(universes, fader, valX, valY);
×
530
        break;
×
531

532
        case Dimmer:
×
533
            //Use Y for coherence with RGB gradient.
534
            setPointDimmer(universes, fader, valY);
×
535
        break;
×
536
    }
537
}
538

539
void EFXFixture::updateFaderValues(FadeChannel &fc, quint32 value)
312✔
540
{
541
    fc.setStart(fc.current());
312✔
542
    fc.setTarget(value);
312✔
543
    fc.setElapsed(0);
312✔
544
    fc.setReady(false);
312✔
545
    fc.setFadeTime(0);
312✔
546
}
312✔
547

548
void EFXFixture::setPointPanTilt(QList<Universe *> universes, QSharedPointer<GenericFader> fader,
156✔
549
                                 float pan, float tilt)
550
{
551
    if (fader.isNull())
156✔
552
        return;
×
553

554
    Universe *uni = universes[universe()];
156✔
555

556
    //qDebug() << "Pan value: " << pan << ", tilt value:" << tilt;
557

558
    /* Check for outbound values */
559
    if (pan < 0)
156✔
560
        pan = 0;
×
561

562
    if (tilt < 0)
156✔
563
        tilt = 0;
×
564

565
    /* Write full 16bit point data to universes */
566
    if (m_firstMsbChannel != QLCChannel::invalid())
156✔
567
    {
568
        quint32 panValue = quint32(pan);
155✔
569
        if (m_firstLsbChannel != QLCChannel::invalid())
155✔
570
        {
571
            if (fader->handleSecondary())
1✔
572
            {
573
                fader->updateChannel(doc(), uni, head().fxi, m_firstMsbChannel, [](FadeChannel &) {});
×
574
                panValue = (panValue << 8) + quint32((pan - floor(pan)) * float(UCHAR_MAX));
×
575
            }
576
            else
577
            {
578
                const quint32 lsbValue = quint32((pan - floor(pan)) * float(UCHAR_MAX));
1✔
579
                fader->updateChannel(doc(), uni, head().fxi, m_firstLsbChannel, [this, lsbValue](FadeChannel &fc)
1✔
580
                {
581
                    updateFaderValues(fc, lsbValue);
1✔
582
                });
1✔
583
            }
584
        }
585
        const quint32 panChannel = (m_firstLsbChannel != QLCChannel::invalid() && fader->handleSecondary())
156✔
586
                                   ? m_firstLsbChannel
156✔
587
                                   : m_firstMsbChannel;
155✔
588
        fader->updateChannel(doc(), uni, head().fxi, panChannel, [this, panValue](FadeChannel &fc)
155✔
589
        {
590
            if (m_parent->isRelative())
155✔
591
                fc.addFlag(FadeChannel::Relative);
×
592

593
            updateFaderValues(fc, panValue);
155✔
594
        });
155✔
595
    }
596
    if (m_secondMsbChannel != QLCChannel::invalid())
156✔
597
    {
598
        quint32 tiltValue = quint32(tilt);
155✔
599
        if (m_secondLsbChannel != QLCChannel::invalid())
155✔
600
        {
601
            if (fader->handleSecondary())
1✔
602
            {
603
                fader->updateChannel(doc(), uni, head().fxi, m_secondMsbChannel, [](FadeChannel &) {});
×
604
                tiltValue = (tiltValue << 8) + quint32((tilt - floor(tilt)) * float(UCHAR_MAX));
×
605
            }
606
            else
607
            {
608
                const quint32 lsbValue = quint32((tilt - floor(tilt)) * float(UCHAR_MAX));
1✔
609
                fader->updateChannel(doc(), uni, head().fxi, m_secondLsbChannel, [this, lsbValue](FadeChannel &fc)
1✔
610
                {
611
                    updateFaderValues(fc, lsbValue);
1✔
612
                });
1✔
613
            }
614
        }
615
        const quint32 tiltChannel = (m_secondLsbChannel != QLCChannel::invalid() && fader->handleSecondary())
156✔
616
                                    ? m_secondLsbChannel
156✔
617
                                    : m_secondMsbChannel;
155✔
618
        fader->updateChannel(doc(), uni, head().fxi, tiltChannel, [this, tiltValue](FadeChannel &fc)
155✔
619
        {
620
            if (m_parent->isRelative())
155✔
621
                fc.addFlag(FadeChannel::Relative);
×
622

623
            updateFaderValues(fc, tiltValue);
155✔
624
        });
155✔
625
    }
626
}
627

628
void EFXFixture::setPointDimmer(QList<Universe *> universes, QSharedPointer<GenericFader> fader, float dimmer)
×
629
{
630
    if (fader.isNull())
×
631
        return;
×
632

633
    Universe *uni = universes[universe()];
×
634

635
    /* Don't write dimmer data directly to universes but use FadeChannel to avoid steps at EFX loop restart */
636
    if (m_firstMsbChannel != QLCChannel::invalid())
×
637
    {
638
        quint32 dimmerValue = quint32(dimmer);
×
639
        if (m_firstLsbChannel != QLCChannel::invalid())
×
640
        {
641
            if (fader->handleSecondary())
×
642
            {
643
                fader->updateChannel(doc(), uni, head().fxi, m_firstMsbChannel, [](FadeChannel &) {});
×
644
                dimmerValue = (dimmerValue << 8) + quint32((dimmer - floor(dimmer)) * float(UCHAR_MAX));
×
645
            }
646
        }
647

648
        const quint32 dimmerChannel = (m_firstLsbChannel != QLCChannel::invalid() && fader->handleSecondary())
×
649
                                      ? m_firstLsbChannel
×
650
                                      : m_firstMsbChannel;
×
651
        fader->updateChannel(doc(), uni, head().fxi, dimmerChannel, [this, dimmerValue](FadeChannel &fc)
×
652
        {
653
            updateFaderValues(fc, dimmerValue);
×
654
        });
×
655
    }
656
}
657

658
void EFXFixture::setPointIntensity(QList<Universe *> universes, QSharedPointer<GenericFader> fader, float dimmer)
×
659
{
660
    if (fader.isNull())
×
661
        return;
×
662

663
    if (m_intensityMsbChannel == QLCChannel::invalid())
×
664
        return;
×
665

666
    Universe *uni = universes[universe()];
×
667

668
    /* Scale the 0.0 - 1.0 dimmer level to a 16bit-capable DMX value */
669
    quint32 value = quint32(dimmer * float(UCHAR_MAX));
×
670

671
    quint32 intChannel = m_intensityMsbChannel;
×
672
    if (m_intensityLsbChannel != QLCChannel::invalid() && fader->handleSecondary())
×
673
    {
674
        fader->updateChannel(doc(), uni, head().fxi, m_intensityMsbChannel, [](FadeChannel &) {});
×
675
        value = (value << 8) + quint32((dimmer * float(UCHAR_MAX) - floor(dimmer * float(UCHAR_MAX))) * float(UCHAR_MAX));
×
676
        intChannel = m_intensityLsbChannel;
×
677
    }
678

679
    fader->updateChannel(doc(), uni, head().fxi, intChannel, [this, value](FadeChannel &fc)
×
680
    {
681
        fc.setFadeTime(0);
×
682
        updateFaderValues(fc, value);
×
683
    });
×
684
}
685

686
void EFXFixture::setPointRGB(QList<Universe *> universes, QSharedPointer<GenericFader> fader, float x, float y)
×
687
{
688
    if (fader.isNull())
×
689
        return;
×
690

691
    Fixture* fxi = doc()->fixture(head().fxi);
×
692
    Q_ASSERT(fxi != NULL);
×
693
    Universe *uni = universes[universe()];
×
694

695
    QVector<quint32> rgbChannels = fxi->rgbChannels(head().head);
×
696

697
    /* Don't write dimmer data directly to universes but use FadeChannel to avoid steps at EFX loop restart */
698
    if (rgbChannels.size() >= 3 && !fader.isNull())
×
699
    {
700
        QColor pixel = m_rgbGradient.pixel(x, y);
×
701
        fader->updateChannel(doc(), uni, fxi->id(), rgbChannels[0], [this, pixel](FadeChannel &fc)
×
702
        {
703
            updateFaderValues(fc, pixel.red());
×
704
        });
×
705
        fader->updateChannel(doc(), uni, fxi->id(), rgbChannels[1], [this, pixel](FadeChannel &fc)
×
706
        {
707
            updateFaderValues(fc, pixel.green());
×
708
        });
×
709
        fader->updateChannel(doc(), uni, fxi->id(), rgbChannels[2], [this, pixel](FadeChannel &fc)
×
710
        {
711
            updateFaderValues(fc, pixel.blue());
×
712
        });
×
713
    }
714
}
×
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