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

mcallegari / qlcplus / 9242447442

26 May 2024 10:08AM UTC coverage: 31.637% (-0.4%) from 32.007%
9242447442

Pull #1422

github

web-flow
Merge 8b0046d03 into ff16fdc69
Pull Request #1422: RgbScript make stage colors available to scripts

60 of 910 new or added lines in 11 files covered. (6.59%)

9 existing lines in 4 files now uncovered.

15421 of 48743 relevant lines covered (31.64%)

22692.38 hits per line

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

40.69
/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 KXMLQLCRGBMatrixColor1          QString("Color1")
40
#define KXMLQLCRGBMatrixColor2          QString("Color2")
41
#define KXMLQLCRGBMatrixColor3          QString("Color3")
42
#define KXMLQLCRGBMatrixColor4          QString("Color4")
43
#define KXMLQLCRGBMatrixColor5          QString("Color5")
44

45
#define KXMLQLCRGBMatrixFixtureGroup    QString("FixtureGroup")
46
#define KXMLQLCRGBMatrixDimmerControl   QString("DimmerControl")
47

48
#define KXMLQLCRGBMatrixProperty        QString("Property")
49
#define KXMLQLCRGBMatrixPropertyName    QString("Name")
50
#define KXMLQLCRGBMatrixPropertyValue   QString("Value")
51

52
#define KXMLQLCRGBMatrixControlMode         QString("ControlMode")
53
#define KXMLQLCRGBMatrixControlModeRgb      QString("RGB")
54
#define KXMLQLCRGBMatrixControlModeAmber    QString("Amber")
55
#define KXMLQLCRGBMatrixControlModeWhite    QString("White")
56
#define KXMLQLCRGBMatrixControlModeUV       QString("UV")
57
#define KXMLQLCRGBMatrixControlModeDimmer   QString("Dimmer")
58
#define KXMLQLCRGBMatrixControlModeShutter  QString("Shutter")
59

60
/****************************************************************************
61
 * Initialization
62
 ****************************************************************************/
63

64
RGBMatrix::RGBMatrix(Doc* doc)
9✔
65
    : Function(doc, Function::RGBMatrixType)
66
    , m_dimmerControl(false)
67
    , m_fixtureGroupID(FixtureGroup::invalidId())
18✔
68
    , m_group(NULL)
69
    , m_algorithm(NULL)
70
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
71
    , m_algorithmMutex(QMutex::Recursive)
72
#endif
73
    , m_rgbColors{
74
        Qt::red,
75
        QColor(),
76
        QColor(),
77
        QColor(),
78
        QColor()
79
    }
80
    , m_stepHandler(new RGBMatrixStep())
9✔
81
    , m_roundTime(new QElapsedTimer())
9✔
82
    , m_stepsCount(0)
83
    , m_stepBeatDuration(0)
84
    , m_controlMode(RGBMatrix::ControlModeRgb)
18✔
85
{
86
    setName(tr("New RGB Matrix"));
9✔
87
    setDuration(500);
9✔
88

89
    RGBScript scr = doc->rgbScriptsCache()->script("Stripes");
18✔
90
    setAlgorithm(scr.clone());
9✔
91
}
9✔
92

93
RGBMatrix::~RGBMatrix()
11✔
94
{
95
    delete m_algorithm;
9✔
96
    delete m_roundTime;
9✔
97
    delete m_stepHandler;
9✔
98
}
11✔
99

100
QIcon RGBMatrix::getIcon() const
×
101
{
102
    return QIcon(":/rgbmatrix.png");
×
103
}
104

105
void RGBMatrix::setTotalDuration(quint32 msec)
1✔
106
{
107
    QMutexLocker algorithmLocker(&m_algorithmMutex);
1✔
108

109
    if (m_algorithm == NULL)
1✔
110
        return;
×
111

112
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
1✔
113
    if (grp == NULL)
1✔
114
        return;
×
115

116
    int steps = m_algorithm->rgbMapStepCount(grp->size());
1✔
117
    setDuration(msec / steps);
1✔
118
}
119

120
quint32 RGBMatrix::totalDuration()
3✔
121
{
122
    QMutexLocker algorithmLocker(&m_algorithmMutex);
6✔
123

124
    if (m_algorithm == NULL)
3✔
125
        return 0;
×
126

127
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
3✔
128
    if (grp == NULL)
3✔
129
        return 0;
1✔
130

131
    qDebug () << "Algorithm steps:" << m_algorithm->rgbMapStepCount(grp->size());
2✔
132
    return m_algorithm->rgbMapStepCount(grp->size()) * duration();
2✔
133
}
134

