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

mcallegari / qlcplus / 6683238402

29 Oct 2023 12:10PM UTC coverage: 28.07%. Remained the same
6683238402

push

github

mcallegari
engine: fix build

15385 of 54809 relevant lines covered (28.07%)

20267.63 hits per line

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

95.33
/ui/src/virtualconsole/vcxypadfixture.cpp
1
/*
2
  Q Light Controller Plus
3
  vcxypadfixture.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 <QStringList>
24
#include <QVariant>
25
#include <QString>
26
#include <QDebug>
27
#include <math.h>
28

29
#include "qlcchannel.h"
30
#include "qlcmacros.h"
31
#include "qlcfile.h"
32

33
#include "vcxypadfixture.h"
34
#include "genericfader.h"
35
#include "fadechannel.h"
36
#include "universe.h"
37
#include "fixture.h"
38
#include "doc.h"
39

40
/*****************************************************************************
41
 * Initialization
42
 *****************************************************************************/
43

44
VCXYPadFixture::VCXYPadFixture(Doc *doc)
59✔
45
    : m_doc(doc)
46
    , m_head()
47
    , m_xMin(0)
48
    , m_xMax(1)
49
    , m_xReverse(false)
50
    , m_xLSB(QLCChannel::invalid())
118✔
51
    , m_xMSB(QLCChannel::invalid())
118✔
52
    , m_yMin(0)
53
    , m_yMax(1)
54
    , m_yReverse(false)
55
    , m_yLSB(QLCChannel::invalid())
118✔
56
    , m_yMSB(QLCChannel::invalid())
118✔
57
    , m_displayMode(Degrees)
58
    , m_enabled(true)
59
    , m_universe(Universe::invalid())
59✔
60
    , m_fixtureAddress(QLCChannel::invalid())
59✔
61
{
62
    Q_ASSERT(m_doc != NULL);
59✔
63

64
    precompute();
59✔
65
}
59✔
66

67
VCXYPadFixture::VCXYPadFixture(Doc* doc, const QVariant& variant)
5✔
68
    : m_doc(doc)
5✔
69
{
70
    Q_ASSERT(m_doc != NULL);
5✔
71

72
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
73
    if (variant.canConvert(QVariant::StringList) == true)
5✔
74
#else
75
    if (variant.canConvert<QStringList>() == true)
76
#endif
77
    {
78
        QStringList list(variant.toStringList());
8✔
79
        if (list.size() == 10)
4✔
80
        {
81
            m_head.fxi = list.takeFirst().toUInt();
3✔
82
            m_head.head = list.takeFirst().toInt();
3✔
83

84
            m_xMin = qreal(list.takeFirst().toDouble());
3✔
85
            m_xMin = CLAMP(m_xMin, qreal(0), qreal(1));
3✔
86
            m_xMax = qreal(list.takeFirst().toDouble());
3✔
87
            m_xMax = CLAMP(m_xMax, qreal(0), qreal(1));
3✔
88
            m_xReverse = bool(list.takeFirst().toInt());
3✔
89

90
            m_yMin = qreal(list.takeFirst().toDouble());
3✔
91
            m_yMin = CLAMP(m_yMin, qreal(0), qreal(1));
3✔
92
            m_yMax = qreal(list.takeFirst().toDouble());
3✔
93
            m_yMax = CLAMP(m_yMax, qreal(0), qreal(1));
3✔
94
            m_yReverse = bool(list.takeFirst().toInt());
3✔
95

96
            m_xMSB = QLCChannel::invalid();
3✔
97
            m_xLSB = QLCChannel::invalid();
3✔
98
            m_yMSB = QLCChannel::invalid();
3✔
99
            m_yLSB = QLCChannel::invalid();
3✔
100

101
            precompute();
3✔
102

103
            m_enabled = bool(list.takeFirst().toInt());
3✔
104
            m_displayMode = DisplayMode(list.takeFirst().toInt());
3✔
105
            Q_ASSERT(list.isEmpty() == true);
3✔
106
        }
107
        else
108
        {
109
            /* Construct an empty fixture */
110
            *this = VCXYPadFixture(doc);
1✔
111
        }
112
    }
113
    else
114
    {
115
        /* Construct an empty fixture */
116
        *this = VCXYPadFixture(doc);
1✔
117
    }
118
}
5✔
119

