diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp | 268 |
1 files changed, 266 insertions, 2 deletions
diff --git a/chromium/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp b/chromium/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp index 4f7d82fa923..809789fe311 100644 --- a/chromium/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp +++ b/chromium/third_party/WebKit/Source/platform/transforms/TransformOperations.cpp @@ -22,8 +22,11 @@ #include "config.h" #include "platform/transforms/TransformOperations.h" +#include "platform/animation/AnimationUtilities.h" +#include "platform/geometry/FloatBox.h" #include "platform/transforms/IdentityTransformOperation.h" #include "platform/transforms/InterpolatedTransformOperation.h" +#include "platform/transforms/RotateTransformOperation.h" #include <algorithm> using namespace std; @@ -75,7 +78,7 @@ TransformOperations TransformOperations::blendByMatchingOperations(const Transfo for (unsigned i = 0; i < size; i++) { RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i].get() : 0; RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i].get() : 0; - RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : 0); + RefPtr<TransformOperation> blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : nullptr); if (blendedOperation) result.operations().append(blendedOperation); else { @@ -109,11 +112,272 @@ TransformOperations TransformOperations::blend(const TransformOperations& from, return blendByUsingMatrixInterpolation(from, progress); } +static void findCandidatesInPlane(double px, double py, double nz, double* candidates, int* numCandidates) +{ + // The angle that this point is rotated with respect to the plane nz + double phi = atan2(px, py); + + *numCandidates = 4; + candidates[0] = phi; // The element at 0deg (maximum x) + + for (int i = 1; i < *numCandidates; ++i) + candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg + if (nz < 0.f) { + for (int i = 0; i < *numCandidates; ++i) + candidates[i] *= -1; + } +} + +// This method returns the bounding box that contains the starting point, +// the ending point, and any of the extrema (in each dimension) found across +// the circle described by the arc. These are then filtered to points that +// actually reside on the arc. +static void boundingBoxForArc(const FloatPoint3D& point, const RotateTransformOperation& fromTransform, const RotateTransformOperation& toTransform, double minProgress, double maxProgress, FloatBox& box) +{ + double candidates[6]; + int numCandidates = 0; + + FloatPoint3D axis(fromTransform.axis()); + double fromDegrees = fromTransform.angle(); + double toDegrees = toTransform.angle(); + + if (axis.dot(toTransform.axis()) < 0) + toDegrees *= -1; + + fromDegrees = blend(fromDegrees, toTransform.angle(), minProgress); + toDegrees = blend(toDegrees, fromTransform.angle(), 1.0 - maxProgress); + if (fromDegrees > toDegrees) + std::swap(fromDegrees, toDegrees); + + TransformationMatrix fromMatrix; + TransformationMatrix toMatrix; + fromMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), fromDegrees); + toMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), toDegrees); + + FloatPoint3D fromPoint = fromMatrix.mapPoint(point); + FloatPoint3D toPoint = toMatrix.mapPoint(point); + + if (box.isEmpty()) + box.setOrigin(fromPoint); + else + box.expandTo(fromPoint); + + box.expandTo(toPoint); + + switch (fromTransform.type()) { + case TransformOperation::RotateX: + findCandidatesInPlane(point.y(), point.z(), fromTransform.x(), candidates, &numCandidates); + break; + case TransformOperation::RotateY: + findCandidatesInPlane(point.z(), point.x(), fromTransform.y(), candidates, &numCandidates); + break; + case TransformOperation::RotateZ: + findCandidatesInPlane(point.x(), point.y(), fromTransform.z(), candidates, &numCandidates); + break; + default: + { + FloatPoint3D normal = axis; + if (normal.isZero()) + return; + normal.normalize(); + FloatPoint3D origin; + FloatPoint3D toPoint = point - origin; + FloatPoint3D center = origin + normal * toPoint.dot(normal); + FloatPoint3D v1 = point - center; + if (v1.isZero()) + return; + + v1.normalize(); + FloatPoint3D v2 = normal.cross(v1); + // v1 is the basis vector in the direction of the point. + // i.e. with a rotation of 0, v1 is our +x vector. + // v2 is a perpenticular basis vector of our plane (+y). + + // Take the parametric equation of a circle. + // (x = r*cos(t); y = r*sin(t); + // We can treat that as a circle on the plane v1xv2 + // From that we get the parametric equations for a circle on the + // plane in 3d space of + // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx + // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy + // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz + // taking the derivative of (x, y, z) and solving for 0 gives us our + // maximum/minimum x, y, z values + // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0 + // tan(t) = v2.x/v1.x + // t = atan2(v2.x, v1.x) + n*M_PI; + + candidates[0] = atan2(v2.x(), v1.x()); + candidates[1] = candidates[0] + M_PI; + candidates[2] = atan2(v2.y(), v1.y()); + candidates[3] = candidates[2] + M_PI; + candidates[4] = atan2(v2.z(), v1.z()); + candidates[5] = candidates[4] + M_PI; + numCandidates = 6; + } + break; + } + + double minRadians = deg2rad(fromDegrees); + double maxRadians = deg2rad(toDegrees); + // Once we have the candidates, we now filter them down to ones that + // actually live on the arc, rather than the entire circle. + for (int i = 0; i < numCandidates; ++i) { + double radians = candidates[i]; + + while (radians < minRadians) + radians += 2.0 * M_PI; + while (radians > maxRadians) + radians -= 2.0 * M_PI; + if (radians < minRadians) + continue; + + TransformationMatrix rotation; + rotation.rotate3d(axis.x(), axis.y(), axis.z(), rad2deg(radians)); + box.expandTo(rotation.mapPoint(point)); + } +} + +bool TransformOperations::blendedBoundsForBox(const FloatBox& box, const TransformOperations& from, const double& minProgress, const double& maxProgress, FloatBox* bounds) const +{ + + int fromSize = from.operations().size(); + int toSize = operations().size(); + int size = max(fromSize, toSize); + + *bounds = box; + for (int i = size - 1; i >= 0; i--) { + RefPtr<TransformOperation> fromOperation = (i < fromSize) ? from.operations()[i] : nullptr; + RefPtr<TransformOperation> toOperation = (i < toSize) ? operations()[i] : nullptr; + if (fromOperation && fromOperation->type() == TransformOperation::None) + fromOperation = nullptr; + + if (toOperation && toOperation->type() == TransformOperation::None) + toOperation = nullptr; + + TransformOperation::OperationType interpolationType = toOperation ? toOperation->type() : + fromOperation ? fromOperation->type() : + TransformOperation::None; + if (fromOperation && toOperation && !fromOperation->canBlendWith(*toOperation.get())) + return false; + + switch (interpolationType) { + case TransformOperation::Identity: + bounds->expandTo(box); + continue; + case TransformOperation::Translate: + case TransformOperation::TranslateX: + case TransformOperation::TranslateY: + case TransformOperation::TranslateZ: + case TransformOperation::Translate3D: + case TransformOperation::Scale: + case TransformOperation::ScaleX: + case TransformOperation::ScaleY: + case TransformOperation::ScaleZ: + case TransformOperation::Scale3D: + case TransformOperation::Skew: + case TransformOperation::SkewX: + case TransformOperation::SkewY: + case TransformOperation::Perspective: + { + RefPtr<TransformOperation> fromTransform; + RefPtr<TransformOperation> toTransform; + if (!toOperation) { + fromTransform = fromOperation->blend(toOperation.get(), 1-minProgress, false); + toTransform = fromOperation->blend(toOperation.get(), 1-maxProgress, false); + } else { + fromTransform = toOperation->blend(fromOperation.get(), minProgress, false); + toTransform = toOperation->blend(fromOperation.get(), maxProgress, false); + } + if (!fromTransform || !toTransform) + continue; + TransformationMatrix fromMatrix; + TransformationMatrix toMatrix; + fromTransform->apply(fromMatrix, FloatSize()); + toTransform->apply(toMatrix, FloatSize()); + FloatBox fromBox = *bounds; + FloatBox toBox = *bounds; + fromMatrix.transformBox(fromBox); + toMatrix.transformBox(toBox); + *bounds = fromBox; + bounds->expandTo(toBox); + continue; + } + case TransformOperation::Rotate: // This is also RotateZ + case TransformOperation::Rotate3D: + case TransformOperation::RotateX: + case TransformOperation::RotateY: + { + RefPtr<RotateTransformOperation> identityRotation; + const RotateTransformOperation* fromRotation = nullptr; + const RotateTransformOperation* toRotation = nullptr; + if (fromOperation) { + fromRotation = static_cast<const RotateTransformOperation*>(fromOperation.get()); + if (fromRotation->axis().isZero()) + fromRotation = nullptr; + } + + if (toOperation) { + toRotation = static_cast<const RotateTransformOperation*>(toOperation.get()); + if (toRotation->axis().isZero()) + toRotation = nullptr; + } + + double fromAngle; + double toAngle; + FloatPoint3D axis; + if (!RotateTransformOperation::shareSameAxis(fromRotation, toRotation, &axis, &fromAngle, &toAngle)) { + return(false); + } + + if (!fromRotation) { + identityRotation = RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->type()); + fromRotation = identityRotation.get(); + } + + if (!toRotation) { + if (!identityRotation) + identityRotation = RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->type()); + toRotation = identityRotation.get(); + } + + FloatBox fromBox = *bounds; + bool first = true; + for (size_t i = 0; i < 2; ++i) { + for (size_t j = 0; j < 2; ++j) { + for (size_t k = 0; k < 2; ++k) { + FloatBox boundsForArc; + FloatPoint3D corner(fromBox.x(), fromBox.y(), fromBox.z()); + corner += FloatPoint3D(i * fromBox.width(), j * fromBox.height(), k * fromBox.depth()); + boundingBoxForArc(corner, *fromRotation, *toRotation, minProgress, maxProgress, boundsForArc); + if (first) { + *bounds = boundsForArc; + first = false; + } else { + bounds->expandTo(boundsForArc); + } + } + } + } + } + continue; + case TransformOperation::None: + continue; + case TransformOperation::Matrix: + case TransformOperation::Matrix3D: + case TransformOperation::Interpolated: + return(false); + } + } + + return true; +} + TransformOperations TransformOperations::add(const TransformOperations& addend) const { TransformOperations result; result.m_operations = operations(); - result.m_operations.append(addend.operations()); + result.m_operations.appendVector(addend.operations()); return result; } |