135
void RGBMatrix::setDimmerControl(bool dimmerControl)
1✔
136
{
137
    m_dimmerControl = dimmerControl;
1✔
138
}
1✔
139

140
bool RGBMatrix::dimmerControl() const
2✔
141
{
142
    return m_dimmerControl;
2✔
143
}
144

145
/****************************************************************************
146
 * Copying
147
 ****************************************************************************/
148

149
Function* RGBMatrix::createCopy(Doc* doc, bool addToDoc)
1✔
150
{
151
    Q_ASSERT(doc != NULL);
1✔
152

153
    Function* copy = new RGBMatrix(doc);
1✔
154
    if (copy->copyFrom(this) == false)
1✔
155
    {
156
        delete copy;
×
157
        copy = NULL;
×
158
    }
159
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
160
    {
161
        delete copy;
×
162
        copy = NULL;
×
163
    }
164

165
    return copy;
1✔
166
}
167

168
bool RGBMatrix::copyFrom(const Function* function)
1✔
169
{
170
    const RGBMatrix* mtx = qobject_cast<const RGBMatrix*> (function);
1✔
171
    if (mtx == NULL)
1✔
172
        return false;
×
173

174
    setDimmerControl(mtx->dimmerControl());
1✔
175
    setFixtureGroup(mtx->fixtureGroup());
1✔
176
    if (mtx->algorithm() != NULL)
1✔
177
        setAlgorithm(mtx->algorithm()->clone());
1✔
178
    else
179
        setAlgorithm(NULL);
×
180

181
    for(unsigned int i = 0; i < RGBAlgorithmRawColorCount; i++)
6✔
182
        setColor(i, mtx->getColor(i));
5✔
183
    setControlMode(mtx->controlMode());
1✔
184

185
    return Function::copyFrom(function);
1✔
186
}
187

188
/****************************************************************************
189
 * Fixtures
190
 ****************************************************************************/
191

192
quint32 RGBMatrix::fixtureGroup() const
37✔
193
{
194
    return m_fixtureGroupID;
37✔
195
}
196

197
void RGBMatrix::setFixtureGroup(quint32 id)
8✔
198
{
199
    m_fixtureGroupID = id;
8✔
200
    {
201
        QMutexLocker algoLocker(&m_algorithmMutex);
8✔
202
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
8✔
203
    }
204
    m_stepsCount = stepsCount();
8✔
205
}
8✔
206

207
QList<quint32> RGBMatrix::components()
2✔
208
{
209
    if (m_group != NULL)
2✔
210
        return m_group->fixtureList();
1✔
211

212
    return QList<quint32>();
1✔
213
}
214

215
/****************************************************************************
216
 * Algorithm
217
 ****************************************************************************/
218

219
void RGBMatrix::setAlgorithm(RGBAlgorithm* algo)
13✔
220
{
221
    {
222
        QMutexLocker algorithmLocker(&m_algorithmMutex);
26✔
223
        delete m_algorithm;
13✔
224
        m_algorithm = algo;
13✔
225

226
        /** If there's been a change of Script algorithm "on the fly",
227
         *  then re-apply the properties currently set in this RGBMatrix */
228
        if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
13✔
229
        {
230
            RGBScript *script = static_cast<RGBScript*> (m_algorithm);
13✔
231
            QHashIterator<QString, QString> it(m_properties);
26✔
232
            while (it.hasNext())
13✔
233
            {
234
                it.next();
×
235
                if (script->setProperty(it.key(), it.value()) == false)
×
236
                {
237
                    /** If the new algorithm doesn't expose a property,
238
                     *  then remove it from the cached list, otherwise
239
                     *  it would be carried around forever (and saved on XML) */
240
                    m_properties.take(it.key());
×
241
                }
242
            }
243
        }
244
    }
245
    m_stepsCount = stepsCount();
13✔
246

247
    emit changed(id());
13✔
248
}
13✔
249

250
RGBAlgorithm* RGBMatrix::algorithm() const
18✔
251
{
252
    return m_algorithm;
18✔
253
}
254

255
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
256
QMutex& RGBMatrix::algorithmMutex()
257
{
258
    return m_algorithmMutex;
259
}
260
#else
261
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
262
{
263
    return m_algorithmMutex;
×
264
}
265
#endif
266

267

