blob: 88da97f43b5796d5a405c936b562ea0bbc2a0dc2 [file] [log] [blame]
Kaido Kert25902c62024-06-17 17:10:28 -07001// Copyright 2012 The Chromium Authors
Kaido Kert56d7c4e2024-04-13 12:59:27 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/gfx/interpolated_transform.h"
6
7#include <cmath>
8
9#include "base/check.h"
10#include "base/numerics/safe_conversions.h"
11#include "ui/gfx/animation/tween.h"
Kaido Kert25902c62024-06-17 17:10:28 -070012#include "ui/gfx/geometry/transform_util.h"
Kaido Kert56d7c4e2024-04-13 12:59:27 -070013
14namespace {
15
16static const double EPSILON = 1e-6;
17
18bool IsMultipleOfNinetyDegrees(double degrees) {
19 double remainder = fabs(fmod(degrees, 90.0));
20 return remainder < EPSILON || 90.0 - remainder < EPSILON;
21}
22
23// Returns false if |degrees| is not a multiple of ninety degrees or if
24// |rotation| is NULL. It does not affect |rotation| in this case. Otherwise
25// *rotation is set to be the appropriate sanitized rotation matrix. That is,
26// the rotation matrix corresponding to |degrees| which has entries that are all
27// either 0, 1 or -1.
28bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform* rotation,
29 float degrees) {
30 if (!IsMultipleOfNinetyDegrees(degrees) || !rotation)
31 return false;
32
Kaido Kert56d7c4e2024-04-13 12:59:27 -070033 float degrees_by_ninety = degrees / 90.0f;
34
35 int n = base::ClampRound(degrees_by_ninety);
36
37 n %= 4;
38 if (n < 0)
39 n += 4;
40
41 // n should now be in the range [0, 3]
Kaido Kert25902c62024-06-17 17:10:28 -070042 if (n == 0) {
43 rotation->MakeIdentity();
44 } else if (n == 1) {
45 *rotation = gfx::Transform::Make90degRotation();
Kaido Kert56d7c4e2024-04-13 12:59:27 -070046 } else if (n == 2) {
Kaido Kert25902c62024-06-17 17:10:28 -070047 *rotation = gfx::Transform::Make180degRotation();
Kaido Kert56d7c4e2024-04-13 12:59:27 -070048 } else if (n == 3) {
Kaido Kert25902c62024-06-17 17:10:28 -070049 *rotation = gfx::Transform::Make270degRotation();
Kaido Kert56d7c4e2024-04-13 12:59:27 -070050 }
Kaido Kert56d7c4e2024-04-13 12:59:27 -070051 return true;
52}
53
54} // namespace
55
56namespace ui {
57
58///////////////////////////////////////////////////////////////////////////////
59// InterpolatedTransform
60//
61
62InterpolatedTransform::InterpolatedTransform()
63 : start_time_(0.0f),
64 end_time_(1.0f),
65 reversed_(false) {
66}
67
68InterpolatedTransform::InterpolatedTransform(float start_time,
69 float end_time)
70 : start_time_(start_time),
71 end_time_(end_time),
72 reversed_(false) {
73}
74
75InterpolatedTransform::~InterpolatedTransform() {}
76
77gfx::Transform InterpolatedTransform::Interpolate(float t) const {
78 if (reversed_)
79 t = 1.0f - t;
80 gfx::Transform result = InterpolateButDoNotCompose(t);
81 if (child_.get()) {
Kaido Kert25902c62024-06-17 17:10:28 -070082 result.PostConcat(child_->Interpolate(t));
Kaido Kert56d7c4e2024-04-13 12:59:27 -070083 }
84 return result;
85}
86
87void InterpolatedTransform::SetChild(
88 std::unique_ptr<InterpolatedTransform> child) {
89 child_ = std::move(child);
90}
91
92inline float InterpolatedTransform::ValueBetween(float time,
93 float start_value,
94 float end_value) const {
95 // can't handle NaN
96 DCHECK(time == time && start_time_ == start_time_ && end_time_ == end_time_);
97 if (time != time || start_time_ != start_time_ || end_time_ != end_time_)
98 return start_value;
99
100 // Ok if equal -- we'll get a step function. Note: if end_time_ ==
101 // start_time_ == x, then if none of the numbers are NaN, then it
102 // must be true that time < x or time >= x, so we will return early
103 // due to one of the following if statements.
104 DCHECK(end_time_ >= start_time_);
105
106 if (time < start_time_)
107 return start_value;
108
109 if (time >= end_time_)
110 return end_value;
111
112 float t = (time - start_time_) / (end_time_ - start_time_);
113 return static_cast<float>(
114 gfx::Tween::DoubleValueBetween(t, start_value, end_value));
115}
116
117///////////////////////////////////////////////////////////////////////////////
118// InterpolatedRotation
119//
120
121InterpolatedRotation::InterpolatedRotation(float start_degrees,
122 float end_degrees)
123 : InterpolatedTransform(),
124 start_degrees_(start_degrees),
125 end_degrees_(end_degrees) {
126}
127
128InterpolatedRotation::InterpolatedRotation(float start_degrees,
129 float end_degrees,
130 float start_time,
131 float end_time)
132 : InterpolatedTransform(start_time, end_time),
133 start_degrees_(start_degrees),
134 end_degrees_(end_degrees) {
135}
136
137InterpolatedRotation::~InterpolatedRotation() {}
138
139gfx::Transform InterpolatedRotation::InterpolateButDoNotCompose(float t) const {
140 gfx::Transform result;
141 float interpolated_degrees = ValueBetween(t, start_degrees_, end_degrees_);
142 result.Rotate(interpolated_degrees);
143 if (t == 0.0f || t == 1.0f)
144 MassageRotationIfMultipleOfNinetyDegrees(&result, interpolated_degrees);
145 return result;
146}
147
148///////////////////////////////////////////////////////////////////////////////
149// InterpolatedAxisAngleRotation
150//
151
152InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
153 const gfx::Vector3dF& axis,
154 float start_degrees,
155 float end_degrees)
156 : InterpolatedTransform(),
157 axis_(axis),
158 start_degrees_(start_degrees),
159 end_degrees_(end_degrees) {
160}
161
162InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
163 const gfx::Vector3dF& axis,
164 float start_degrees,
165 float end_degrees,
166 float start_time,
167 float end_time)
168 : InterpolatedTransform(start_time, end_time),
169 axis_(axis),
170 start_degrees_(start_degrees),
171 end_degrees_(end_degrees) {
172}
173
174InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
175
176gfx::Transform
177InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t) const {
178 gfx::Transform result;
179 result.RotateAbout(axis_, ValueBetween(t, start_degrees_, end_degrees_));
180 return result;
181}
182
183///////////////////////////////////////////////////////////////////////////////
184// InterpolatedScale
185//
186
187InterpolatedScale::InterpolatedScale(float start_scale, float end_scale)
188 : InterpolatedTransform(),
189 start_scale_(gfx::Point3F(start_scale, start_scale, start_scale)),
190 end_scale_(gfx::Point3F(end_scale, end_scale, end_scale)) {
191}
192
193InterpolatedScale::InterpolatedScale(float start_scale, float end_scale,
194 float start_time, float end_time)
195 : InterpolatedTransform(start_time, end_time),
196 start_scale_(gfx::Point3F(start_scale, start_scale, start_scale)),
197 end_scale_(gfx::Point3F(end_scale, end_scale, end_scale)) {
198}
199
200InterpolatedScale::InterpolatedScale(const gfx::Point3F& start_scale,
201 const gfx::Point3F& end_scale)
202 : InterpolatedTransform(),
203 start_scale_(start_scale),
204 end_scale_(end_scale) {
205}
206
207InterpolatedScale::InterpolatedScale(const gfx::Point3F& start_scale,
208 const gfx::Point3F& end_scale,
209 float start_time,
210 float end_time)
211 : InterpolatedTransform(start_time, end_time),
212 start_scale_(start_scale),
213 end_scale_(end_scale) {
214}
215
216InterpolatedScale::~InterpolatedScale() {}
217
218gfx::Transform InterpolatedScale::InterpolateButDoNotCompose(float t) const {
219 gfx::Transform result;
220 float scale_x = ValueBetween(t, start_scale_.x(), end_scale_.x());
221 float scale_y = ValueBetween(t, start_scale_.y(), end_scale_.y());
222 float scale_z = ValueBetween(t, start_scale_.z(), end_scale_.z());
223 result.Scale3d(scale_x, scale_y, scale_z);
224 return result;
225}
226
227///////////////////////////////////////////////////////////////////////////////
228// InterpolatedTranslation
229//
230
231InterpolatedTranslation::InterpolatedTranslation(const gfx::PointF& start_pos,
232 const gfx::PointF& end_pos)
233 : InterpolatedTransform(), start_pos_(start_pos), end_pos_(end_pos) {}
234
235InterpolatedTranslation::InterpolatedTranslation(const gfx::PointF& start_pos,
236 const gfx::PointF& end_pos,
237 float start_time,
238 float end_time)
239 : InterpolatedTransform(start_time, end_time),
240 start_pos_(start_pos),
241 end_pos_(end_pos) {}
242
243InterpolatedTranslation::InterpolatedTranslation(const gfx::Point3F& start_pos,
244 const gfx::Point3F& end_pos)
245 : InterpolatedTransform(), start_pos_(start_pos), end_pos_(end_pos) {
246}
247
248InterpolatedTranslation::InterpolatedTranslation(const gfx::Point3F& start_pos,
249 const gfx::Point3F& end_pos,
250 float start_time,
251 float end_time)
252 : InterpolatedTransform(start_time, end_time),
253 start_pos_(start_pos),
254 end_pos_(end_pos) {
255}
256
257InterpolatedTranslation::~InterpolatedTranslation() {}
258
259gfx::Transform
260InterpolatedTranslation::InterpolateButDoNotCompose(float t) const {
261 gfx::Transform result;
262 result.Translate3d(ValueBetween(t, start_pos_.x(), end_pos_.x()),
263 ValueBetween(t, start_pos_.y(), end_pos_.y()),
264 ValueBetween(t, start_pos_.z(), end_pos_.z()));
265 return result;
266}
267
268///////////////////////////////////////////////////////////////////////////////
269// InterpolatedConstantTransform
270//
271
272InterpolatedConstantTransform::InterpolatedConstantTransform(
273 const gfx::Transform& transform)
274 : InterpolatedTransform(),
275 transform_(transform) {
276}
277
278gfx::Transform
279InterpolatedConstantTransform::InterpolateButDoNotCompose(float t) const {
280 return transform_;
281}
282
283InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
284
285///////////////////////////////////////////////////////////////////////////////
286// InterpolatedTransformAboutPivot
287//
288
289InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
290 const gfx::Point& pivot,
291 std::unique_ptr<InterpolatedTransform> transform)
292 : InterpolatedTransform() {
293 Init(pivot, std::move(transform));
294}
295
296InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
297 const gfx::Point& pivot,
298 std::unique_ptr<InterpolatedTransform> transform,
299 float start_time,
300 float end_time)
301 : InterpolatedTransform() {
302 Init(pivot, std::move(transform));
303}
304
305InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
306
307gfx::Transform
308InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t) const {
309 if (transform_.get()) {
310 return transform_->Interpolate(t);
311 }
312 return gfx::Transform();
313}
314
315void InterpolatedTransformAboutPivot::Init(
316 const gfx::Point& pivot,
317 std::unique_ptr<InterpolatedTransform> xform) {
318 gfx::Transform to_pivot;
319 gfx::Transform from_pivot;
320 to_pivot.Translate(SkIntToScalar(-pivot.x()), SkIntToScalar(-pivot.y()));
321 from_pivot.Translate(SkIntToScalar(pivot.x()), SkIntToScalar(pivot.y()));
322
323 std::unique_ptr<InterpolatedTransform> pre_transform =
324 std::make_unique<InterpolatedConstantTransform>(to_pivot);
325 std::unique_ptr<InterpolatedTransform> post_transform =
326 std::make_unique<InterpolatedConstantTransform>(from_pivot);
327
328 xform->SetChild(std::move(post_transform));
329 pre_transform->SetChild(std::move(xform));
330 transform_ = std::move(pre_transform);
331}
332
333InterpolatedMatrixTransform::InterpolatedMatrixTransform(
334 const gfx::Transform& start_transform,
335 const gfx::Transform& end_transform)
336 : InterpolatedTransform() {
337 Init(start_transform, end_transform);
338}
339
340InterpolatedMatrixTransform::InterpolatedMatrixTransform(
341 const gfx::Transform& start_transform,
342 const gfx::Transform& end_transform,
343 float start_time,
344 float end_time)
345 : InterpolatedTransform() {
346 Init(start_transform, end_transform);
347}
348
349InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
350
351gfx::Transform
352InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t) const {
353 gfx::DecomposedTransform blended =
354 gfx::BlendDecomposedTransforms(end_decomp_, start_decomp_, t);
Kaido Kert25902c62024-06-17 17:10:28 -0700355 return gfx::Transform::Compose(blended);
Kaido Kert56d7c4e2024-04-13 12:59:27 -0700356}
357
358void InterpolatedMatrixTransform::Init(const gfx::Transform& start_transform,
359 const gfx::Transform& end_transform) {
Kaido Kert25902c62024-06-17 17:10:28 -0700360 // Both transforms should be decomposible.
361 start_decomp_ = *start_transform.Decompose();
362 end_decomp_ = *end_transform.Decompose();
Kaido Kert56d7c4e2024-04-13 12:59:27 -0700363}
364
365} // namespace ui