120
VCXYPadFixture::VCXYPadFixture(const VCXYPadFixture &other)
62✔
121
{
122
    *this = other;
62✔
123
}
62✔
124

125
VCXYPadFixture::~VCXYPadFixture()
126✔
126
{
127
}
126✔
128

129
VCXYPadFixture& VCXYPadFixture::operator=(const VCXYPadFixture& fxi)
68✔
130
{
131
    m_doc = fxi.m_doc;
68✔
132
    Q_ASSERT(m_doc != NULL);
68✔
133

134
    m_head = fxi.m_head;
68✔
135
    m_universe = fxi.m_universe;
68✔
136
    m_fixtureAddress = fxi.m_fixtureAddress;
68✔
137

138
    m_xMin = fxi.m_xMin;
68✔
139
    m_xMax = fxi.m_xMax;
68✔
140
    m_xReverse = fxi.m_xReverse;
68✔
141

142
    m_yMin = fxi.m_yMin;
68✔
143
    m_yMax = fxi.m_yMax;
68✔
144
    m_yReverse = fxi.m_yReverse;
68✔
145

146
    m_xMSB = fxi.m_xMSB;
68✔
147
    m_xLSB = fxi.m_xLSB;
68✔
148

149
    m_yMSB = fxi.m_yMSB;
68✔
150
    m_yLSB = fxi.m_yLSB;
68✔
151

152
    precompute();
68✔
153

154
    m_enabled = fxi.m_enabled;
68✔
155
    m_displayMode = fxi.m_displayMode;
68✔
156

157
    return *this;
68✔
158
}
159

160
bool VCXYPadFixture::operator==(const VCXYPadFixture& fxi) const
26✔
161
{
162
    if (m_head == fxi.m_head)
26✔
163
        return true;
12✔
164
    else
165
        return false;
14✔
166
}
167

168
VCXYPadFixture::operator QVariant() const
1✔
169
{
170
    QStringList list;
2✔
171

172
    list << QString("%1").arg(m_head.fxi);
1✔
173
    list << QString("%1").arg(m_head.head);
1✔
174

175
    list << QString("%1").arg(m_xMin);
1✔
176
    list << QString("%1").arg(m_xMax);
1✔
177
    list << QString("%1").arg(m_xReverse);
1✔
178

179
    list << QString("%1").arg(m_yMin);
1✔
180
    list << QString("%1").arg(m_yMax);
1✔
181
    list << QString("%1").arg(m_yReverse);
1✔
182

183
    list << QString("%1").arg(m_enabled);
1✔
184
    list << QString("%1").arg(m_displayMode);
1✔
185

186
    return QVariant(list);
2✔
187
}
188

189
/****************************************************************************
190
 * Fixture
191
 ****************************************************************************/
192

193
void VCXYPadFixture::setHead(GroupHead const & head)
55✔
194
{
195
    m_head = head;
55✔
196
}
55✔
197

198
GroupHead const & VCXYPadFixture::head() const
29✔
199
{
200
    return m_head;
29✔
201
}
202

203
QRectF VCXYPadFixture::degreesRange() const
22✔
204
{
205
    Fixture* fxi = m_doc->fixture(m_head.fxi);
22✔
206
    if (fxi == NULL)
22✔
207
    {
208
        return QRectF();
17✔
209
    }
210
    else
211
    {
212
       return fxi->degreesRange(m_head.head);
5✔
213
    }
214
}
215

216
QString VCXYPadFixture::name() const
3✔
217
{
218
    if (!m_head.isValid())
3✔
219
        return QString();
1✔
220

221
    Fixture* fxi = m_doc->fixture(m_head.fxi);
2✔
222
    if (fxi == NULL)
2✔
223
        return QString();
1✔
224

225
    if (m_head.head >= fxi->heads())
1✔
226
        return QString();
×
227

228
    if (fxi->heads() == 1)
1✔
229
        return fxi->name();
1✔
230

231
    return QString("%1 [%2]").arg(fxi->name()).arg(m_head.head);
×
232
}
233