268
int RGBMatrix::stepsCount()
24✔
269
{
270
    QMutexLocker algorithmLocker(&m_algorithmMutex);
48✔
271

272
    if (m_algorithm == NULL)
24✔
273
        return 0;
×
274

275
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
24✔
276
    if (grp != NULL)
24✔
277
        return m_algorithm->rgbMapStepCount(grp->size());
7✔
278

279
    return 0;
17✔
280
}
281

282
void RGBMatrix::previewMap(int step, RGBMatrixStep *handler)
7✔
283
{
284
    QMutexLocker algorithmLocker(&m_algorithmMutex);
7✔
285
    if (m_algorithm == NULL || handler == NULL)
7✔
286
        return;
×
287

288
    if (m_group == NULL)
7✔
289
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
290

291
    if (m_group != NULL) {
7✔
292
        Q_ASSERT(5 == RGBAlgorithmRawColorCount);
293
        uint rawColors[] = {
294
            m_rgbColors[0].rgb()
6✔
295
            ,m_rgbColors[1].isValid() ? m_rgbColors[1].rgb() : 0
6✔
296
            ,m_rgbColors[2].isValid() ? m_rgbColors[2].rgb() : 0
6✔
297
            ,m_rgbColors[3].isValid() ? m_rgbColors[3].rgb() : 0
6✔
298
            ,m_rgbColors[4].isValid() ? m_rgbColors[4].rgb() : 0
6✔
299
        };
24✔
300
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map, rawColors);
6✔
301
    }
302
}
303

304
/****************************************************************************
305
 * Color
306
 ****************************************************************************/
307

308
void RGBMatrix::setColor(unsigned int i, QColor c)
21✔
309
{
310
    if (i >= RGBAlgorithmRawColorCount)
21✔
NEW
311
        return;
×
312
    m_rgbColors[i] = c;
21✔
313
    {
314
        QMutexLocker algorithmLocker(&m_algorithmMutex);
42✔
315
        if (m_algorithm != NULL)
21✔
316
        {
317
            m_algorithm->setColors(&m_rgbColors[0]);
21✔
318
            updateColorDelta();
21✔
319
        }
320
    }
321
    emit changed(id());
21✔
322
}
323

324
QColor RGBMatrix::getColor(unsigned int i) const
24✔
325
{
326
    return m_rgbColors[i];
24✔
327
}
328

329
void RGBMatrix::updateColorDelta()
21✔
330
{
331
    m_stepHandler->calculateColorDelta(m_rgbColors[0], m_rgbColors[1], m_algorithm);
21✔
332
}
21✔
333

334
/************************************************************************
335
 * Properties
336
 ************************************************************************/
337

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

350
QString RGBMatrix::property(QString propName)
3✔
351
{
352
    QMutexLocker algoLocker(&m_algorithmMutex);
6✔
353

354
    /** If the property is cached, then return it right away */
355
    if (m_properties.contains(propName))
3✔
356
        return m_properties[propName];
1✔
357

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

365
    return QString();
×
366
}
367

368
/****************************************************************************
369
 * Load & Save
370
 ****************************************************************************/
371

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

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

386
    /* Load matrix contents */
387
    while (root.readNextStartElement())
