1 | /* |
2 | * Copyright 2020 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "modules/skottie/src/Transform.h" |
9 | |
10 | #include "modules/skottie/src/SkottieJson.h" |
11 | #include "modules/skottie/src/SkottiePriv.h" |
12 | #include "modules/sksg/include/SkSGTransform.h" |
13 | |
14 | namespace skottie { |
15 | namespace internal { |
16 | |
17 | TransformAdapter2D::TransformAdapter2D(const AnimationBuilder& abuilder, |
18 | const skjson::ObjectValue* janchor_point, |
19 | const skjson::ObjectValue* jposition, |
20 | const skjson::ObjectValue* jscale, |
21 | const skjson::ObjectValue* jrotation, |
22 | const skjson::ObjectValue* jskew, |
23 | const skjson::ObjectValue* jskew_axis) |
24 | : INHERITED(sksg::Matrix<SkMatrix>::Make(SkMatrix::I())) { |
25 | |
26 | this->bind(abuilder, janchor_point, fAnchorPoint); |
27 | this->bind(abuilder, jposition , fPosition); |
28 | this->bind(abuilder, jscale , fScale); |
29 | this->bind(abuilder, jrotation , fRotation); |
30 | this->bind(abuilder, jskew , fSkew); |
31 | this->bind(abuilder, jskew_axis , fSkewAxis); |
32 | } |
33 | |
34 | TransformAdapter2D::~TransformAdapter2D() {} |
35 | |
36 | void TransformAdapter2D::onSync() { |
37 | this->node()->setMatrix(this->totalMatrix()); |
38 | } |
39 | |
40 | SkMatrix TransformAdapter2D::totalMatrix() const { |
41 | SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x, -fAnchorPoint.y); |
42 | |
43 | t.postScale(fScale.x / 100, fScale.y / 100); // 100% based |
44 | t.postRotate(fRotation); |
45 | t.postTranslate(fPosition.x, fPosition.y); |
46 | // TODO: skew |
47 | |
48 | return t; |
49 | } |
50 | |
51 | SkPoint TransformAdapter2D::getAnchorPoint() const { |
52 | return { fAnchorPoint.x, fAnchorPoint.y }; |
53 | } |
54 | |
55 | void TransformAdapter2D::setAnchorPoint(const SkPoint& ap) { |
56 | fAnchorPoint = { ap.x(), ap.y() }; |
57 | this->onSync(); |
58 | } |
59 | |
60 | SkPoint TransformAdapter2D::getPosition() const { |
61 | return { fPosition.x, fPosition.y }; |
62 | } |
63 | |
64 | void TransformAdapter2D::setPosition(const SkPoint& p) { |
65 | fPosition = { p.x(), p.y() }; |
66 | this->onSync(); |
67 | } |
68 | |
69 | SkVector TransformAdapter2D::getScale() const { |
70 | return { fScale.x, fScale.y }; |
71 | } |
72 | |
73 | void TransformAdapter2D::setScale(const SkVector& s) { |
74 | fScale = { s.x(), s.y() }; |
75 | this->onSync(); |
76 | } |
77 | |
78 | void TransformAdapter2D::setRotation(float r) { |
79 | fRotation = r; |
80 | this->onSync(); |
81 | } |
82 | |
83 | void TransformAdapter2D::setSkew(float sk) { |
84 | fSkew = sk; |
85 | this->onSync(); |
86 | } |
87 | |
88 | void TransformAdapter2D::setSkewAxis(float sa) { |
89 | fSkewAxis = sa; |
90 | this->onSync(); |
91 | } |
92 | |
93 | sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& jtransform, |
94 | sk_sp<sksg::Transform> parent) const { |
95 | const auto* jrotation = &jtransform["r" ]; |
96 | if (jrotation->is<skjson::NullValue>()) { |
97 | // Some 2D rotations are disguised as 3D... |
98 | jrotation = &jtransform["rz" ]; |
99 | } |
100 | |
101 | auto adapter = TransformAdapter2D::Make(*this, |
102 | jtransform["a" ], |
103 | jtransform["p" ], |
104 | jtransform["s" ], |
105 | *jrotation, |
106 | jtransform["sk" ], |
107 | jtransform["sa" ]); |
108 | SkASSERT(adapter); |
109 | |
110 | const auto dispatched = this->dispatchTransformProperty(adapter); |
111 | |
112 | if (adapter->isStatic()) { |
113 | if (!dispatched && adapter->totalMatrix().isIdentity()) { |
114 | // The transform has no observable effects - we can discard. |
115 | return parent; |
116 | } |
117 | adapter->seek(0); |
118 | } else { |
119 | fCurrentAnimatorScope->push_back(adapter); |
120 | } |
121 | |
122 | return sksg::Transform::MakeConcat(std::move(parent), adapter->node()); |
123 | } |
124 | |
125 | TransformAdapter3D::TransformAdapter3D(const skjson::ObjectValue& jtransform, |
126 | const AnimationBuilder& abuilder) |
127 | : INHERITED(sksg::Matrix<SkM44>::Make(SkM44())) { |
128 | |
129 | this->bind(abuilder, jtransform["a" ], fAnchorPoint); |
130 | this->bind(abuilder, jtransform["p" ], fPosition); |
131 | this->bind(abuilder, jtransform["s" ], fScale); |
132 | |
133 | // Axis-wise rotation and orientation are mapped to the same rotation property (3D rotation). |
134 | // The difference is in how they get interpolated (scalar/decomposed vs. vector). |
135 | this->bind(abuilder, jtransform["rx" ], fRx); |
136 | this->bind(abuilder, jtransform["ry" ], fRy); |
137 | this->bind(abuilder, jtransform["rz" ], fRz); |
138 | this->bind(abuilder, jtransform["or" ], fOrientation); |
139 | } |
140 | |
141 | TransformAdapter3D::~TransformAdapter3D() = default; |
142 | |
143 | void TransformAdapter3D::onSync() { |
144 | this->node()->setMatrix(this->totalMatrix()); |
145 | } |
146 | |
147 | SkV3 TransformAdapter3D::anchor_point() const { |
148 | return fAnchorPoint; |
149 | } |
150 | |
151 | SkV3 TransformAdapter3D::position() const { |
152 | return fPosition; |
153 | } |
154 | |
155 | SkV3 TransformAdapter3D::rotation() const { |
156 | // orientation and axis-wise rotation map onto the same property. |
157 | return static_cast<SkV3>(fOrientation) + SkV3{ fRx, fRy, fRz }; |
158 | } |
159 | |
160 | SkM44 TransformAdapter3D::totalMatrix() const { |
161 | const auto anchor_point = this->anchor_point(), |
162 | position = this->position(), |
163 | scale = static_cast<SkV3>(fScale), |
164 | rotation = this->rotation(); |
165 | |
166 | return SkM44::Translate(position.x, position.y, position.z) |
167 | * SkM44::Rotate({ 1, 0, 0 }, SkDegreesToRadians(rotation.x)) |
168 | * SkM44::Rotate({ 0, 1, 0 }, SkDegreesToRadians(rotation.y)) |
169 | * SkM44::Rotate({ 0, 0, 1 }, SkDegreesToRadians(rotation.z)) |
170 | * SkM44::Scale(scale.x / 100, scale.y / 100, scale.z / 100) |
171 | * SkM44::Translate(-anchor_point.x, -anchor_point.y, -anchor_point.z); |
172 | } |
173 | |
174 | sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& jtransform, |
175 | sk_sp<sksg::Transform> parent) const { |
176 | auto adapter = TransformAdapter3D::Make(jtransform, *this); |
177 | SkASSERT(adapter); |
178 | |
179 | if (adapter->isStatic()) { |
180 | // TODO: SkM44::isIdentity? |
181 | if (adapter->totalMatrix() == SkM44()) { |
182 | // The transform has no observable effects - we can discard. |
183 | return parent; |
184 | } |
185 | adapter->seek(0); |
186 | } else { |
187 | fCurrentAnimatorScope->push_back(adapter); |
188 | } |
189 | |
190 | return sksg::Transform::MakeConcat(std::move(parent), adapter->node()); |
191 | } |
192 | |
193 | } // namespace internal |
194 | } // namespace skottie |
195 | |