234
/****************************************************************************
235
 * X-Axis
236
 ****************************************************************************/
237

238
void VCXYPadFixture::setX(qreal min, qreal max, bool reverse)
40✔
239
{
240
    m_xMin = CLAMP(min, 0.0, 1.0);
40✔
241
    m_xMax = CLAMP(max, 0.0, 1.0);
40✔
242
    m_xReverse = reverse;
40✔
243
    precompute();
40✔
244
}
40✔
245

246
qreal VCXYPadFixture::xMin() const
132,391✔
247
{
248
    return m_xMin;
132,391✔
249
}
250

251
qreal VCXYPadFixture::xMax() const
66,200✔
252
{
253
    return m_xMax;
66,200✔
254
}
255

256
bool VCXYPadFixture::xReverse() const
8✔
257
{
258
    return m_xReverse;
8✔
259
}
260

261
QString VCXYPadFixture::xBrief() const
6✔
262
{
263
    qreal scale = 100.0;
6✔
264
    QString units = "%";
12✔
265

266
    if (m_displayMode == DMX)
6✔
267
    {
268
        scale = 255.0;
2✔
269
        units = "";
2✔
270
    }
271
    else if (m_displayMode == Degrees)
4✔
272
    {
273
        scale = degreesRange().width();
2✔
274
        units = "°";
2✔
275
    }
276

277
    if (m_xReverse == false)
6✔
278
        return QString("%1%3 - %2%3").arg(qRound(m_xMin * scale)).arg(qRound(m_xMax * scale)).arg(units);
4✔
279
    else
280
        return QString("%1: %2%4 - %3%4").arg(QObject::tr("Reversed"))
4✔
281
                                      .arg(qRound(m_xMax * scale)).arg(qRound(m_xMin * scale)).arg(units);
2✔
282
}
283

284
void VCXYPadFixture::precompute()
210✔
285
{
286
    if (m_xReverse)
210✔
287
    {
288
        m_xOffset = m_xMax * qreal(USHRT_MAX);
54✔
289
        m_xRange = (m_xMin - m_xMax) * qreal(USHRT_MAX);
54✔
290
    }
291
    else
292
    {
293
        m_xOffset = m_xMin * qreal(USHRT_MAX);
156✔
294
        m_xRange = (m_xMax - m_xMin) * qreal(USHRT_MAX);
156✔
295
    }
296

297
    if (m_yReverse)
210✔
298
    {
299
        m_yOffset = m_yMax * qreal(USHRT_MAX);
26✔
300
        m_yRange = (m_yMin - m_yMax) * qreal(USHRT_MAX);
26✔
301
    }
302
    else
303
    {
304
        m_yOffset = m_yMin * qreal(USHRT_MAX);
184✔
305
        m_yRange = (m_yMax - m_yMin) * qreal(USHRT_MAX);
184✔
306
    }
307
}
210✔
308

309
/****************************************************************************
310
 * Y-Axis
311
 ****************************************************************************/
312

313
void VCXYPadFixture::setY(qreal min, qreal max, bool reverse)
40✔
314
{
315
    m_yMin = CLAMP(min, 0.0, 1.0);
40✔
316
    m_yMax = CLAMP(max, 0.0, 1.0);
40✔
317
    m_yReverse = reverse;
40✔
318
    precompute();
40✔
319
}
40✔
320

321
qreal VCXYPadFixture::yMin() const
132,391✔
322
{
323
    return m_yMin;
132,391✔
324
}
325

326
qreal VCXYPadFixture::yMax() const
66,200✔
327
{
328
    return m_yMax;
66,200✔
329
}
330

331
bool VCXYPadFixture::yReverse() const
8✔
332
{
333
    return m_yReverse;
8✔
334
}
335

