| Index: Source/platform/transforms/TransformOperations.cpp
|
| diff --git a/Source/platform/transforms/TransformOperations.cpp b/Source/platform/transforms/TransformOperations.cpp
|
| index a0664f7d71439d9f48642e4150e4e74c81ba613d..809789fe3112f8bc3adecd6ee1d122568bc9571c 100644
|
| --- a/Source/platform/transforms/TransformOperations.cpp
|
| +++ b/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;
|
| @@ -109,6 +112,267 @@ 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;
|
|
|