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

mcallegari / qlcplus / 11388022654

17 Oct 2024 03:21PM UTC coverage: 31.573% (-0.4%) from 31.983%
11388022654

Pull #1422

github

web-flow
Merge 4147c937e into 5f77fc96f
Pull Request #1422: RgbScript make stage colors available to scripts

56 of 908 new or added lines in 11 files covered. (6.17%)

12 existing lines in 8 files now uncovered.

14057 of 44522 relevant lines covered (31.57%)

26666.02 hits per line

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

40.87
/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 KXMLQLCRGBMatrixColorBase       QString("Color")
40

41
#define KXMLQLCRGBMatrixFixtureGroup    QString("FixtureGroup")
42
#define KXMLQLCRGBMatrixDimmerControl   QString("DimmerControl")
43

44
#define KXMLQLCRGBMatrixProperty        QString("Property")
45
#define KXMLQLCRGBMatrixPropertyName    QString("Name")
46
#define KXMLQLCRGBMatrixPropertyValue   QString("Value")
47

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

56
/****************************************************************************
57
 * Initialization
58
 ****************************************************************************/
59

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

85
    RGBScript scr = doc->rgbScriptsCache()->script("Stripes");
18✔
86
    setAlgorithm(scr.clone());
9✔
87
}
9✔
88

89
RGBMatrix::~RGBMatrix()
11✔
90
{
91
    delete m_algorithm;
9✔
92
    delete m_roundTime;
9✔
93
    delete m_stepHandler;
18✔
94
}
11✔
95

96
QIcon RGBMatrix::getIcon() const
×
97
{
98
    return QIcon(":/rgbmatrix.png");
×
99
}
100

101
void RGBMatrix::setTotalDuration(quint32 msec)
1✔
102
{
103
    QMutexLocker algorithmLocker(&m_algorithmMutex);
1✔
104

105
    if (m_algorithm == NULL)
1✔
106
        return;
107

108
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
1✔
109
    if (grp == NULL)
1✔
110
        return;
111

112
    int steps = m_algorithm->rgbMapStepCount(grp->size());
1✔
113
    setDuration(msec / steps);
1✔
114
}
115

116
quint32 RGBMatrix::totalDuration()
3✔
117
{
118
    QMutexLocker algorithmLocker(&m_algorithmMutex);
3✔
119

120
    if (m_algorithm == NULL)
3✔
121
        return 0;
122

123
    FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
3✔
124
    if (grp == NULL)
3✔
125
        return 0;
126

127
    //qDebug () << "Algorithm steps:" << m_algorithm->rgbMapStepCount(grp->size());
128
    return m_algorithm->rgbMapStepCount(grp->size()) * duration();
2✔
129
}
130

131
void RGBMatrix::setDimmerControl(bool dimmerControl)
1✔
132
{
133
    m_dimmerControl = dimmerControl;
1✔
134
}
1✔
135

136
bool RGBMatrix::dimmerControl() const
2✔
137
{
138
    return m_dimmerControl;
2✔
139
}
140

141
/****************************************************************************
142
 * Copying
143
 ****************************************************************************/
144

145
Function* RGBMatrix::createCopy(Doc* doc, bool addToDoc)
1✔
146
{
147
    Q_ASSERT(doc != NULL);
148

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

161
    return copy;
1✔
162
}
163

164
bool RGBMatrix::copyFrom(const Function* function)
1✔
165
{
166
    const RGBMatrix* mtx = qobject_cast<const RGBMatrix*> (function);
167
    if (mtx == NULL)
1✔
168
        return false;
169

170
    setDimmerControl(mtx->dimmerControl());
1✔
171
    setFixtureGroup(mtx->fixtureGroup());
1✔
172
    if (mtx->algorithm() != NULL)
1✔
173
        setAlgorithm(mtx->algorithm()->clone());
1✔
174
    else
175
        setAlgorithm(NULL);
×
176

177
    QVectorIterator<QColor> it(mtx->getColors());
2✔
178
    uint count = 0;
179
    while (it.hasNext()) {
6✔
180
        QColor color = it.next();
181
        setColor(count, color);
5✔
182
        count ++;
5✔
183
    }
184

185
    setControlMode(mtx->controlMode());
1✔
186

187
    return Function::copyFrom(function);
1✔
188
}
189

190
/****************************************************************************
191
 * Fixtures
192
 ****************************************************************************/