336
QString VCXYPadFixture::yBrief() const
6✔
337
{
338
    qreal scale = 100.0;
6✔
339
    QString units = "%";
12✔
340

341
    if (m_displayMode == DMX)
6✔
342
    {
343
        scale = 255.0;
2✔
344
        units = "";
2✔
345
    }
346
    else if (m_displayMode == Degrees)
4✔
347
    {
348
        scale = degreesRange().height();
2✔
349
        units = "°";
2✔
350
    }
351

352
    if (m_yReverse == false)
6✔
353
        return QString("%1%3 - %2%3").arg(qRound(m_yMin * scale)).arg(qRound(m_yMax * scale)).arg(units);
4✔
354
    else
355
        return QString("%1: %2%4 - %3%4").arg(QObject::tr("Reversed"))
4✔
356
                .arg(qRound(m_yMax * scale)).arg(qRound(m_yMin * scale)).arg(units);
2✔
357
}
358

359
/********************************************************************
360
 * Display mode
361
 ********************************************************************/
362

363
void VCXYPadFixture::setDisplayMode(VCXYPadFixture::DisplayMode mode)
10✔
364
{
365
    m_displayMode = mode;
10✔
366
}
10✔
367

368
VCXYPadFixture::DisplayMode VCXYPadFixture::displayMode() const
4✔
369
{
370
    return m_displayMode;
4✔
371
}
372

373
/****************************************************************************
374
 * Load & Save
375
 ****************************************************************************/
376

377
bool VCXYPadFixture::loadXML(QXmlStreamReader &root)
7✔
378
{
379
    if (root.name() != KXMLQLCVCXYPadFixture)
7✔
380
    {
381
        qWarning() << Q_FUNC_INFO << "XYPad Fixture node not found";
1✔
382
        return false;
1✔
383
    }
384

385
    /* Fixture ID */
386
    GroupHead head;
6✔
387
    head.fxi = root.attributes().value(KXMLQLCVCXYPadFixtureID).toString().toInt();
6✔
388
    head.head = root.attributes().value(KXMLQLCVCXYPadFixtureHead).toString().toInt();
6✔
389
    setHead(head);
6✔
390

391
    /* Children */
392
    while (root.readNextStartElement())
22✔
393
    {
394
        if (root.name() == KXMLQLCVCXYPadFixtureAxis)
16✔
395
        {
396
            QXmlStreamAttributes attrs = root.attributes();
28✔
397
            QString axis = attrs.value(KXMLQLCVCXYPadFixtureAxisID).toString();
42✔
398
            QString min = attrs.value(KXMLQLCVCXYPadFixtureAxisLowLimit).toString();
42✔
399
            QString max = attrs.value(KXMLQLCVCXYPadFixtureAxisHighLimit).toString();
42✔
400
            QString rev = attrs.value(KXMLQLCVCXYPadFixtureAxisReverse).toString();
42✔
401

402
            if (axis == KXMLQLCVCXYPadFixtureAxisX)
14✔
403
            {
404
                if (rev == KXMLQLCTrue)
6✔
405
                    setX(min.toDouble(), max.toDouble(), true);
3✔
406
                else
407
                    setX(min.toDouble(), max.toDouble(), false);
3✔
408
            }
409
            else if (axis == KXMLQLCVCXYPadFixtureAxisY)
8✔
410
            {
411
                if (rev == KXMLQLCTrue)
6✔
412
                    setY(min.toDouble(), max.toDouble(), true);
3✔
413
                else
414
                    setY(min.toDouble(), max.toDouble(), false);
3✔
415
            }
416
            else
417
            {
418
                qWarning() << Q_FUNC_INFO << "Unknown XYPad axis" << axis;
2✔
419
            }
420
            root.skipCurrentElement();
14✔
421
        }
422
        else
423
        {
424
            qWarning() << Q_FUNC_INFO << "Unknown XY Pad tag:" << root.name().toString();
2✔
425
            root.skipCurrentElement();
2✔
426
        }
427
    }
428

429
    return true;
6✔
430
}
431

