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 | bool auto_orient) |
25 | : INHERITED(sksg::Matrix<SkMatrix>::Make(SkMatrix::I())) { |
26 | |
27 | this->bind(abuilder, janchor_point, fAnchorPoint); |
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 | this->bindAutoOrientable(abuilder, jposition, &fPosition, auto_orient ? &fOrientation |
34 | : nullptr); |
35 | } |
36 | |
37 | TransformAdapter2D::~TransformAdapter2D() {} |
38 | |
39 | void TransformAdapter2D::onSync() { |
40 | this->node()->setMatrix(this->totalMatrix()); |
41 | } |
42 | |
43 | SkMatrix TransformAdapter2D::totalMatrix() const { |
44 | // TODO: skew |
45 | return SkMatrix::Translate(fPosition.x, fPosition.y) |
46 | * SkMatrix::RotateDeg(fRotation + fOrientation) |
47 | * SkMatrix::Scale (fScale.x / 100, fScale.y / 100) // 100% based |
48 | * SkMatrix::Translate(-fAnchorPoint.x, -fAnchorPoint.y); |
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, |
95 | bool auto_orient) const { |
96 | const auto* jrotation = &jtransform["r" ]; |
97 | if (jrotation->is<skjson::NullValue>()) { |
98 | // Some 2D rotations are disguised as 3D... |
99 | jrotation = &jtransform["rz" ]; |
100 | } |
101 | |
102 | auto adapter = TransformAdapter2D::Make(*this, |
103 | jtransform["a" ], |
104 | jtransform["p" ], |
105 | jtransform["s" ], |
106 | *jrotation, |
107 | jtransform["sk" ], |
108 | jtransform["sa" ], |
109 | auto_orient); |
110 | SkASSERT(adapter); |
111 | |
112 | const auto dispatched = this->dispatchTransformProperty(adapter); |
113 | |
114 | if (adapter->isStatic()) { |
115 | if (!dispatched && adapter->totalMatrix().isIdentity()) { |
116 | // The transform has no observable effects - we can discard. |
117 | return parent; |
118 | } |
119 | adapter->seek(0); |
120 | } else { |
121 | fCurrentAnimatorScope->push_back(adapter); |
122 | } |
123 | |
124 | return sksg::Transform::MakeConcat(std::move(parent), adapter->node()); |
125 | } |
126 | |
127 | TransformAdapter3D::TransformAdapter3D(const skjson::ObjectValue& jtransform, |
128 | const AnimationBuilder& abuilder) |
129 | : INHERITED(sksg::Matrix<SkM44>::Make(SkM44())) { |
130 | |
131 | this->bind(abuilder, jtransform["a" ], fAnchorPoint); |
132 | this->bind(abuilder, jtransform["p" ], fPosition); |
133 | this->bind(abuilder, jtransform["s" ], fScale); |
134 | |
135 | // Axis-wise rotation and orientation are mapped to the same rotation property (3D rotation). |
136 | // The difference is in how they get interpolated (scalar/decomposed vs. vector). |
137 | this->bind(abuilder, jtransform["rx" ], fRx); |
138 | this->bind(abuilder, jtransform["ry" ], fRy); |
139 | this->bind(abuilder, jtransform["rz" ], fRz); |
140 | this->bind(abuilder, jtransform["or" ], fOrientation); |
141 | } |
142 | |
143 | TransformAdapter3D::~TransformAdapter3D() = default; |
144 | |
145 | void TransformAdapter3D::onSync() { |
146 | this->node()->setMatrix(this->totalMatrix()); |
147 | } |
148 | |
149 | SkV3 TransformAdapter3D::anchor_point() const { |
150 | return fAnchorPoint; |
151 | } |
152 | |
153 | SkV3 TransformAdapter3D::position() const { |
154 | return fPosition; |
155 | } |
156 | |
157 | SkV3 TransformAdapter3D::rotation() const { |
158 | // orientation and axis-wise rotation map onto the same property. |
159 | return static_cast<SkV3>(fOrientation) + SkV3{ fRx, fRy, fRz }; |
160 | } |
161 | |
162 | SkM44 TransformAdapter3D::totalMatrix() const { |
163 | const auto anchor_point = this->anchor_point(), |
164 | position = this->position(), |
165 | scale = static_cast<SkV3>(fScale), |
166 | rotation = this->rotation(); |
167 | |
168 | return SkM44::Translate(position.x, position.y, position.z) |
169 | * SkM44::Rotate({ 1, 0, 0 }, SkDegreesToRadians(rotation.x)) |
170 | * SkM44::Rotate({ 0, 1, 0 }, SkDegreesToRadians(rotation.y)) |
171 | * SkM44::Rotate({ 0, 0, 1 }, SkDegreesToRadians(rotation.z)) |
172 | * SkM44::Scale(scale.x / 100, scale.y / 100, scale.z / 100) |
173 | * SkM44::Translate(-anchor_point.x, -anchor_point.y, -anchor_point.z); |
174 | } |
175 | |
176 | sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& jtransform, |
177 | sk_sp<sksg::Transform> parent, |
178 | bool /*TODO: auto_orient*/) const { |
179 | auto adapter = TransformAdapter3D::Make(jtransform, *this); |
180 | SkASSERT(adapter); |
181 | |
182 | if (adapter->isStatic()) { |
183 | // TODO: SkM44::isIdentity? |
184 | if (adapter->totalMatrix() == SkM44()) { |
185 | // The transform has no observable effects - we can discard. |
186 | return parent; |
187 | } |
188 | adapter->seek(0); |
189 | } else { |
190 | fCurrentAnimatorScope->push_back(adapter); |
191 | } |
192 | |
193 | return sksg::Transform::MakeConcat(std::move(parent), adapter->node()); |
194 | } |
195 | |
196 | } // namespace internal |
197 | } // namespace skottie |
198 | |