193

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

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

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

214
    return QList<quint32>();
215
}
216

217
/****************************************************************************
218
 * Algorithm
219
 ****************************************************************************/
220

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

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

249
    emit changed(id());
13✔
250
}
13✔
251

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

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

269

270
int RGBMatrix::stepsCount()
24✔
271
{
272
    QMutexLocker algorithmLocker(&m_algorithmMutex);
24✔
273

274
    if (m_algorithm == NULL)
24✔
275
        return 0;
276

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

281
    return 0;
282
}
283

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

290
    if (m_group == NULL)
7✔
291
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
292

293
    if (m_group != NULL)
7✔
294
    {
295
        setMapColors();
6✔
296
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map);
6✔
297
    }
298
}
299

300
/****************************************************************************
301
 * Color
302
 ****************************************************************************/
303

304
void RGBMatrix::setColor(int i, QColor c)
21✔
305
{
306
    if (i >= m_rgbColors.count())
21✔
NEW
307
        m_rgbColors.resize(i + 1);
×
308
    m_rgbColors.replace(i, c);
21✔
309
    {
310
        QMutexLocker algorithmLocker(&m_algorithmMutex);
21✔
311
        if (m_algorithm != NULL)
21✔
312
        {
313
            m_algorithm->setColors(m_rgbColors);
21✔
314
            updateColorDelta();
21✔
315
        }
316
    }
317
    setMapColors();
21✔
318
    emit changed(id());
21✔
319
}
21✔
320

321
QColor RGBMatrix::getColor(int i) const
10✔
322
{
323
    if (i >= m_rgbColors.count())
10✔
324
        return QColor();
325
    return m_rgbColors.at(i);
326
}
327

328
QVector<QColor> RGBMatrix::getColors() const
1✔
329
{
330
    return m_rgbColors;
1✔
331
}
332

333
void RGBMatrix::updateColorDelta()
21✔
334
{
335
    m_stepHandler->calculateColorDelta(m_rgbColors[0], m_rgbColors[1], m_algorithm);
42✔
336
}
21✔
337

338
void RGBMatrix::setMapColors()
27✔
339
{
340
    QMutexLocker algorithmLocker(&m_algorithmMutex);
27✔
341
    if (m_algorithm == NULL)
27✔
342
        return;
343

344
    if (m_algorithm->apiVersion() < 3)
27✔
345
        return;
346

NEW
347
    if (m_group == NULL)
×
NEW
348
        m_group = doc()->fixtureGroup(fixtureGroup());
×
349

NEW
350
    if (m_group != NULL) {
×
NEW
351
        QVector<unsigned int> rawColors;
×
NEW
352
        int accColors = m_algorithm->acceptColors();
×
NEW
353
        rawColors.resize(accColors);
×
NEW
354
        QVectorIterator<QColor> it(m_rgbColors);
×
355
        int count = 0;
NEW
356
        while (it.hasNext() && count < accColors) {
×
357
            QColor color = it.next();
NEW
358
            rawColors.replace(count, color.isValid() ? color.rgb() : 0);
×
NEW
359
            count ++;
×
360
        };
NEW
361
        m_algorithm->rgbMapSetColors(rawColors);
×
362
    };
363
}
364

365
/************************************************************************
366
 * Properties
367
 ************************************************************************/
368

369
void RGBMatrix::setProperty(QString propName, QString value)
1✔
370
{
371
    QMutexLocker algoLocker(&m_algorithmMutex);
1✔
372
    m_properties[propName] = value;
1✔
373
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
1✔
374
    {
375
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
1✔
376
        script->setProperty(propName, value);
1✔
377
    }
378
    m_stepsCount = stepsCount();
1✔
379
}
1✔
380

381
QString RGBMatrix::property(QString propName)
3✔
382
{
383
    QMutexLocker algoLocker(&m_algorithmMutex);
3✔
384

385
    /** If the property is cached, then return it right away */
386
    if (m_properties.contains(propName))
3✔
387
        return m_properties[propName];
1✔
388

389
    /** Otherwise, let's retrieve it from the Script */
390
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
2✔
391
    {
392
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
2✔
393
        return script->property(propName);
2✔
394
    }
395

396
    return QString();
397
}
398

399
/****************************************************************************
400
 * Load & Save
401
 ****************************************************************************/
402