12✔
388
    {
389
        if (root.name() == KXMLQLCFunctionSpeed)
11✔
390
        {
391
            loadXMLSpeed(root);
1✔
392
        }
393
        else if (root.name() == KXMLQLCRGBAlgorithm)
10✔
394
        {
395
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
396
        }
397
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
9✔
398
        {
399
            setFixtureGroup(root.readElementText().toUInt());
1✔
400
        }
401
        else if (root.name() == KXMLQLCFunctionDirection)
8✔
402
        {
403
            loadXMLDirection(root);
1✔
404
        }
405
        else if (root.name() == KXMLQLCFunctionRunOrder)
7✔
406
        {
407
            loadXMLRunOrder(root);
1✔
408
        }
409
#if (5 != RGBAlgorithmRawColorCount)
410
#error "Further colors need to be read."
411
#endif
412
        else if (root.name() == KXMLQLCRGBMatrixStartColor
6✔
413
                || root.name() == KXMLQLCRGBMatrixColor1)
6✔
414
        {
415
            setColor(0, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
416
        }
417
        else if (root.name() == KXMLQLCRGBMatrixEndColor
5✔
418
                || root.name() == KXMLQLCRGBMatrixColor2)
5✔
419
        {
420
            setColor(1, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
421
        }
422
        else if (root.name() == KXMLQLCRGBMatrixColor3)
4✔
423
        {
424
            setColor(2, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
425
        }
426
        else if (root.name() == KXMLQLCRGBMatrixColor4)
3✔
427
        {
428
            setColor(3, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
429
        }
430
        else if (root.name() == KXMLQLCRGBMatrixColor5)
2✔
431
        {
432
            setColor(4, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
1✔
433
        }
434
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
1✔
435
        {
436
            setControlMode(stringToControlMode(root.readElementText()));
1✔
437
        }
438
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
439
        {
440
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
441
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
442
            setProperty(name, value);
×
443
            root.skipCurrentElement();
×
444
        }
445
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
446
        {
447
            setDimmerControl(root.readElementText().toInt());
×
448
        }
449
        else
450
        {
451
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
452
            root.skipCurrentElement();
×
453
        }
454
    }
455

456
    return true;
1✔
457
}
458

459
bool RGBMatrix::saveXML(QXmlStreamWriter *doc)
1✔
460
{
461
    Q_ASSERT(doc != NULL);
1✔
462

463
    /* Function tag */
464
    doc->writeStartElement(KXMLQLCFunction);
1✔
465

466
    /* Common attributes */
467
    saveXMLCommon(doc);
1✔
468

469
    /* Speeds */
470
    saveXMLSpeed(doc);
1✔
471

472
    /* Direction */
473
    saveXMLDirection(doc);
1✔
474

475
    /* Run order */
476
    saveXMLRunOrder(doc);
1✔
477

478
    /* Algorithm */
479
    if (m_algorithm != NULL)
1✔
480
        m_algorithm->saveXML(doc);
1✔
481

482
    /* LEGACY - Dimmer Control */
483
    if (dimmerControl())
1✔
484
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
485

486
    /* Color 1 */
487
    doc->writeTextElement(KXMLQLCRGBMatrixColor1, QString::number(getColor(0).rgb()));
1✔
488

489
    /* Color 2 */
490
    if (getColor(1).isValid())
1✔
491
        doc->writeTextElement(KXMLQLCRGBMatrixColor2, QString::number(getColor(1).rgb()));
1✔
492

493
    /* Color 2 */
494
    if (getColor(2).isValid())
1✔
495
        doc->writeTextElement(KXMLQLCRGBMatrixColor3, QString::number(getColor(2).rgb()));
1✔
496

497
    /* Color 2 */
498
    if (getColor(3).isValid())
1✔
499
        doc->writeTextElement(KXMLQLCRGBMatrixColor4, QString::number(getColor(3).rgb()));
1✔
500

501
    /* Color 5 */
502
    if (getColor(4).isValid())
1✔
503
        doc->writeTextElement(KXMLQLCRGBMatrixColor5, QString::number(getColor(4).rgb()));
1✔
504
    Q_ASSERT(5 == RGBAlgorithmRawColorCount);
505

506
    /* Control Mode */
507
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
1✔
508

509
    /* Fixture Group */
510
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
1✔
511

512
    /* Properties */
513
    QHashIterator<QString, QString> it(m_properties);
1✔
514
    while (it.hasNext())
1✔
515
    {
516
        it.next();
×
517
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
518
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
519
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
520
        doc->writeEndElement();
×
521
    }
522

523
    /* End the <Function> tag */
524
    doc->writeEndElement();
1✔
525

526
    return true;
2✔
527
}
528

529
/****************************************************************************
530
 * Running
531
 ****************************************************************************/
532

533
void RGBMatrix::tap()
×
534
{
535
    if (stopped() == false)
×
536
    {
537
        FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
×
538
        // Filter out taps that are too close to each other
539
        if (grp != NULL && uint(m_roundTime->elapsed()) >= (duration() / 4))
×
540
        {
541
            roundCheck();
×
542
            resetElapsed();
×
543
        }
544
    }
545
}
×
546

547
void RGBMatrix::preRun(MasterTimer *timer)
×
548
{
549
    {
550
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
551

552
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
553
        if (m_group == NULL)
×
554
        {
555
            // No fixture group to control
556
            stop(FunctionParent::master());
×
557
            return;
×
558
        }
559

560
        if (m_algorithm != NULL)
×
561
        {
562
            // Copy direction from parent class direction
NEW
563
            m_stepHandler->initializeDirection(direction(), m_rgbColors[0], m_rgbColors[1], m_stepsCount, m_algorithm);
×
564

565
            if (m_algorithm->type() == RGBAlgorithm::Script)
×
566
            {
567
                RGBScript *script = static_cast<RGBScript*> (m_algorithm);
×
568
                QHashIterator<QString, QString> it(m_properties);
×
569
                while (it.hasNext())
×
570
                {
571
                    it.next();
×
572
                    script->setProperty(it.key(), it.value());
×
573
                }
574
            }
575
        }
576
    }
577

578
    m_roundTime->restart();
×
579

580
    Function::preRun(timer);
×
581
}
582

583
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
584
{
585
    Q_UNUSED(timer);
586

587
    {
588
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
589
        if (m_group == NULL)
×
590
        {
591
            // No fixture group to control
592
            stop(FunctionParent::master());
×
593
            return;
×
594
        }
595

596
        // No time to do anything.
597
        if (duration() == 0)
×
598
            return;
×
599

600
        // Invalid/nonexistent script
601
        if (m_algorithm == NULL || m_algorithm->apiVersion() == 0)
×
602
            return;
×
603

604
        if (isPaused() == false)
×
605
        {
606
            // Get a new map every time elapsed is reset to zero
607
            if (elapsed() < MasterTimer::tick())
×
608
            {
609
                if (tempoType() == Beats)
×
610
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
611

612
                Q_ASSERT(5 == RGBAlgorithmRawColorCount);
613
                uint rawColors[] = {
NEW
614
                    m_rgbColors[0].rgb()
×
NEW
615
                    ,m_rgbColors[1].isValid() ? m_rgbColors[1].rgb() : 0
×
NEW
616
                    ,m_rgbColors[2].isValid() ? m_rgbColors[2].rgb() : 0
×
NEW
617
                    ,m_rgbColors[3].isValid() ? m_rgbColors[3].rgb() : 0
×
NEW
618
                    ,m_rgbColors[4].isValid() ? m_rgbColors[4].rgb() : 0
×
NEW
619
                };
×
620
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
621
                m_algorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
NEW
622
                                    m_stepHandler->currentStepIndex(), m_stepHandler->m_map, rawColors);
×
623
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
624
            }
625
        }
626
    }
627

628
    if (isPaused() == false)
×
629
    {
630
        // Increment the ms elapsed time
631
        incrementElapsed();
×
632

633
        /* Check if we need to change direction, stop completely or go to next step
634
         * The cases are:
635
         * 1- time tempo type: act normally, on ms elapsed time
636
         * 2- beat tempo type, beat occurred: check if the elapsed beats is a multiple of
637
         *    the step beat duration. If so, proceed to the next step
638
         * 3- beat tempo type, not beat: if the ms elapsed time reached the step beat
639
         *    duration in ms, and the ms time to the next beat is not less than 1/16 of
640
         *    the step beat duration in ms, then proceed to the next step. If the ms time to the
641
         *    next beat is less than 1/16 of the step beat duration in ms, then defer the step
642
         *    change to case #2, to resync the matrix to the next beat
643
         */
644
        if (tempoType() == Time && elapsed() >= duration())
×
645
        {
646
            roundCheck();
×
647
        }
648
        else if (tempoType() == Beats)
×
649
        {
650
            if (timer->isBeat())
×
651
            {
652
                incrementElapsedBeats();
×
653
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
×
654
                if (elapsedBeats() % duration() == 0)
×
655
                {
656
                    roundCheck();
×
657
                    resetElapsed();
×
658
                }
659
            }
660
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
661
            {
662
                qDebug() << "Elapsed exceeded";
×
663
                roundCheck();
×
664
            }
665
        }
666
    }
667
}
668

669
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
670
{
671
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
672

673
    /* If no fade out is needed, dismiss all the requested faders.
674
     * Otherwise, set all the faders to fade out and let Universe dismiss them
675
     * when done */
676
    if (fadeout == 0)
×
677
    {
678
        dismissAllFaders();
×
679
    }
680
    else
681
    {
682
        if (tempoType() == Beats)
×
683
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
684

685
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
686
        {
687
            if (!fader.isNull())
×
688
                fader->setFadeOut(true, fadeout);
×
689
        }
690
    }
691

692
    m_fadersMap.clear();
×
693

694
    {
695
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
696
        if (m_algorithm != NULL)
×
697
            m_algorithm->postRun();
×
698
    }
699

700
    Function::postRun(timer, universes);
×
701
}
×
702

703
void RGBMatrix::roundCheck()
×
704
{
705
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
706
    if (m_algorithm == NULL)
×
707
        return;
×
708

NEW
709
    if (m_stepHandler->checkNextStep(runOrder(), m_rgbColors[0], m_rgbColors[1], m_stepsCount) == false)
×
710
        stop(FunctionParent::master());
×
711

712
    m_roundTime->restart();
×
713

714
    if (tempoType() == Beats)
×
715
        roundElapsed(m_stepBeatDuration);
×
716
    else
717
        roundElapsed(duration());
×
718
}
719

720
FadeChannel *RGBMatrix::getFader(QList<Universe *> universes, quint32 universeID, quint32 fixtureID, quint32 channel)
×
721
{
722
    // get the universe Fader first. If doesn't exist, create it
723
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
×
724
    if (fader.isNull())
×
725
    {
726
        fader = universes[universeID]->requestFader();
×
727
        fader->adjustIntensity(getAttributeValue(Intensity));
×
728
        fader->setBlendMode(blendMode());
×
729
        fader->setName(name());
×
730
        fader->setParentFunctionID(id());
×
731
        m_fadersMap[universeID] = fader;
×
732
    }
733

734
    return fader->getChannelFader(doc(), universes[universeID], fixtureID, channel);
×
735
}
736

737
void RGBMatrix::updateFaderValues(FadeChannel *fc, uchar value, uint fadeTime)
×
738
{
739
    fc->setStart(fc->current());
×
740
    fc->setTarget(value);
×
741
    fc->setElapsed(0);
×
742
    fc->setReady(false);
×
743
    // fade in/out depends on target value
744
    if (value == 0)
×
745
        fc->setFadeTime(fadeOutSpeed());
×
746
    else
747
        fc->setFadeTime(fadeTime);
×
748
}
×
749

750
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
751
{
752
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
753

754
    // Create/modify fade channels for ALL heads in the group
755
    QMapIterator<QLCPoint, GroupHead> it(grp->headsMap());
×
756
    while (it.hasNext())
×
757
    {
758
        it.next();
×
759
        QLCPoint pt = it.key();
×
760
        GroupHead grpHead = it.value();
×
761
        Fixture *fxi = doc()->fixture(grpHead.fxi);
×
762
        if (fxi == NULL)
×
763
            continue;
×
764

765
        QLCFixtureHead head = fxi->head(grpHead.head);
×
766

767
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
768
            continue;
×
769

770
        uint col = map[pt.y()][pt.x()];
×
771

772
        if (m_controlMode == ControlModeRgb)
×
773
        {
774
            QVector <quint32> rgb = head.rgbChannels();
×
775
            QVector <quint32> cmy = head.cmyChannels();
×
776

777
            if (rgb.size() == 3)
×
778
            {
779
                // RGB color mixing
780
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(0));
×
781
                updateFaderValues(fc, qRed(col), fadeTime);
×
782

783
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(1));
×
784
                updateFaderValues(fc, qGreen(col), fadeTime);
×
785

786
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(2));
×
787
                updateFaderValues(fc, qBlue(col), fadeTime);
×
788
            }
789
            else if (cmy.size() == 3)
×
790
            {
791
                // CMY color mixing
792
                QColor cmyCol(col);
×
793

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

797
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(1));
×
798
                updateFaderValues(fc, cmyCol.magenta(), fadeTime);
×
799

800
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(2));
×
801
                updateFaderValues(fc, cmyCol.yellow(), fadeTime);
×
802
            }
803
        }
