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

mcallegari / qlcplus / 8499243839

31 Mar 2024 03:36PM UTC coverage: 32.084% (+0.006%) from 32.078%
8499243839

Pull #1530

github

web-flow
Merge 9d3ca6b88 into 068ac52ad
Pull Request #1530: Add beamZ BAC500 and BAC506 fixtures

15389 of 47965 relevant lines covered (32.08%)

22925.48 hits per line

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

40.08
/engine/src/rgbmatrix.cpp
1
/*
2
  Q Light Controller Plus
3
  rgbmatrix.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 <QCoreApplication>
24
#include <QElapsedTimer>
25
#include <QDebug>
26
#include <cmath>
27
#include <QDir>
28

29
#include "rgbscriptscache.h"
30
#include "qlcfixturehead.h"
31
#include "fixturegroup.h"
32
#include "genericfader.h"
33
#include "fadechannel.h"
34
#include "rgbmatrix.h"
35
#include "doc.h"
36

37
#define KXMLQLCRGBMatrixStartColor      QString("MonoColor")
38
#define KXMLQLCRGBMatrixEndColor        QString("EndColor")
39
#define KXMLQLCRGBMatrixFixtureGroup    QString("FixtureGroup")
40
#define KXMLQLCRGBMatrixDimmerControl   QString("DimmerControl")
41

42
#define KXMLQLCRGBMatrixProperty        QString("Property")
43
#define KXMLQLCRGBMatrixPropertyName    QString("Name")
44
#define KXMLQLCRGBMatrixPropertyValue   QString("Value")
45

46
#define KXMLQLCRGBMatrixControlMode         QString("ControlMode")
47
#define KXMLQLCRGBMatrixControlModeRgb      QString("RGB")
48
#define KXMLQLCRGBMatrixControlModeAmber    QString("Amber")
49
#define KXMLQLCRGBMatrixControlModeWhite    QString("White")
50
#define KXMLQLCRGBMatrixControlModeUV       QString("UV")
51
#define KXMLQLCRGBMatrixControlModeDimmer   QString("Dimmer")
52
#define KXMLQLCRGBMatrixControlModeShutter  QString("Shutter")
53

54
/****************************************************************************
55
 * Initialization
56
 ****************************************************************************/
57

58
RGBMatrix::RGBMatrix(Doc* doc)
9✔
59
    : Function(doc, Function::RGBMatrixType)
60
    , m_dimmerControl(false)
61
    , m_fixtureGroupID(FixtureGroup::invalidId())
18✔
62
    , m_group(NULL)
63
    , m_algorithm(NULL)
64
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
65
    , m_algorithmMutex(QMutex::Recursive)
66
#endif
67
    , m_startColor(Qt::red)
68
    , m_endColor(QColor())
69
    , m_stepHandler(new RGBMatrixStep())
9✔
70
    , m_roundTime(new QElapsedTimer())
9✔
71
    , m_stepsCount(0)
72
    , m_stepBeatDuration(0)
73
    , m_controlMode(RGBMatrix::ControlModeRgb)
18✔
74
{
75
    setName(tr("New RGB Matrix"));
9✔
76
    setDuration(500);
9✔
77

78
    RGBScript scr = doc->rgbScriptsCache()->script("Stripes");
18✔
79
    setAlgorithm(scr.clone());
9✔
80
}
9✔
81

82
RGBMatrix::~RGBMatrix()
11✔
83
{
84
    delete m_algorithm;
9✔
85
    delete m_roundTime;
9✔
86
    delete m_stepHandler;
9✔
87
}
11✔
88

89
QIcon RGBMatrix::getIcon() const
×
90
{
91
    return QIcon(":/rgbmatrix.png");
×
92
}
93

94
void RGBMatrix::setTotalDuration(quint32 msec)
1✔
95
{
96
    QMutexLocker algorithmLocker(&m_algorithmMutex);
1✔
97

98
    if (m_algorithm == NULL)
1✔
99
        return;
×
100

101
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
1✔
102
    if (grp == NULL)
1✔
103
        return;
×
104

105
    int steps = m_algorithm->rgbMapStepCount(grp->size());
1✔
106
    setDuration(msec / steps);
1✔
107
}
108