403
bool RGBMatrix::loadXML(QXmlStreamReader &root)
3✔
404
{
405
    if (root.name() != KXMLQLCFunction)
6✔
406
    {
407
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
408
        return false;
1✔
409
    }
410

411
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::RGBMatrixType))
4✔
412
    {
413
        qWarning() << Q_FUNC_INFO << "Function is not an RGB matrix";
1✔
414
        return false;
1✔
415
    }
416

417
    /* Load matrix contents */
418
    while (root.readNextStartElement())
12✔
419
    {
420
        if (root.name() == KXMLQLCFunctionSpeed)
22✔
421
        {
422
            loadXMLSpeed(root);
1✔
423
        }
424
        else if (root.name() == KXMLQLCRGBAlgorithm)
20✔
425
        {
426
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
427
        }
428
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
18✔
429
        {
430
            setFixtureGroup(root.readElementText().toUInt());
1✔
431
        }
432
        else if (root.name() == KXMLQLCFunctionDirection)
16✔
433
        {
434
            loadXMLDirection(root);
1✔
435
        }
436
        else if (root.name() == KXMLQLCFunctionRunOrder)
14✔
437
        {
438
            loadXMLRunOrder(root);
1✔
439
        }
440
        // Legacy support
441
        else if (root.name() == KXMLQLCRGBMatrixStartColor)
12✔
442
        {
NEW
443
            setColor(0, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
444
        }
445
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
12✔
446
        {
NEW
447
            setColor(1, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
448
        }
449
        else if (root.name().startsWith(KXMLQLCRGBMatrixColorBase, Qt::CaseSensitive))
6✔
450
        {
451
            QString colorNumText = root.name().string()->right(
5✔
452
                    root.name().length() - KXMLQLCRGBMatrixColorBase.length());
5✔
453
            uint colorIdx = colorNumText.toUInt() - 1;
5✔
454
            setColor(colorIdx, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
5✔
455
        }
456
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
2✔
457
        {
458
            setControlMode(stringToControlMode(root.readElementText()));
1✔
459
        }
460
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
461
        {
462
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
463
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
464
            setProperty(name, value);
×
465
            root.skipCurrentElement();
×
466
        }
467
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
468
        {
469
            setDimmerControl(root.readElementText().toInt());
×
470
        }
471
        else
472
        {
473
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
474
            root.skipCurrentElement();
×
475
        }
476
    }
477

478
    return true;
479
}
480

481
bool RGBMatrix::saveXML(QXmlStreamWriter *doc)
1✔
482
{
483
    Q_ASSERT(doc != NULL);
484

485
    /* Function tag */
486
    doc->writeStartElement(KXMLQLCFunction);
1✔
487

488
    /* Common attributes */
489
    saveXMLCommon(doc);
1✔
490

491
    /* Speeds */
492
    saveXMLSpeed(doc);
1✔
493

494
    /* Direction */
495
    saveXMLDirection(doc);
1✔
496

497
    /* Run order */
498
    saveXMLRunOrder(doc);
1✔
499

500
    /* Algorithm */
501
    if (m_algorithm != NULL)
1✔
502
        m_algorithm->saveXML(doc);
1✔
503

504
    /* LEGACY - Dimmer Control */
505
    if (dimmerControl())
1✔
506
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
507

508
    /* Colors */
509
    QVectorIterator<QColor> colorIt(m_rgbColors);
1✔
510
    uint count = 0;
511
    while (colorIt.hasNext()) {
6✔
512
        QColor color = colorIt.next();
513
        QString elementName = KXMLQLCRGBMatrixColorBase.append(QString::number(count + 1));
10✔
514
        doc->writeTextElement(elementName, QString::number(color.rgb()));
5✔
515
        count ++;
516
    }
517

518
    /* Control Mode */
519
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
1✔
520

521
    /* Fixture Group */
522
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
1✔
523

524
    /* Properties */
525
    QHashIterator<QString, QString> it(m_properties);
1✔
526
    while (it.hasNext())
1✔
527
    {
528
        it.next();
529
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
530
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
531
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
532
        doc->writeEndElement();
×
533
    }
534

535
    /* End the <Function> tag */
536
    doc->writeEndElement();
1✔
537

538
    return true;
1✔
539
}
540

541
/****************************************************************************
542
 * Running
543
 ****************************************************************************/
544

545
void RGBMatrix::tap()
×
546
{
547
    if (stopped() == false)
×
548
    {
549
        FixtureGroup* grp = doc()->fixtureGroup(fixtureGroup());
×
550
        // Filter out taps that are too close to each other
551
        if (grp != NULL && uint(m_roundTime->elapsed()) >= (duration() / 4))
×
552
        {
553
            roundCheck();
×
554
            resetElapsed();
×
555
        }
556
    }
557
}
×
558

559
void RGBMatrix::preRun(MasterTimer *timer)
×
560
{
561
    {
562
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
563

564
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
565
        if (m_group == NULL)
×
566
        {
567
            // No fixture group to control
568
            stop(FunctionParent::master());
×
569
            return;
570
        }
571

572
        if (m_algorithm != NULL)
×
573
        {
574
            // Copy direction from parent class direction
NEW
575
            m_stepHandler->initializeDirection(direction(), m_rgbColors[0], m_rgbColors[1], m_stepsCount, m_algorithm);
×
576

577
            if (m_algorithm->type() == RGBAlgorithm::Script)
×
578
            {
579
                RGBScript *script = static_cast<RGBScript*> (m_algorithm);
×
580
                QHashIterator<QString, QString> it(m_properties);
×
581
                while (it.hasNext())
×
582
                {
583
                    it.next();
584
                    script->setProperty(it.key(), it.value());
×
585
                }
586
            }
587
        }
588
    }
589

590
    m_roundTime->restart();
×
591

592
    Function::preRun(timer);
×
593
}
594

595
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
596
{
597
    Q_UNUSED(timer);
598

599
    {
600
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
601
        if (m_group == NULL)
×
602
        {
603
            // No fixture group to control
604
            stop(FunctionParent::master());
×
605
            return;
606
        }
607

608
        // No time to do anything.
609
        if (duration() == 0)
×
610
            return;
611

612
        // Invalid/nonexistent script
613
        if (m_algorithm == NULL || m_algorithm->apiVersion() == 0)
×
614
            return;
×
615

616
        if (isPaused() == false)
×
617
        {
618
            // Get a new map every time elapsed is reset to zero
619
            if (elapsed() < MasterTimer::tick())
×
620
            {
621
                if (tempoType() == Beats)
×
622
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
623

624
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
625
                m_algorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
626
                                    m_stepHandler->currentStepIndex(), m_stepHandler->m_map);
×
627
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
628
            }
629
        }
630
    }
631

632
    if (isPaused() == false)
×
633
    {
634
        // Increment the ms elapsed time
635
        incrementElapsed();
×
636

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

673
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
674
{
675
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
676

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

689
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
690
        {
691
            if (!fader.isNull())
×
692
                fader->setFadeOut(true, fadeout);
×
693
        }
694
    }
695

696
    m_fadersMap.clear();
×
697

698
    {
699
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
700
        if (m_algorithm != NULL)
×
701
            m_algorithm->postRun();
×
702
    }
703

704
    Function::postRun(timer, universes);
×
705
}
×
706

707
void RGBMatrix::roundCheck()
×
708
{
709
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
710
    if (m_algorithm == NULL)
×
711
        return;
712

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

716
    m_roundTime->restart();
×
717

718
    if (tempoType() == Beats)
×
719
        roundElapsed(m_stepBeatDuration);
×
720
    else
721
        roundElapsed(duration());
×
722
}
723

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

738
    return fader->getChannelFader(doc(), universes[universeID], fixtureID, channel);
×
739
}
740

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

754
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
755
{
756
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
757

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

769
        QLCFixtureHead head = fxi->head(grpHead.head);
×
770

771
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
772
            continue;
×
773

774
        uint col = map[pt.y()][pt.x()];
×
775

776
        if (m_controlMode == ControlModeRgb)
×
777
        {
778
            QVector <quint32> rgb = head.rgbChannels();
×
779
            QVector <quint32> cmy = head.cmyChannels();
×
780

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

787
                fc = getFader(universes, fxi->universe(), grpHead.fxi, rgb.at(1));
×
788
                updateFaderValues(fc, qGreen(col), fadeTime);
×
789

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

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

801
                fc = getFader(universes, fxi->universe(), grpHead.fxi, cmy.at(1));
×
802
                updateFaderValues(fc, cmyCol.magenta(), fadeTime);
×
803

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

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

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

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

842
            if (shutters.size())
×
843
            {
844
                FadeChannel *fc = getFader(universes, fxi->universe(), grpHead.fxi, shutters.first());
×
845
                updateFaderValues(fc, rgbToGrey(col), fadeTime);
×
846
            }
847
        }
848

849
        if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
850
        {
851
            quint32 masterDim = fxi->masterIntensityChannel();
×
852
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
853
            QVector <quint32> dimmers;
×
854

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

872
            if (masterDim != QLCChannel::invalid())
×
873
                dimmers << masterDim;
874

875
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
876
                dimmers << headDim;
877

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

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

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

903
/*********************************************************************
904
 * Attributes
905
 *********************************************************************/
906

907
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
908
{
909
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
910

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

920
    return attrIndex;
×
921
}
922

923
/*************************************************************************
924
 * Blend mode
925
 *************************************************************************/
926

927
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
928
{
929
    if (mode == blendMode())
×
930
        return;
931

932
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
933
    {
934
        if (!fader.isNull())
×
935
            fader->setBlendMode(mode);
×
936
    }
937

938
    Function::setBlendMode(mode);
×
939
    emit changed(id());
×
940
}
941

942
/*************************************************************************
943
 * Control Mode
944
 *************************************************************************/
945

946
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
947
{
948
    return m_controlMode;
2✔
949
}
950

951
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
952
{
953
    m_controlMode = mode;
3✔
954
    emit changed(id());
3✔
955
}
3✔
956

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

972
    return ControlModeRgb;
973
}
974

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

1001

1002
/*************************************************************************
1003
 *************************************************************************
1004
 *                          RGBMatrixStep class
1005
 *************************************************************************
1006
 *************************************************************************/
1007

1008
RGBMatrixStep::RGBMatrixStep()
10✔
1009
    : m_direction(Function::Forward)
1010
    , m_currentStepIndex(0)
1011
    , m_stepColor(QColor())
1012
    , m_crDelta(0)
1013
    , m_cgDelta(0)
1014
    , m_cbDelta(0)
10✔
1015
{
1016

1017
}
10✔
1018

1019
void RGBMatrixStep::setCurrentStepIndex(int index)
×
1020
{
1021
    m_currentStepIndex = index;
×
1022
}
×
1023

1024
int RGBMatrixStep::currentStepIndex() const
1✔
1025
{
1026
    return m_currentStepIndex;
1✔
1027
}
1028

1029
void RGBMatrixStep::calculateColorDelta(QColor startColor, QColor endColor, RGBAlgorithm *algorithm)
21✔
1030
{
1031
    m_crDelta = 0;
21✔
1032
    m_cgDelta = 0;
21✔
1033
    m_cbDelta = 0;
21✔
1034

1035
    if (endColor.isValid() && algorithm != NULL && algorithm->acceptColors() > 1)
21✔
1036
    {
1037
        m_crDelta = endColor.red() - startColor.red();
14✔
1038
        m_cgDelta = endColor.green() - startColor.green();
14✔
1039
        m_cbDelta = endColor.blue() - startColor.blue();
14✔
1040

1041
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
1042
    }
1043
}
21✔
1044

1045
void RGBMatrixStep::setStepColor(QColor color)
×
1046
{
1047
    m_stepColor = color;
×
1048
}
×
1049

1050
QColor RGBMatrixStep::stepColor()
6✔
1051
{
1052
    return m_stepColor;
6✔
1053
}
1054

1055
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1056
{
1057
    if (stepsCount <= 0)
×
1058
        return;
1059

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

1071
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1072
}
1073

NEW
1074
void RGBMatrixStep::initializeDirection(Function::Direction direction, QColor startColor, QColor endColor, int stepsCount, RGBAlgorithm *algorithm)
×
1075
{
1076
    m_direction = direction;
×
1077

1078
    if (m_direction == Function::Forward)
×
1079
    {
1080
        setCurrentStepIndex(0);
×
1081
        setStepColor(startColor);
×
1082
    }
1083
    else
1084
    {
1085
        setCurrentStepIndex(stepsCount - 1);
×
1086

1087
        if (endColor.isValid())
×
1088
            setStepColor(endColor);
×
1089
        else
1090
            setStepColor(startColor);
×
1091
    }
1092

NEW
1093
    calculateColorDelta(startColor, endColor, algorithm);
×
1094
}
×
1095

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

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

1180
    return true;
1181
}
1182

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