432
bool VCXYPadFixture::saveXML(QXmlStreamWriter *doc) const
4✔
433
{
434
    Q_ASSERT(doc != NULL);
4✔
435

436
    /* VCXYPad Fixture */
437
    doc->writeStartElement(KXMLQLCVCXYPadFixture);
4✔
438
    doc->writeAttribute(KXMLQLCVCXYPadFixtureID, QString("%1").arg(m_head.fxi));
4✔
439
    doc->writeAttribute(KXMLQLCVCXYPadFixtureHead, QString("%1").arg(m_head.head));
4✔
440

441
    /* X-Axis */
442
    doc->writeStartElement(KXMLQLCVCXYPadFixtureAxis);
4✔
443
    doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisID, KXMLQLCVCXYPadFixtureAxisX);
4✔
444
    doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisLowLimit, QString("%1").arg(m_xMin));
4✔
445
    doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisHighLimit, QString("%1").arg(m_xMax));
4✔
446
    if (m_xReverse == true)
4✔
447
        doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisReverse, KXMLQLCTrue);
1✔
448
    else
449
        doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisReverse, KXMLQLCFalse);
3✔
450
    doc->writeEndElement();
4✔
451

452
    /* Y-Axis */
453
    doc->writeStartElement(KXMLQLCVCXYPadFixtureAxis);
4✔
454
    doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisID, KXMLQLCVCXYPadFixtureAxisY);
4✔
455
    doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisLowLimit, QString("%1").arg(m_yMin));
4✔
456
    doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisHighLimit, QString("%1").arg(m_yMax));
4✔
457
    if (m_yReverse == true)
4✔
458
        doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisReverse, KXMLQLCTrue);
1✔
459
    else
460
        doc->writeAttribute(KXMLQLCVCXYPadFixtureAxisReverse, KXMLQLCFalse);
3✔
461
    doc->writeEndElement();
4✔
462

463
    /* End the <Fixture> tag */
464
    doc->writeEndElement();
4✔
465

466
    return true;
4✔
467
}
468

469
/****************************************************************************
470
 * Running
471
 ****************************************************************************/
472

473
void VCXYPadFixture::arm()
25✔
474
{
475
    Fixture* fxi = m_doc->fixture(m_head.fxi);
25✔
476
    if (fxi == NULL)
25✔
477
    {
478
        m_xMSB = QLCChannel::invalid();
1✔
479
        m_xLSB = QLCChannel::invalid();
1✔
480
        m_yMSB = QLCChannel::invalid();
1✔
481
        m_yLSB = QLCChannel::invalid();
1✔
482
        m_universe = Universe::invalid();
1✔
483
        m_fixtureAddress = QLCChannel::invalid();
1✔
484
    }
485
    else
486
    {
487
        m_universe = fxi->universe();
24✔
488
        m_fixtureAddress = fxi->address();
24✔
489
        m_xMSB = fxi->channelNumber(QLCChannel::Pan, QLCChannel::MSB, m_head.head);
24✔
490
        m_xLSB = fxi->channelNumber(QLCChannel::Pan, QLCChannel::LSB, m_head.head);
24✔
491
        m_yMSB = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::MSB, m_head.head);
24✔
492
        m_yLSB = fxi->channelNumber(QLCChannel::Tilt, QLCChannel::LSB, m_head.head);
24✔
493
    }
494
}
25✔
495

496
void VCXYPadFixture::disarm()
2✔
497
{
498
    m_xLSB = QLCChannel::invalid();
2✔
499
    m_xMSB = QLCChannel::invalid();
2✔
500
    m_yLSB = QLCChannel::invalid();
2✔
501
    m_yMSB = QLCChannel::invalid();
2✔
502
    m_universe = Universe::invalid();
2✔
503
    m_fixtureAddress = QLCChannel::invalid();
2✔
504
}
2✔
505

506
void VCXYPadFixture::setEnabled(bool enable)
×
507
{
508
    m_enabled = enable;
×
509
}
×
510

511
bool VCXYPadFixture::isEnabled() const
×
512
{
513
    return m_enabled;
×
514
}
515

516
quint32 VCXYPadFixture::universe() const
×
517
{
518
    return m_universe;
×
519
}
520