804
        else if (m_controlMode == ControlModeWhite)
×
805
        {
806
            quint32 white = head.channelNumber(QLCChannel::White, QLCChannel::MSB);
×
807

808
            if (white != QLCChannel::invalid())
×
809
            {
810
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, white);
×
811
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
812
            }
813
        }
814
        else if (m_controlMode == ControlModeAmber)
×
815
        {
816
            quint32 amber = head.channelNumber(QLCChannel::Amber, QLCChannel::MSB);
×
817

818
            if (amber != QLCChannel::invalid())
×
819
            {
820
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, amber);
×
821
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
822
            }
823
        }
824
        else if (m_controlMode == ControlModeUV)
×
825
        {
826
            quint32 uv = head.channelNumber(QLCChannel::UV, QLCChannel::MSB);
×
827

828
            if (uv != QLCChannel::invalid())
×
829
            {
830
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, uv);
×
831
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
832
            }
833
        }
834
        else if (m_controlMode == ControlModeShutter)
×
835
        {
836
            QVector <quint32> shutters = head.shutterChannels();
×
837

838
            if (shutters.size())
×
839
            {
840
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, shutters.first());
×
841
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
842
            }
843
        }
844

845
        if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
846
        {
847
            quint32 masterDim = fxi->masterIntensityChannel();
×
848
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
849
            QVector <quint32> dimmers;
×
850

851
            // Collect all dimmers that affect current head:
852
            // They are the master dimmer (affects whole fixture)
853
            // and per-head dimmer.
854
            //
855
            // If there are no RGB or CMY channels, the least important* dimmer channel
856
            // is used to create grayscale image.
857
            //
858
            // The rest of the dimmer channels are set to full if dimmer control is
859
            // enabled and target color is > 0 (see
860
            // http://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
861
            //
862
            // Note: If there is only one head, and only one dimmer channel,
863
            // make it a master dimmer in fixture definition.
864
            //
865
            // *least important - per head dimmer if present,
866
            // otherwise per fixture dimmer if present
867

868
            if (masterDim != QLCChannel::invalid())
×
869
                dimmers << masterDim;
×
870

871
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
872
                dimmers << headDim;
×
873

874
            if (dimmers.size())
×
875
            {
876
                // Set dimmer to value of the color (e.g. for PARs)
877
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, dimmers.last());
×
878
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
879
                dimmers.pop_back();
×
880
            }