109
quint32 RGBMatrix::totalDuration()
3✔
110
{
111
    QMutexLocker algorithmLocker(&m_algorithmMutex);
6✔
112

113
    if (m_algorithm == NULL)
3✔
114
        return 0;
×
115

116
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
3✔
117
    if (grp == NULL)
3✔
118
        return 0;
1✔
119

120
    qDebug () << "Algorithm steps:" << m_algorithm->rgbMapStepCount(grp->size());
2✔
121
    return m_algorithm->rgbMapStepCount(grp->size()) * duration();
2✔
122
}
123

124
void RGBMatrix::setDimmerControl(bool dimmerControl)
1✔
125
{
126
    m_dimmerControl = dimmerControl;
1✔
127
}
1✔
128

129
bool RGBMatrix::dimmerControl() const
2✔
130
{
131
    return m_dimmerControl;
2✔
132
}
133

134
/****************************************************************************
135
 * Copying
136
 ****************************************************************************/
137

138
Function* RGBMatrix::createCopy(Doc* doc, bool addToDoc)
1✔
139
{
140
    Q_ASSERT(doc != NULL);
1✔
141

142
    Function* copy = new RGBMatrix(doc);
1✔
143
    if (copy->copyFrom(this) == false)
1✔
144
    {
145
        delete copy;
×
146
        copy = NULL;
×
147
    }
148
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
149
    {
150
        delete copy;
×
151
        copy = NULL;
×
152
    }
153

154
    return copy;
1✔
155
}
156

157
bool RGBMatrix::copyFrom(const Function* function)
1✔
158
{
159
    const RGBMatrix* mtx = qobject_cast<const RGBMatrix*> (function);
1✔
160
    if (mtx == NULL)
1✔
161
        return false;
×
162

163
    setDimmerControl(mtx->dimmerControl());
1✔
164
    setFixtureGroup(mtx->fixtureGroup());
1✔
165
    if (mtx->algorithm() != NULL)
1✔
166
        setAlgorithm(mtx->algorithm()->clone());
1✔
167
    else
168
        setAlgorithm(NULL);
×
169
    setStartColor(mtx->startColor());
1✔
170
    setEndColor(mtx->endColor());
1✔
171
    setControlMode(mtx->controlMode());
1✔
172

173
    return Function::copyFrom(function);
1✔
174
}
175

176
/****************************************************************************
177
 * Fixtures
178
 ****************************************************************************/
179

180
quint32 RGBMatrix::fixtureGroup() const
37✔
181
{
182
    return m_fixtureGroupID;
37✔
183
}
184

185
void RGBMatrix::setFixtureGroup(quint32 id)
8✔
186
{
187
    m_fixtureGroupID = id;
8✔
188
    {
189
        QMutexLocker algoLocker(&m_algorithmMutex);
8✔
190
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
8✔
191
    }
192
    m_stepsCount = stepsCount();
8✔
193
}
8✔
194

195
QList<quint32> RGBMatrix::components()
2✔
196
{
197
    if (m_group != NULL)
2✔
198
        return m_group->fixtureList();
1✔
199

200
    return QList<quint32>();
1✔
201
}
202

203
/****************************************************************************
204
 * Algorithm
205
 ****************************************************************************/
206

207
void RGBMatrix::setAlgorithm(RGBAlgorithm* algo)
13✔
208
{
209
    {
210
        QMutexLocker algorithmLocker(&m_algorithmMutex);
26✔
211
        delete m_algorithm;
13✔
212
        m_algorithm = algo;
13✔
213

214
        /** If there's been a change of Script algorithm "on the fly",
215
         *  then re-apply the properties currently set in this RGBMatrix */
216
        if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
13✔
217
        {
218
            RGBScript *script = static_cast<RGBScript*> (m_algorithm);
13✔
219
            QHashIterator<QString, QString> it(m_properties);
26✔
220
            while (it.hasNext())
13✔
221
            {
222
                it.next();
×
223
                if (script->setProperty(it.key(), it.value()) == false)
×
224
                {
225
                    /** If the new algorithm doesn't expose a property,
226
                     *  then remove it from the cached list, otherwise
227
                     *  it would be carried around forever (and saved on XML) */
228
                    m_properties.take(it.key());
×
229
                }
230
            }
231
        }
232
    }
233
    m_stepsCount = stepsCount();
13✔
234

235
    emit changed(id());
13✔
236
}
13✔
237

