Chromium Code Reviews| Index: cc/transform_operations.cc |
| diff --git a/cc/transform_operations.cc b/cc/transform_operations.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2e57d5152ceaeecc35a232dec13c658ebd0980b9 |
| --- /dev/null |
| +++ b/cc/transform_operations.cc |
| @@ -0,0 +1,383 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <algorithm> |
| +#include <cmath> |
| +#include <limits> |
| +#include <vector> |
| + |
| +#include "cc/transform_operations.h" |
| + |
| +using WebKit::WebTransformationMatrix; |
| + |
| +namespace { |
| +const double EPSILON = 1e-4; |
|
jamesr
2013/01/14 22:03:00
use chromium style kEpsilon
it's pretty weird for
Ian Vollick
2013/01/15 15:02:38
I used this epsilon when checking if two rotations
|
| +} |
| + |
| +namespace cc { |
| + |
| +struct TransformOperation { |
| + enum Type { |
| + TransformOperationTranslate, |
| + TransformOperationRotate, |
| + TransformOperationScale, |
| + TransformOperationSkew, |
| + TransformOperationPerspective, |
| + TransformOperationMatrix, |
| + TransformOperationIdentity |
| + }; |
| + |
| + TransformOperation() |
| + : type(TransformOperationIdentity) { |
|
jamesr
2013/01/14 22:03:00
this should be indented 4 spaces from the previous
|
| + } |
| + |
| + Type type; |
| + WebKit::WebTransformationMatrix matrix; |
| + |
| + union { |
| + double perspective_depth; |
| + |
| + struct { |
| + double x, y; |
| + } skew; |
| + |
| + struct { |
| + double x, y, z; |
| + } scale; |
| + |
| + struct { |
| + double x, y, z; |
| + } translate; |
| + |
| + struct { |
| + struct { |
| + double x, y, z; |
| + } axis; |
| + |
| + double angle; |
| + } rotate; |
| + }; |
| + |
| + bool IsIdentity() const { return matrix.isIdentity(); } |
| +}; |
| + |
| +TransformOperations::TransformOperations() { |
| + Initialize(); |
| +} |
| + |
| +TransformOperations::TransformOperations(const TransformOperations& other) { |
| + Initialize(other); |
| +} |
| + |
| +TransformOperations::~TransformOperations() { |
| +} |
| + |
| +struct TransformOperationsPrivate { |
| + std::vector<TransformOperation> operations; |
| +}; |
| + |
| +WebTransformationMatrix TransformOperations::Apply() const { |
| + WebTransformationMatrix to_return; |
| + for (size_t i = 0; i < private_->operations.size(); ++i) |
| + to_return.multiply(private_->operations[i].matrix); |
| + return to_return; |
| +} |
| + |
| +static bool IsIdentity(const TransformOperation* operation) { |
| + return !operation || operation->IsIdentity(); |
| +} |
| + |
| +static bool ShareSameAxis(const TransformOperation* from, |
| + const TransformOperation* to, |
| + double& axis_x, double& axis_y, double& axis_z, |
| + double& angle_from) { |
| + if (IsIdentity(from) && IsIdentity(to)) |
| + return false; |
| + |
| + if (IsIdentity(from) && !IsIdentity(to)) { |
| + axis_x = to->rotate.axis.x; |
| + axis_y = to->rotate.axis.y; |
| + axis_z = to->rotate.axis.z; |
| + angle_from = 0; |
| + return true; |
| + } |
| + |
| + if (!IsIdentity(from) && IsIdentity(to)) { |
| + axis_x = from->rotate.axis.x; |
| + axis_y = from->rotate.axis.y; |
| + axis_z = from->rotate.axis.z; |
| + angle_from = from->rotate.angle; |
| + return true; |
| + } |
| + |
| + double length_2 = from->rotate.axis.x * from->rotate.axis.x + |
| + from->rotate.axis.y * from->rotate.axis.y + |
| + from->rotate.axis.z * from->rotate.axis.z; |
| + double other_length_2 = to->rotate.axis.x * to->rotate.axis.x + |
| + to->rotate.axis.y * to->rotate.axis.y + |
| + to->rotate.axis.z * to->rotate.axis.z; |
| + |
| + if (length_2 <= EPSILON || other_length_2 <= EPSILON) |
| + return false; |
| + |
| + double dot = to->rotate.axis.x * from->rotate.axis.x + |
| + to->rotate.axis.y * from->rotate.axis.y + |
| + to->rotate.axis.z * from->rotate.axis.z; |
| + double error = std::fabs(1.0 - (dot * dot) / (length_2 * other_length_2)); |
| + bool result = error < EPSILON; |
| + if (result) { |
| + axis_x = to->rotate.axis.x; |
| + axis_y = to->rotate.axis.y; |
| + axis_z = to->rotate.axis.z; |
| + // If the axes are pointing in opposite directions, we need to reverse |
| + // the angle. |
| + angle_from = dot > 0 ? from->rotate.angle : -from->rotate.angle; |
| + } |
| + return result; |
| +} |
| + |
| +static double BlendDoubles(double from, double to, double progress) { |
| + return from * (1 - progress) + to * progress; |
| +} |
| + |
| +static bool BlendTransformOperations(const TransformOperation* from, |
| + const TransformOperation* to, |
| + double progress, |
| + WebTransformationMatrix& result) { |
| + if (IsIdentity(from) && IsIdentity(to)) |
| + return true; |
| + |
| + TransformOperation::Type interpolation_type = |
| + TransformOperation::TransformOperationIdentity; |
| + if (IsIdentity(to)) |
| + interpolation_type = from->type; |
| + else |
| + interpolation_type = to->type; |
| + |
| + switch (interpolation_type) { |
| + case TransformOperation::TransformOperationTranslate: { |
| + double from_x = IsIdentity(from) ? 0 : from->translate.x; |
| + double from_y = IsIdentity(from) ? 0 : from->translate.y; |
| + double from_z = IsIdentity(from) ? 0 : from->translate.z; |
| + double to_x = IsIdentity(to) ? 0 : to->translate.x; |
| + double to_y = IsIdentity(to) ? 0 : to->translate.y; |
| + double to_z = IsIdentity(to) ? 0 : to->translate.z; |
| + result.translate3d(BlendDoubles(from_x, to_x, progress), |
| + BlendDoubles(from_y, to_y, progress), |
| + BlendDoubles(from_z, to_z, progress)); |
| + break; |
| + } |
| + case TransformOperation::TransformOperationRotate: { |
| + double axis_x = 0; |
| + double axis_y = 0; |
| + double axis_z = 1; |
| + double from_angle = 0; |
| + double to_angle = IsIdentity(to) ? 0 : to->rotate.angle; |
| + if (ShareSameAxis(from, to, axis_x, axis_y, axis_z, from_angle)) |
| + result.rotate3d(axis_x, axis_y, axis_z, |
| + BlendDoubles(from_angle, to_angle, progress)); |
| + else { |
| + WebTransformationMatrix to_matrix; |
| + if (!IsIdentity(to)) |
| + to_matrix = to->matrix; |
| + WebTransformationMatrix from_matrix; |
| + if (!IsIdentity(from)) |
| + from_matrix = from->matrix; |
| + result = to_matrix; |
| + if (!result.blend(from_matrix, progress)) |
| + return false; |
| + } |
| + break; |
| + } |
| + case TransformOperation::TransformOperationScale: { |
| + double from_x = IsIdentity(from) ? 1 : from->scale.x; |
| + double from_y = IsIdentity(from) ? 1 : from->scale.y; |
| + double from_z = IsIdentity(from) ? 1 : from->scale.z; |
| + double to_x = IsIdentity(to) ? 1 : to->scale.x; |
| + double to_y = IsIdentity(to) ? 1 : to->scale.y; |
| + double to_z = IsIdentity(to) ? 1 : to->scale.z; |
| + result.scale3d(BlendDoubles(from_x, to_x, progress), |
| + BlendDoubles(from_y, to_y, progress), |
| + BlendDoubles(from_z, to_z, progress)); |
| + break; |
| + } |
| + case TransformOperation::TransformOperationSkew: { |
| + double from_x = IsIdentity(from) ? 0 : from->skew.x; |
| + double from_y = IsIdentity(from) ? 0 : from->skew.y; |
| + double to_x = IsIdentity(to) ? 0 : to->skew.x; |
| + double to_y = IsIdentity(to) ? 0 : to->skew.y; |
| + result.skewX(BlendDoubles(from_x, to_x, progress)); |
| + result.skewY(BlendDoubles(from_y, to_y, progress)); |
| + break; |
| + } |
| + case TransformOperation::TransformOperationPerspective: { |
| + double from_perspective_depth = IsIdentity(from) ? |
| + std::numeric_limits<double>::max() : from->perspective_depth; |
| + double to_perspective_depth = IsIdentity(to) ? |
| + std::numeric_limits<double>::max() : to->perspective_depth; |
| + result.applyPerspective( |
| + BlendDoubles(from_perspective_depth, to_perspective_depth, progress)); |
| + break; |
| + } |
| + case TransformOperation::TransformOperationMatrix: { |
| + WebTransformationMatrix to_matrix; |
| + if (!IsIdentity(to)) |
| + to_matrix = to->matrix; |
| + WebTransformationMatrix from_matrix; |
| + if (!IsIdentity(from)) |
| + from_matrix = from->matrix; |
| + result = to_matrix; |
| + if (!result.blend(from_matrix, progress)) |
| + return false; |
| + break; |
| + } |
| + case TransformOperation::TransformOperationIdentity: |
| + // Do nothing. |
| + break; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +WebTransformationMatrix TransformOperations::Blend( |
| + const TransformOperations& from, double progress) const { |
| + WebTransformationMatrix to_return; |
| + BlendInternal(from, progress, to_return); |
| + return to_return; |
| +} |
| + |
| +bool TransformOperations::MatchesTypes(const TransformOperations& other) const { |
| + if (IsIdentity() || other.IsIdentity()) |
| + return true; |
| + |
| + if (private_->operations.size() != other.private_->operations.size()) |
| + return false; |
| + |
| + for (size_t i = 0; i < private_->operations.size(); ++i) { |
| + if (private_->operations[i].type != other.private_->operations[i].type |
| + && !private_->operations[i].IsIdentity() |
| + && !other.private_->operations[i].IsIdentity()) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool TransformOperations::CanBlendWith( |
| + const TransformOperations& other) const { |
| + WebTransformationMatrix dummy; |
| + return BlendInternal(other, 0.5, dummy); |
| +} |
| + |
| +void TransformOperations::AppendTranslate(double x, double y, double z) { |
| + TransformOperation to_add; |
| + to_add.matrix.translate3d(x, y, z); |
| + to_add.type = TransformOperation::TransformOperationTranslate; |
| + to_add.translate.x = x; |
| + to_add.translate.y = y; |
| + to_add.translate.z = z; |
| + private_->operations.push_back(to_add); |
| +} |
| + |
| +void TransformOperations::AppendRotate(double x, double y, double z, |
| + double degrees) { |
| + TransformOperation to_add; |
| + to_add.matrix.rotate3d(x, y, z, degrees); |
| + to_add.type = TransformOperation::TransformOperationRotate; |
| + to_add.rotate.axis.x = x; |
| + to_add.rotate.axis.y = y; |
| + to_add.rotate.axis.z = z; |
| + to_add.rotate.angle = degrees; |
| + private_->operations.push_back(to_add); |
| +} |
| + |
| +void TransformOperations::AppendScale(double x, double y, double z) { |
| + TransformOperation to_add; |
| + to_add.matrix.scale3d(x, y, z); |
| + to_add.type = TransformOperation::TransformOperationScale; |
| + to_add.scale.x = x; |
| + to_add.scale.y = y; |
| + to_add.scale.z = z; |
| + private_->operations.push_back(to_add); |
| +} |
| + |
| +void TransformOperations::AppendSkew(double x, double y) { |
| + TransformOperation to_add; |
| + to_add.matrix.skewX(x); |
| + to_add.matrix.skewY(y); |
| + to_add.type = TransformOperation::TransformOperationSkew; |
| + to_add.skew.x = x; |
| + to_add.skew.y = y; |
| + private_->operations.push_back(to_add); |
| +} |
| + |
| +void TransformOperations::AppendPerspective(double depth) { |
| + TransformOperation to_add; |
| + to_add.matrix.applyPerspective(depth); |
| + to_add.type = TransformOperation::TransformOperationPerspective; |
| + to_add.perspective_depth = depth; |
| + private_->operations.push_back(to_add); |
| +} |
| + |
| +void TransformOperations::AppendMatrix(const WebTransformationMatrix& matrix) { |
| + TransformOperation to_add; |
| + to_add.matrix = matrix; |
| + to_add.type = TransformOperation::TransformOperationMatrix; |
| + private_->operations.push_back(to_add); |
| +} |
| + |
| +void TransformOperations::AppendIdentity() { |
| + private_->operations.push_back(TransformOperation()); |
| +} |
| + |
| +bool TransformOperations::IsIdentity() const { |
| + for (size_t i = 0; i < private_->operations.size(); ++i) { |
| + if (!private_->operations[i].IsIdentity()) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +void TransformOperations::Initialize() { |
| + private_.reset(new TransformOperationsPrivate); |
| +} |
| + |
| +void TransformOperations::Initialize(const TransformOperations& other) { |
| + if (private_.get() != other.private_.get()) |
| + private_.reset(new TransformOperationsPrivate(*other.private_.get())); |
| + else |
| + Initialize(); |
| +} |
| + |
| +bool TransformOperations::BlendInternal(const TransformOperations& from, |
| + double progress, |
| + WebTransformationMatrix& result) const { |
| + bool from_identity = from.IsIdentity(); |
| + bool to_identity = IsIdentity(); |
| + if (from_identity && to_identity) |
| + return true; |
| + |
| + if (MatchesTypes(from)) { |
| + size_t num_operations = |
| + std::max(from_identity ? 0 : from.private_->operations.size(), |
| + to_identity ? 0 : private_->operations.size()); |
| + for (size_t i = 0; i < num_operations; ++i) { |
| + WebTransformationMatrix blended; |
| + if (!BlendTransformOperations( |
| + from_identity ? 0 : &from.private_->operations[i], |
| + to_identity ? 0 : &private_->operations[i], |
| + progress, |
| + blended)) |
| + return false; |
| + result.multiply(blended); |
| + } |
| + return true; |
| + } |
| + |
| + result = Apply(); |
| + WebTransformationMatrix from_transform = from.Apply(); |
| + return result.blend(from_transform, progress); |
| +} |
| + |
| +} // namespace cc |