881

882
            // Set the rest of the dimmer channels to full on
883
            foreach (quint32 ch, dimmers)
×
884
            {
885
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, ch);
×
886
                updateFaderValues(fc, col == 0 ? 0 : 255, fadeTime);
×
887
            }
888
        }
889
    }
890
}
×
891

892
uchar RGBMatrix::rgbToGrey(uint col)
×
893
{
894
    // the weights are taken from
895
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
896
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
897
}
898

899
/*********************************************************************
900
 * Attributes
901
 *********************************************************************/
902

903
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
904
{
905
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
906

907
    if (attrIndex == Intensity)
×
908
    {
909
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
910
        {
911
            if (!fader.isNull())
×
912
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
913
        }
914
    }
915

916
    return attrIndex;
×
917
}
918

919
/*************************************************************************
920
 * Blend mode
921
 *************************************************************************/
922

923
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
924
{
925
    if (mode == blendMode())
×
926
        return;
×
927

928
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
929
    {
930
        if (!fader.isNull())
×
931
            fader->setBlendMode(mode);
×
932
    }
933

934
    Function::setBlendMode(mode);
×
935
    emit changed(id());
×
936
}
937

938
/*************************************************************************
939
 * Control Mode
940
 *************************************************************************/
941

942
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
943
{
944
    return m_controlMode;
2✔
945
}
946