238
RGBAlgorithm* RGBMatrix::algorithm() const
18✔
239
{
240
    return m_algorithm;
18✔
241
}
242

243
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
244
QMutex& RGBMatrix::algorithmMutex()
245
{
246
    return m_algorithmMutex;
247
}
248
#else
249
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
250
{
251
    return m_algorithmMutex;
×
252
}
253
#endif
254

255

256
int RGBMatrix::stepsCount()
24✔
257
{
258
    QMutexLocker algorithmLocker(&m_algorithmMutex);
48✔
259

260
    if (m_algorithm == NULL)
24✔
261
        return 0;
×
262

263
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
24✔
264
    if (grp != NULL)
24✔
265
        return m_algorithm->rgbMapStepCount(grp->size());
7✔
266

267
    return 0;
17✔
268
}
269

270
void RGBMatrix::previewMap(int step, RGBMatrixStep *handler)
7✔
271
{
272
    QMutexLocker algorithmLocker(&m_algorithmMutex);
7✔
273
    if (m_algorithm == NULL || handler == NULL)
7✔
274
        return;
×
275

276
    if (m_group == NULL)
7✔
277
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
278

279
    if (m_group != NULL)
7✔
280
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map);
6✔
281
}
282

283
/****************************************************************************
284
 * Color
285
 ****************************************************************************/
286

287
void RGBMatrix::setStartColor(const QColor& c)
6✔
288
{
289
    m_startColor = c;
6✔
290
    {
291
        QMutexLocker algorithmLocker(&m_algorithmMutex);
12✔
292
        if (m_algorithm != NULL)
6✔
293
        {
294
            m_algorithm->setColors(m_startColor, m_endColor);
6✔
295
            updateColorDelta();
6✔
296
        }
297
    }
298
    emit changed(id());
6✔
299
}
6✔
300

301
QColor RGBMatrix::startColor() const
7✔
302
{
303
    return m_startColor;
7✔
304
}
305

306
void RGBMatrix::setEndColor(const QColor &c)
6✔
307
{
308
    m_endColor = c;
6✔
309
    {
310
        QMutexLocker algorithmLocker(&m_algorithmMutex);
12✔
311
        if (m_algorithm != NULL)
6✔
312
        {
313
            m_algorithm->setColors(m_startColor, m_endColor);
6✔
314
            updateColorDelta();
6✔
315
        }
316
    }
317
    emit changed(id());
6✔
318
}
6✔
319

320
QColor RGBMatrix::endColor() const
8✔
321
{
322
    return m_endColor;
8✔
323
}
324

325
void RGBMatrix::updateColorDelta()
12✔
326
{
327
    m_stepHandler->calculateColorDelta(m_startColor, m_endColor);
12✔
328
}
12✔
329

330
/************************************************************************
331
 * Properties
332
 ************************************************************************/
333

334
void RGBMatrix::setProperty(QString propName, QString value)
1✔
335
{
336
    QMutexLocker algoLocker(&m_algorithmMutex);
1✔
337
    m_properties[propName] = value;
1✔
338
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
1✔
339
    {
340
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
1✔
341
        script->setProperty(propName, value);
1✔
342
    }
343
    m_stepsCount = stepsCount();
1✔
344
}
1✔
345

346
QString RGBMatrix::property(QString propName)
3✔
347
{
348
    QMutexLocker algoLocker(&m_algorithmMutex);
6✔
349

350
    /** If the property is cached, then return it right away */
351
    if (m_properties.contains(propName))
3✔
352
        return m_properties[propName];
1✔
353

354
    /** Otherwise, let's retrieve it from the Script */
355
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
2✔
356
    {
357
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
2✔
358
        return script->property(propName);
2✔
359
    }
360

361
    return QString();
×
362
}
363

364
/****************************************************************************
365
 * Load & Save
366
 ****************************************************************************/
367

368
bool RGBMatrix::loadXML(QXmlStreamReader &root)
3✔
369
{
370
    if (root.name() != KXMLQLCFunction)
3✔
371
    {
372
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
373
        return false;
1✔
374
    }
375

376
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::RGBMatrixType))
2✔
377
    {
378
        qWarning() << Q_FUNC_INFO << "Function is not an RGB matrix";
1✔
379
        return false;
1✔
380
    }