521
void VCXYPadFixture::updateChannel(FadeChannel *fc, uchar value)
794,356✔
522
{
523
    fc->setStart(value);
794,356✔
524
    fc->setCurrent(value);
794,356✔
525
    fc->setTarget(value);
794,356✔
526
    fc->setElapsed(0);
794,356✔
527
    fc->setReady(false);
794,356✔
528
}
794,356✔
529

530
void VCXYPadFixture::writeDMX(qreal xmul, qreal ymul, QSharedPointer<GenericFader> fader, Universe *universe)
264,783✔
531
{
532
    if (m_xMSB == QLCChannel::invalid() || m_yMSB == QLCChannel::invalid())
264,783✔
533
        return;
3✔
534

535
    if (fader.isNull())
264,780✔
536
        return;
×
537

538
    ushort x = floor(m_xRange * xmul + m_xOffset + 0.5);
264,780✔
539
    ushort y = floor(m_yRange * ymul + m_yOffset + 0.5);
264,780✔
540

541
    FadeChannel *fc = fader->getChannelFader(m_doc, universe, m_head.fxi, m_xMSB);
264,780✔
542
    updateChannel(fc, uchar(x >> 8));
264,780✔
543

544
    fc = fader->getChannelFader(m_doc, universe, m_head.fxi, m_yMSB);
264,780✔
545
    updateChannel(fc, uchar(y >> 8));
264,780✔
546

547
    if (m_xLSB != QLCChannel::invalid() && m_yLSB != QLCChannel::invalid())
264,780✔
548
    {
549
        fc = fader->getChannelFader(m_doc, universe, m_head.fxi, m_xLSB);
132,398✔
550
        updateChannel(fc, uchar(x & 0xFF));
132,398✔
551

552
        fc = fader->getChannelFader(m_doc, universe, m_head.fxi, m_yLSB);
132,398✔
553
        updateChannel(fc, uchar(y & 0xFF));
132,398✔
554
    }
555
}
556

557
void VCXYPadFixture::readDMX(const QByteArray& universeData, qreal & xmul, qreal & ymul)
16✔
558
{
559
    xmul = -1;
16✔
560
    ymul = -1;
16✔
561

562
    if (m_xMSB == QLCChannel::invalid() || m_yMSB == QLCChannel::invalid())
16✔
563
        return;
×
564

565
    qreal x = 0;
16✔
566
    qreal y = 0;
16✔
567

568
    if (m_xMSB + m_fixtureAddress < (quint32)universeData.size())
16✔
569
        x = (uchar)universeData.at(m_xMSB + m_fixtureAddress) * 256;
16✔
570
    if (m_yMSB + m_fixtureAddress < (quint32)universeData.size())
16✔
571
        y = (uchar)universeData.at(m_yMSB + m_fixtureAddress) * 256;
16✔
572

573
    if (m_xLSB != QLCChannel::invalid() && m_yLSB != QLCChannel::invalid())
16✔
574
    {
575
        if (m_xLSB + m_fixtureAddress < (quint32)universeData.size())
16✔
576
            x += (uchar)universeData.at(m_xLSB + m_fixtureAddress);
16✔
577
        if (m_yLSB + m_fixtureAddress < (quint32)universeData.size())
16✔
578
            y += (uchar)universeData.at(m_yLSB + m_fixtureAddress);
16✔
579
    }
580

581
    if (m_xRange == 0 || m_yRange == 0)
16✔
582
    {
583
        Q_ASSERT(m_xRange != 0);
×
584
        Q_ASSERT(m_yRange != 0);
×
585
        return; // potential divide by zero!
×
586
    }
587

588
    x = (x - m_xOffset) / m_xRange;
16✔
589
    y = (y - m_yOffset) / m_yRange;
16✔
590

591
    x = CLAMP(x, qreal(0), qreal(1));
16✔
592
    y = CLAMP(y, qreal(0), qreal(1));
16✔
593

594
    xmul = x;
16✔
595
    ymul = y;
16✔
596
}
597

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc