1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef QLOTTIESPATIALPROPERTY_P_H
5#define QLOTTIESPATIALPROPERTY_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QPointF>
19#include <QPainterPath>
20
21#include <QtLottie/private/qlottieproperty_p.h>
22
23QT_BEGIN_NAMESPACE
24
25class QLottieSpatialProperty : public QLottieProperty2D<QPointF>
26{
27public:
28 virtual void construct(const QJsonObject &definition) override
29 {
30 qCDebug(lcLottieQtLottieParser) << "QLottieSpatialProperty::construct()";
31 m_bezierPath.setCachingEnabled(true);
32 QLottieProperty2D<QPointF>::construct(definition);
33 }
34
35 virtual EasingSegment<QPointF> parseKeyframe(const QJsonObject keyframe,
36 bool fromExpression) override
37 {
38 EasingSegment<QPointF> easing =
39 QLottieProperty2D<QPointF>::parseKeyframe(keyframe, fromExpression);
40
41 // No need to parse further incomplete keyframes (i.e. last keyframes)
42 if (!easing.complete) {
43 return easing;
44 }
45
46 qreal tix = 0, tiy = 0, tox = 0, toy = 0;
47 if (fromExpression) {
48 // If spatial property definition originates from
49 // an expression (specifically Slider), it contains scalar
50 // property. It must be expanded to both x and y coordinates
51 QJsonArray iArr = keyframe.value(key: QLatin1String("i")).toArray();
52 QJsonArray oArr = keyframe.value(key: QLatin1String("o")).toArray();
53
54 if (iArr.count() && oArr.count()) {
55 tix = iArr.at(i: 0).toDouble();
56 tiy = tix;
57 tox = oArr.at(i: 0).toDouble();
58 toy = tox;
59 }
60 } else {
61 QJsonArray tiArr = keyframe.value(key: QLatin1String("ti")).toArray();
62 QJsonArray toArr = keyframe.value(key: QLatin1String("to")).toArray();
63
64 if (tiArr.count() && toArr.count()) {
65 tix = tiArr.at(i: 0).toDouble();
66 tiy = tiArr.at(i: 1).toDouble();
67 tox = toArr.at(i: 0).toDouble();
68 toy = toArr.at(i: 1).toDouble();
69 }
70 }
71 QPointF s(easing.startValue);
72 QPointF e(easing.endValue);
73 QPointF c1(tox, toy);
74 QPointF c2(tix, tiy);
75
76 m_bezierPath.moveTo(p: s);
77 if (c1.isNull() && c2.isNull()) {
78 m_bezierPath.lineTo(p: e);
79 } else {
80 c1 += s;
81 c2 += e;
82 m_bezierPath.cubicTo(ctrlPt1: c1, ctrlPt2: c2, endPt: e);
83 }
84
85 return easing;
86 }
87
88 virtual EasingSegment<QPointF> parseKeyframe(const QJsonObject keyframe,
89 const QJsonObject nextKeyframe,
90 bool fromExpression) override
91 {
92 EasingSegment<QPointF> easing =
93 QLottieProperty2D<QPointF>::parseKeyframe(keyframe, nextKeyframe, fromExpression);
94
95 // No need to parse further incomplete keyframes (i.e. last keyframes)
96 if (!easing.complete) {
97 return easing;
98 }
99
100 qreal tix = 0, tiy = 0, tox = 0, toy = 0;
101 if (fromExpression) {
102 // If spatial property definition originates from
103 // an expression (specifically Slider), it contains scalar
104 // property. It must be expanded to both x and y coordinates
105 QJsonArray iArr = keyframe.value(key: QLatin1String("i")).toArray();
106 QJsonArray oArr = keyframe.value(key: QLatin1String("o")).toArray();
107
108 if (iArr.count() && oArr.count()) {
109 tix = iArr.at(i: 0).toDouble();
110 tiy = tix;
111 tox = oArr.at(i: 0).toDouble();
112 toy = tox;
113 }
114 } else {
115 QJsonArray tiArr = keyframe.value(key: QLatin1String("ti")).toArray();
116 QJsonArray toArr = keyframe.value(key: QLatin1String("to")).toArray();
117
118 if (tiArr.count() && toArr.count()) {
119 tix = tiArr.at(i: 0).toDouble();
120 tiy = tiArr.at(i: 1).toDouble();
121 tox = toArr.at(i: 0).toDouble();
122 toy = toArr.at(i: 1).toDouble();
123 }
124 }
125 QPointF s(easing.startValue);
126 QPointF e(easing.endValue);
127 QPointF c1(tox, toy);
128 QPointF c2(tix, tiy);
129
130 m_bezierPath.moveTo(p: s);
131 if (c1.isNull() && c2.isNull()) {
132 m_bezierPath.lineTo(p: e);
133 } else {
134 c1 += s;
135 c2 += e;
136 m_bezierPath.cubicTo(ctrlPt1: c1, ctrlPt2: c2, endPt: e);
137 }
138
139 return easing;
140 }
141
142 virtual bool update(int frame) override
143 {
144 if (!m_animated)
145 return false;
146
147 int adjustedFrame = qBound(min: m_startFrame, val: frame, max: m_endFrame);
148 if (const EasingSegment<QPointF> *easing = getEasingSegment(frame: adjustedFrame)) {
149 qreal progress = ((adjustedFrame - m_startFrame) * 1.0) / (m_endFrame - m_startFrame);
150 qreal easedValue = easing->valueForProgress(progress);
151 m_value = m_bezierPath.pointAtPercent(t: easedValue);
152 }
153
154 return true;
155 }
156
157private:
158 QPainterPath m_bezierPath;
159};
160
161QT_END_NAMESPACE
162
163#endif // QLOTTIESPATIALPROPERTY_P_H
164

source code of qtlottie/src/lottie/qlottiespatialproperty_p.h