381

382
    /* Load matrix contents */
383
    while (root.readNextStartElement())
9✔
384
    {
385
        if (root.name() == KXMLQLCFunctionSpeed)
8✔
386
        {
387
            loadXMLSpeed(root);
1✔
388
        }
389
        else if (root.name() == KXMLQLCRGBAlgorithm)
7✔
390
        {
391
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
392
        }
393
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
6✔
394
        {
395
            setFixtureGroup(root.readElementText().toUInt());
1✔
396
        }
397
        else if (root.name() == KXMLQLCFunctionDirection)
5✔
398
        {
399
            loadXMLDirection(root);
1✔
400
        }
401
        else if (root.name() == KXMLQLCFunctionRunOrder)
4✔
402
        {
403
            loadXMLRunOrder(root);
1✔
404
        }
405
        else if (root.name() == KXMLQLCRGBMatrixStartColor)
3✔
406
        {
407
            setStartColor(QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
408
        }
409
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
2✔
410
        {
411
            setEndColor(QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
412
        }
413
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
1✔
414
        {
415
            setControlMode(stringToControlMode(root.readElementText()));
1✔
416
        }
417
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
418
        {
419
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
420
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
421
            setProperty(name, value);
×
422
            root.skipCurrentElement();
×
423
        }
424
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
425
        {
426
            setDimmerControl(root.readElementText().toInt());
×
427
        }
428
        else
429
        {
430
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
431
            root.skipCurrentElement();
×
432
        }
433
    }
434

435
    return true;
1✔
436
}
437

438
bool RGBMatrix::saveXML(QXmlStreamWriter *doc)
1✔
439
{
440
    Q_ASSERT(doc != NULL);
1✔
441

442
    /* Function tag */
443
    doc->writeStartElement(KXMLQLCFunction);
1✔
444

445
    /* Common attributes */
446
    saveXMLCommon(doc);
1✔
447

448
    /* Speeds */
449
    saveXMLSpeed(doc);
1✔
450

451
    /* Direction */
452
    saveXMLDirection(doc);
1✔
453

454
    /* Run order */
455
    saveXMLRunOrder(doc);
1✔
456

457
    /* Algorithm */
458
    if (m_algorithm != NULL)
1✔
459
        m_algorithm->saveXML(doc);
1✔
460

461
    /* LEGACY - Dimmer Control */
462
    if (dimmerControl())
1✔
463
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
464

465
    /* Start Color */
466
    doc->writeTextElement(KXMLQLCRGBMatrixStartColor, QString::number(startColor().rgb()));
1✔
467

468
    /* End Color */
469
    if (endColor().isValid())
1✔
470
        doc->writeTextElement(KXMLQLCRGBMatrixEndColor, QString::number(endColor().rgb()));
1✔
471

472
    /* Control Mode */
473
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
1✔
474

475
    /* Fixture Group */
476
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
1✔
477

478
    /* Properties */
479
    QHashIterator<QString, QString> it(m_properties);
1✔
480
    while (it.hasNext())
1✔
481
    {
482
        it.next();
×
483
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
484
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
485
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
486
        doc->writeEndElement();
×
487
    }
488

489
    /* End the <Function> tag */
490
    doc->writeEndElement();
1✔
491

492
    return true;
2✔
493
}
494

495
/****************************************************************************
496
 * Running
497
 ****************************************************************************/
498

499
void RGBMatrix::tap()
×
500
{
501
    if (stopped() == false)
×
502
    {
503
        FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
×
504
        // Filter out taps that are too close to each other
505
        if (grp != NULL && uint(m_roundTime->elapsed()) >= (duration() / 4))
×
506
        {
507
            roundCheck();
×
508
            resetElapsed();
×
509
        }
510
    }
511
}
×
512

513
void RGBMatrix::preRun(MasterTimer *timer)
×
514
{
515
    {
516
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
517

518
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
519
        if (m_group == NULL)
×
520
        {
521
            // No fixture group to control
522
            stop(FunctionParent::master());
×
523
            return;
×
524
        }
525

526
        if (m_algorithm != NULL)
×
527
        {
528
            // Copy direction from parent class direction
529
            m_stepHandler->initializeDirection(direction(), m_startColor, m_endColor, m_stepsCount);
×
530

531
            if (m_algorithm->type() == RGBAlgorithm::Script)
×
532
            {
533
                RGBScript *script = static_cast<RGBScript*> (m_algorithm);
×
534
                QHashIterator<QString, QString> it(m_properties);
×
535
                while (it.hasNext())
×
536
                {
537
                    it.next();
×
538
                    script->setProperty(it.key(), it.value());
×
539
                }
540
            }
541
        }
542
    }
543

544
    m_roundTime->restart();
×
545

546
    Function::preRun(timer);
×
547
}
548

549
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
550
{
551
    Q_UNUSED(timer);
552

553
    {
554
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
555
        if (m_group == NULL)
×
556
        {
557
            // No fixture group to control
558
            stop(FunctionParent::master());
×
559
            return;
×
560
        }
561

562
        // No time to do anything.
563
        if (duration() == 0)
×
564
            return;
×
565

566
        // Invalid/nonexistent script
567
        if (m_algorithm == NULL || m_algorithm->apiVersion() == 0)
×
568
            return;
×
569

570
        if (isPaused() == false)
×
571
        {
572
            // Get a new map every time elapsed is reset to zero
573
            if (elapsed() < MasterTimer::tick())
×
574
            {
575
                if (tempoType() == Beats)
×
576
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
577

578
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
579
                m_algorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
580
                                    m_stepHandler->currentStepIndex(), m_stepHandler->m_map);
×
581
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
582
            }
583
        }
584
    }
585

586
    if (isPaused() == false)
×
587
    {
588
        // Increment the ms elapsed time
589
        incrementElapsed();
×
590

591
        /* Check if we need to change direction, stop completely or go to next step
592
         * The cases are:
593
         * 1- time tempo type: act normally, on ms elapsed time
594
         * 2- beat tempo type, beat occurred: check if the elapsed beats is a multiple of
595
         *    the step beat duration. If so, proceed to the next step
596
         * 3- beat tempo type, not beat: if the ms elapsed time reached the step beat
597
         *    duration in ms, and the ms time to the next beat is not less than 1/16 of
598
         *    the step beat duration in ms, then proceed to the next step. If the ms time to the
599
         *    next beat is less than 1/16 of the step beat duration in ms, then defer the step
600
         *    change to case #2, to resync the matrix to the next beat
601
         */
602
        if (tempoType() == Time && elapsed() >= duration())
×
603
        {
604
            roundCheck();
×
605
        }
606
        else if (tempoType() == Beats)
×
607
        {
608
            if (timer->isBeat())
×
609
            {
610
                incrementElapsedBeats();
×
611
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
×
612
                if (elapsedBeats() % duration() == 0)
×
613
                {
614
                    roundCheck();
×
615
                    resetElapsed();
×
616
                }
617
            }
618
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
619
            {
620
                qDebug() << "Elapsed exceeded";
×
621
                roundCheck();
×
622
            }
623
        }
624
    }
625
}
626

627
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
628
{
629
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
630

631
    /* If no fade out is needed, dismiss all the requested faders.
632
     * Otherwise, set all the faders to fade out and let Universe dismiss them
633
     * when done */
634
    if (fadeout == 0)
×
635
    {
636
        dismissAllFaders();
×
637
    }
638
    else
639
    {
640
        if (tempoType() == Beats)
×
641
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
642

643
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
644
        {
645
            if (!fader.isNull())
×
646
                fader->setFadeOut(true, fadeout);
×
647
        }
648
    }
649

650
    m_fadersMap.clear();
×
651

652
    {
653
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
654
        if (m_algorithm != NULL)
×
655
            m_algorithm->postRun();
×
656
    }
657

658
    Function::postRun(timer, universes);
×
659
}
×
660

661
void RGBMatrix::roundCheck()
×
662
{
663
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
664
    if (m_algorithm == NULL)
×
665
        return;
×
666

667
    if (m_stepHandler->checkNextStep(runOrder(), m_startColor, m_endColor, m_stepsCount) == false)
×
668
        stop(FunctionParent::master());
×
669

670
    m_roundTime->restart();
×
671

672
    if (tempoType() == Beats)
×
673
        roundElapsed(m_stepBeatDuration);
×
674
    else
675
        roundElapsed(duration());
×
676
}
677

678
FadeChannel *RGBMatrix::getFader(QList<Universe *> universes, quint32 universeID, quint32 fixtureID, quint32 channel)
×
679
{
680
    // get the universe Fader first. If doesn't exist, create it
681
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
×
682
    if (fader.isNull())
×
683
    {
684
        fader = universes[universeID]->requestFader();
×
685
        fader->adjustIntensity(getAttributeValue(Intensity));
×
686
        fader->setBlendMode(blendMode());
×
687
        fader->setName(name());
×
688
        fader->setParentFunctionID(id());
×
689
        m_fadersMap[universeID] = fader;
×
690
    }
691

692
    return fader->getChannelFader(doc(), universes[universeID], fixtureID, channel);
×
693
}
694

695
void RGBMatrix::updateFaderValues(FadeChannel *fc, uchar value, uint fadeTime)
×
696
{
697
    fc->setStart(fc->current());
×
698
    fc->setTarget(value);
×
699
    fc->setElapsed(0);
×
700
    fc->setReady(false);
×
701
    // fade in/out depends on target value
702
    if (value == 0)
×
703
        fc->setFadeTime(fadeOutSpeed());
×
704
    else
705
        fc->setFadeTime(fadeTime);
×
706
}
×
707

708
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
709
{
710
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
711

712
    // Create/modify fade channels for ALL heads in the group
713
    QMapIterator<QLCPoint, GroupHead> it(grp->headsMap());
×
714
    while (it.hasNext())
×
715
    {
716
        it.next();
×
717
        QLCPoint pt = it.key();
×
718
        GroupHead grpHead = it.value();
×
719
        Fixture *fxi = doc()->fixture(grpHead.fxi);
×
720
        if (fxi == NULL)
×
721
            continue;
×
722

723
        QLCFixtureHead head = fxi->head(grpHead.head);
×
724

725
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
726
            continue;
×
727

728
        uint col = map[pt.y()][pt.x()];
×
729

730
        if (m_controlMode == ControlModeRgb)
×
731
        {
732
            QVector <quint32> rgb = head.rgbChannels();
×
733
            QVector <quint32> cmy = head.cmyChannels();
×
734

735
            if (rgb.size() == 3)
×
736
            {
737
                // RGB color mixing
738
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(0));
×
739
                updateFaderValues(fc, qRed(col), fadeTime);
×
740

741
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(1));
×
742
                updateFaderValues(fc, qGreen(col), fadeTime);
×
743

744
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(2));
×
745
                updateFaderValues(fc, qBlue(col), fadeTime);
×
746
            }
747
            else if (cmy.size() == 3)
×
748
            {
749
                // CMY color mixing
750
                QColor cmyCol(col);
×
751

752
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(0));
×
753
                updateFaderValues(fc, cmyCol.cyan(), fadeTime);
×
754

755
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(1));
×
756
                updateFaderValues(fc, cmyCol.magenta(), fadeTime);
×
757

758
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(2));
×
759
                updateFaderValues(fc, cmyCol.yellow(), fadeTime);
×
760
            }
761
        }
762
        else if (m_controlMode == ControlModeWhite)
×
763
        {
764
            quint32 white = head.channelNumber(QLCChannel::White, QLCChannel::MSB);
×
765

766
            if (white != QLCChannel::invalid())
×
767
            {
768
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, white);
×
769
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
770
            }
771
        }
772
        else if (m_controlMode == ControlModeAmber)
×
773
        {
774
            quint32 amber = head.channelNumber(QLCChannel::Amber, QLCChannel::MSB);
×
775

776
            if (amber != QLCChannel::invalid())
×
777
            {
778
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, amber);
×
779
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
780
            }
781
        }
782
        else if (m_controlMode == ControlModeUV)
×
783
        {
784
            quint32 uv = head.channelNumber(QLCChannel::UV, QLCChannel::MSB);
×
785

786
            if (uv != QLCChannel::invalid())
×
787
            {
788
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, uv);
×
789
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
790
            }
791
        }
792
        else if (m_controlMode == ControlModeShutter)
×
793
        {
794
            QVector <quint32> shutters = head.shutterChannels();
×
795

796
            if (shutters.size())
×
797
            {
798
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, shutters.first());
×
799
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
800
            }
801
        }
802

803
        if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
804
        {
805
            quint32 masterDim = fxi->masterIntensityChannel();
×
806
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
807
            QVector <quint32> dimmers;
×
808

809
            // Collect all dimmers that affect current head:
810
            // They are the master dimmer (affects whole fixture)
811
            // and per-head dimmer.
812
            //
813
            // If there are no RGB or CMY channels, the least important* dimmer channel
814
            // is used to create grayscale image.
815
            //
816
            // The rest of the dimmer channels are set to full if dimmer control is
817
            // enabled and target color is > 0 (see
818
            // http://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
819
            //
820
            // Note: If there is only one head, and only one dimmer channel,
821
            // make it a master dimmer in fixture definition.
822
            //
823
            // *least important - per head dimmer if present,
824
            // otherwise per fixture dimmer if present
825

826
            if (masterDim != QLCChannel::invalid())
×
827
                dimmers << masterDim;
×
828

829
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
830
                dimmers << headDim;
×
831

832
            if (dimmers.size())
×
833
            {
834
                // Set dimmer to value of the color (e.g. for PARs)
835
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, dimmers.last());
×
836
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
837
                dimmers.pop_back();
×
838
            }
839

840
            // Set the rest of the dimmer channels to full on
841
            foreach (quint32 ch, dimmers)
×
842
            {
843
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, ch);
×
844
                updateFaderValues(fc, col == 0 ? 0 : 255, fadeTime);
×
845
            }
846
        }
847
    }
848
}
×
849

850
uchar RGBMatrix::rgbToGrey(uint col)
×
851
{
852
    // the weights are taken from
853
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
854
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
855
}
856

857
/*********************************************************************
858
 * Attributes
859
 *********************************************************************/
860

861
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
862
{
863
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
864

865
    if (attrIndex == Intensity)
×
866
    {
867
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
868
        {
869
            if (!fader.isNull())
×
870
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
871
        }
872
    }
873

874
    return attrIndex;
×
875
}
876

877
/*************************************************************************
878
 * Blend mode
879
 *************************************************************************/
880

881
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
882
{
883
    if (mode == blendMode())
×
884
        return;
×
885

886
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
887
    {
888
        if (!fader.isNull())
×
889
            fader->setBlendMode(mode);
×
890
    }
891

892
    Function::setBlendMode(mode);
×
893
    emit changed(id());
×
894
}
895

896
/*************************************************************************
897
 * Control Mode
898
 *************************************************************************/
899

900
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
901
{
902
    return m_controlMode;
2✔
903
}
904

905
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
906
{
907
    m_controlMode = mode;
3✔
908
    emit changed(id());
3✔
909
}
3✔
910

911
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
912
{
913
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
914
        return ControlModeRgb;
1✔
915
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
916
        return ControlModeAmber;
×
917
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
918
        return ControlModeWhite;
×
919
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
920
        return ControlModeUV;
×
921
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
922
        return ControlModeDimmer;
×
923
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
924
        return ControlModeShutter;
×
925

926
    return ControlModeRgb;
×
927
}
928

929
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
930
{
931
    switch(mode)
1✔
932
    {
933
        default:
1✔
934
        case ControlModeRgb:
935
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
936
        break;
937
        case ControlModeAmber:
×
938
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
939
        break;
940
        case ControlModeWhite:
×
941
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
942
        break;
943
        case ControlModeUV:
×
944
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
945
        break;
946
        case ControlModeDimmer:
×
947
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
948
        break;
949
        case ControlModeShutter:
×
950
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
951
        break;
952
    }
953
}
954

955

956
/*************************************************************************
957
 *************************************************************************
958
 *                          RGBMatrixStep class
959
 *************************************************************************
960
 *************************************************************************/
961

962
RGBMatrixStep::RGBMatrixStep()
10✔
963
    : m_direction(Function::Forward)
964
    , m_currentStepIndex(0)
965
    , m_stepColor(QColor())
966
    , m_crDelta(0)
967
    , m_cgDelta(0)
968
    , m_cbDelta(0)
10✔
969
{
970

971
}
10✔
972

973
void RGBMatrixStep::setCurrentStepIndex(int index)
×
974
{
975
    m_currentStepIndex = index;
×
976
}
×
977

