Home | History | Annotate | Download | only in transforms
      1 /*
      2  * Copyright (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "platform/transforms/RotateTransformOperation.h"
     24 
     25 #include "platform/animation/AnimationUtilities.h"
     26 #include "platform/geometry/FloatPoint3D.h"
     27 #include "wtf/MathExtras.h"
     28 #include <algorithm>
     29 
     30 using namespace std;
     31 
     32 namespace WebCore {
     33 
     34 static const double angleEpsilon = 1e-4;
     35 
     36 FloatPoint3D RotateTransformOperation::axis() const
     37 {
     38     return FloatPoint3D(x(), y(), z());
     39 }
     40 
     41 bool RotateTransformOperation::shareSameAxis(const RotateTransformOperation* from, const RotateTransformOperation* to, FloatPoint3D* axis, double* fromAngle, double* toAngle)
     42 {
     43     *axis = FloatPoint3D(0, 0, 1);
     44     *fromAngle = 0;
     45     *toAngle = 0;
     46 
     47     if (!from && !to)
     48         return true;
     49 
     50     bool fromZero = !from || from->axis().isZero();
     51     bool toZero = !to || to->axis().isZero();
     52 
     53     if (fromZero && toZero)
     54         return true;
     55 
     56     if (fromZero) {
     57         *axis = to->axis();
     58         *toAngle = to->angle();
     59         return true;
     60     }
     61 
     62     if (toZero) {
     63         *axis = from->axis();
     64         *fromAngle = from->angle();
     65         return true;
     66     }
     67 
     68     FloatPoint3D fromAxis = from->axis();
     69     FloatPoint3D toAxis = to->axis();
     70 
     71     double fromSquared = fromAxis.lengthSquared();
     72     double toSquared   = toAxis.lengthSquared();
     73 
     74     double dot = fromAxis.dot(toAxis);
     75     double error = std::abs(1 - (dot * dot) / (fromSquared * toSquared));
     76 
     77     if (error > angleEpsilon)
     78         return false;
     79     *axis = from->axis();
     80     *fromAngle = from->angle();
     81     *toAngle = to->angle();
     82     return true;
     83 }
     84 
     85 PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity)
     86 {
     87     if (from && !from->isSameType(*this))
     88         return this;
     89 
     90     if (blendToIdentity)
     91         return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type);
     92 
     93     const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from);
     94 
     95     // Optimize for single axis rotation
     96     if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) ||
     97                    (fromOp->m_x == 0 && fromOp->m_y == 1 && fromOp->m_z == 0) ||
     98                    (fromOp->m_x == 1 && fromOp->m_y == 0 && fromOp->m_z == 0)) {
     99         double fromAngle = fromOp ? fromOp->m_angle : 0;
    100         return RotateTransformOperation::create(fromOp ? fromOp->m_x : m_x,
    101                                                 fromOp ? fromOp->m_y : m_y,
    102                                                 fromOp ? fromOp->m_z : m_z,
    103                                                 WebCore::blend(fromAngle, m_angle, progress), m_type);
    104     }
    105     double fromAngle;
    106     double toAngle;
    107     FloatPoint3D axis;
    108 
    109     if (shareSameAxis(fromOp, this, &axis, &fromAngle, &toAngle))
    110         return RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), WebCore::blend(fromAngle, toAngle, progress), m_type);
    111 
    112     const RotateTransformOperation* toOp = this;
    113 
    114     // Create the 2 rotation matrices
    115     TransformationMatrix fromT;
    116     TransformationMatrix toT;
    117     fromT.rotate3d((fromOp ? fromOp->m_x : 0),
    118         (fromOp ? fromOp->m_y : 0),
    119         (fromOp ? fromOp->m_z : 1),
    120         (fromOp ? fromOp->m_angle : 0));
    121 
    122     toT.rotate3d((toOp ? toOp->m_x : 0),
    123         (toOp ? toOp->m_y : 0),
    124         (toOp ? toOp->m_z : 1),
    125         (toOp ? toOp->m_angle : 0));
    126 
    127     // Blend them
    128     toT.blend(fromT, progress);
    129 
    130     // Extract the result as a quaternion
    131     TransformationMatrix::DecomposedType decomp;
    132     toT.decompose(decomp);
    133 
    134     // Convert that to Axis/Angle form
    135     double x = -decomp.quaternionX;
    136     double y = -decomp.quaternionY;
    137     double z = -decomp.quaternionZ;
    138     double length = sqrt(x * x + y * y + z * z);
    139     double angle = 0;
    140 
    141     if (length > 0.00001) {
    142         x /= length;
    143         y /= length;
    144         z /= length;
    145         angle = rad2deg(acos(decomp.quaternionW) * 2);
    146     } else {
    147         x = 0;
    148         y = 0;
    149         z = 1;
    150     }
    151     return RotateTransformOperation::create(x, y, z, angle, Rotate3D);
    152 }
    153 
    154 bool RotateTransformOperation::canBlendWith(const TransformOperation& other) const
    155 {
    156     return other.isSameType(*this);
    157 }
    158 
    159 } // namespace WebCore
    160