947
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
948
{
949
    m_controlMode = mode;
3✔
950
    emit changed(id());
3✔
951
}
3✔
952

953
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
954
{
955
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
956
        return ControlModeRgb;
1✔
957
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
958
        return ControlModeAmber;
×
959
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
960
        return ControlModeWhite;
×
961
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
962
        return ControlModeUV;
×
963
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
964
        return ControlModeDimmer;
×
965
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
966
        return ControlModeShutter;
×
967

968
    return ControlModeRgb;
×
969
}
970

971
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
972
{
973
    switch(mode)
1✔
974
    {
975
        default:
1✔
976
        case ControlModeRgb:
977
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
978
        break;
979
        case ControlModeAmber:
×
980
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
981
        break;
982
        case ControlModeWhite:
×
983
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
984
        break;
985
        case ControlModeUV:
×
986
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
987
        break;
988
        case ControlModeDimmer:
×
989
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
990
        break;
991
        case ControlModeShutter:
×
992
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
993
        break;
994
    }
995
}
996

997

998
/*************************************************************************
999
 *************************************************************************
1000
 *                          RGBMatrixStep class
1001
 *************************************************************************
1002
 *************************************************************************/
1003

1004
RGBMatrixStep::RGBMatrixStep()
10✔
1005
    : m_direction(Function::Forward)
1006
    , m_currentStepIndex(0)
1007
    , m_stepColor(QColor())
1008
    , m_crDelta(0)
1009
    , m_cgDelta(0)
1010
    , m_cbDelta(0)
10✔
1011
{
1012

1013
}
10✔
1014

1015
void RGBMatrixStep::setCurrentStepIndex(int index)
×
1016
{
1017
    m_currentStepIndex = index;
×
1018
}
×
1019

1020
int RGBMatrixStep::currentStepIndex() const
1✔
1021
{
1022
    return m_currentStepIndex;
1✔
1023
}
1024

1025
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor, RGBAlgorithm *algorithm)
21✔
1026
{
1027
    m_crDelta = 0;
21✔
1028
    m_cgDelta = 0;
21✔
1029
    m_cbDelta = 0;
21✔
1030

1031
    if (endColor.isValid() && algorithm != NULL && algorithm->acceptColors() > 1)
21✔
1032
    {
1033
        m_crDelta = endColor.red() - startColor.red();
14✔
1034
        m_cgDelta = endColor.green() - startColor.green();
14✔
1035
        m_cbDelta = endColor.blue() - startColor.blue();
14✔
1036

1037
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
1038
    }
1039
}
21✔
1040

1041
void RGBMatrixStep::setStepColor(QColor color)
×
1042
{
1043
    m_stepColor = color;
×
1044
}
×
1045

1046
QColor RGBMatrixStep::stepColor()
6✔
1047
{
1048
    return m_stepColor;
6✔
1049
}
1050

1051
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1052
{
1053
    if (stepsCount <= 0)
×
1054
        return;
×
1055

1056
    if (stepsCount == 1)
×
1057
    {
1058
        m_stepColor = startColor;
×
1059
    }
1060
    else
1061
    {
1062
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
1063
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1064
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1065
    }
1066

1067
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1068
}
1069

NEW
1070
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount, RGBAlgorithm *algorithm)
×
1071
{
1072
    m_direction = direction;
×
1073

1074
    if (m_direction == Function::Forward)
×
1075
    {
1076
        setCurrentStepIndex(0);
×
1077
        setStepColor(startColor);
×
1078
    }
1079
    else
1080
    {
1081
        setCurrentStepIndex(stepsCount - 1);
×
1082

1083
        if (endColor.isValid())
×
1084
            setStepColor(endColor);
×
1085
        else
1086
            setStepColor(startColor);
×
1087
    }
1088

NEW
1089
    calculateColorDelta(startColor, endColor, algorithm);
×
1090
}
×
1091

1092
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1093
                                  QColor startColor, QColor endColor, int stepsNumber)
1094
{
1095
    if (order == Function::PingPong)
×
1096
    {
1097
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1098
        {
1099
            m_direction = Function::Backward;
×
1100
            m_currentStepIndex = stepsNumber - 2;
×
1101
            if (endColor.isValid())
×
1102
                m_stepColor = endColor;
×
1103

1104
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1105
        }
1106
        else if (m_direction == Function::Backward && (m_currentStepIndex - 1) < 0)
×
1107
        {
1108
            m_direction = Function::Forward;
×
1109
            m_currentStepIndex = 1;
×
1110
            m_stepColor = startColor;
×
1111
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1112
        }
1113
        else
1114
        {
1115
            if (m_direction == Function::Forward)
×
1116
                m_currentStepIndex++;
×
1117
            else
1118
                m_currentStepIndex--;
×
1119
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1120
        }
1121
    }
1122
    else if (order == Function::SingleShot)
×
1123
    {
1124
        if (m_direction == Function::Forward)
×
1125
        {
1126
            if (m_currentStepIndex >= stepsNumber - 1)
×
1127
                return false;
×
1128
            else
1129
            {
1130
                m_currentStepIndex++;
×
1131
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1132
            }
1133
        }
1134
        else
1135
        {
1136
            if (m_currentStepIndex <= 0)
×
1137
                return false;
×
1138
            else
1139
            {
1140
                m_currentStepIndex--;
×
1141
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1142
            }
1143
        }
1144
    }
1145
    else
1146
    {
1147
        if (m_direction == Function::Forward)
×
1148
        {
1149
            if (m_currentStepIndex >= stepsNumber - 1)
×
1150
            {
1151
                m_currentStepIndex = 0;
×
1152
                m_stepColor = startColor;
×
1153
            }
1154
            else
1155
            {
1156
                m_currentStepIndex++;
×
1157
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1158
            }
1159
        }
1160
        else
1161
        {
1162
            if (m_currentStepIndex <= 0)
×
1163
            {
1164
                m_currentStepIndex = stepsNumber - 1;
×
1165
                if (endColor.isValid())
×
1166
                    m_stepColor = endColor;
×
1167
            }
1168
            else
1169
            {
1170
                m_currentStepIndex--;
×
1171
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1172
            }
1173
        }
1174
    }
1175

1176
    return true;
×
1177
}
1178

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