978
int RGBMatrixStep::currentStepIndex() const
1✔
979
{
980
    return m_currentStepIndex;
1✔
981
}
982

983
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor)
12✔
984
{
985
    m_crDelta = 0;
12✔
986
    m_cgDelta = 0;
12✔
987
    m_cbDelta = 0;
12✔
988

989
    if (endColor.isValid())
12✔
990
    {
991
        m_crDelta = endColor.red() - startColor.red();
5✔
992
        m_cgDelta = endColor.green() - startColor.green();
5✔
993
        m_cbDelta = endColor.blue() - startColor.blue();
5✔
994

995
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
996
    }
997
}
12✔
998

999
void RGBMatrixStep::setStepColor(QColor color)
×
1000
{
1001
    m_stepColor = color;
×
1002
}
×
1003

1004
QColor RGBMatrixStep::stepColor()
6✔
1005
{
1006
    return m_stepColor;
6✔
1007
}
1008

1009
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1010
{
1011
    if (stepsCount <= 0)
×
1012
        return;
×
1013

1014
    if (stepsCount == 1)
×
1015
    {
1016
        m_stepColor = startColor;
×
1017
    }
1018
    else
1019
    {
1020
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
1021
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1022
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1023
    }
1024

1025
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1026
}
1027

1028
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount)
×
1029
{
1030
    m_direction = direction;
×
1031

1032
    if (m_direction == Function::Forward)
×
1033
    {
1034
        setCurrentStepIndex(0);
×
1035
        setStepColor(startColor);
×
1036
    }
1037
    else
1038
    {
1039
        setCurrentStepIndex(stepsCount - 1);
×
1040

1041
        if (endColor.isValid())
×
1042
            setStepColor(endColor);
×
1043
        else
1044
            setStepColor(startColor);
×
1045
    }
1046

1047
    calculateColorDelta(startColor, endColor);
×
1048
}
×
1049

1050
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1051
                                  QColor startColor, QColor endColor, int stepsNumber)
1052
{
1053
    if (order == Function::PingPong)
×
1054
    {
1055
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1056
        {
1057
            m_direction = Function::Backward;
×
1058
            m_currentStepIndex = stepsNumber - 2;
×
1059
            if (endColor.isValid())
×
1060
                m_stepColor = endColor;
×
1061

1062
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1063
        }
1064
        else if (m_direction == Function::Backward && (m_currentStepIndex - 1) < 0)
×
1065
        {
1066
            m_direction = Function::Forward;
×
1067
            m_currentStepIndex = 1;
×
1068
            m_stepColor = startColor;
×
1069
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1070
        }
1071
        else
1072
        {
1073
            if (m_direction == Function::Forward)
×
1074
                m_currentStepIndex++;
×
1075
            else
1076
                m_currentStepIndex--;
×
1077
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1078
        }
1079
    }
1080
    else if (order == Function::SingleShot)
×
1081
    {
1082
        if (m_direction == Function::Forward)
×
1083
        {
1084
            if (m_currentStepIndex >= stepsNumber - 1)
×
1085
                return false;
×
1086
            else
1087
            {
1088
                m_currentStepIndex++;
×
1089
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1090
            }
1091
        }
1092
        else
1093
        {
1094
            if (m_currentStepIndex <= 0)
×
1095
                return false;
×
1096
            else
1097
            {
1098
                m_currentStepIndex--;
×
1099
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1100
            }
1101
        }
1102
    }
1103
    else
1104
    {
1105
        if (m_direction == Function::Forward)
×
1106
        {
1107
            if (m_currentStepIndex >= stepsNumber - 1)
×
1108
            {
1109
                m_currentStepIndex = 0;
×
1110
                m_stepColor = startColor;
×
1111
            }
1112
            else
1113
            {
1114
                m_currentStepIndex++;
×
1115
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1116
            }
1117
        }
1118
        else
1119
        {
1120
            if (m_currentStepIndex <= 0)
×
1121
            {
1122
                m_currentStepIndex = stepsNumber - 1;
×
1123
                if (endColor.isValid())
×
1124
                    m_stepColor = endColor;
×
1125
            }
1126
            else
1127
            {
1128
                m_currentStepIndex--;
×
1129
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1130
            }
1131
        }
1132
    }
1133

1134
    return true;
×
1135
}
1136

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