Home | History | Annotate | Download | only in animation
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "platform/animation/TimingFunction.h"
      7 
      8 #include "wtf/MathExtras.h"
      9 
     10 namespace blink {
     11 
     12 String LinearTimingFunction::toString() const
     13 {
     14     return "linear";
     15 }
     16 
     17 double LinearTimingFunction::evaluate(double fraction, double) const
     18 {
     19     return fraction;
     20 }
     21 
     22 void LinearTimingFunction::range(double* minValue, double* maxValue) const
     23 {
     24 }
     25 
     26 String CubicBezierTimingFunction::toString() const
     27 {
     28     switch (this->subType()) {
     29     case CubicBezierTimingFunction::Ease:
     30         return "ease";
     31     case CubicBezierTimingFunction::EaseIn:
     32         return "ease-in";
     33     case CubicBezierTimingFunction::EaseOut:
     34         return "ease-out";
     35     case CubicBezierTimingFunction::EaseInOut:
     36         return "ease-in-out";
     37     case CubicBezierTimingFunction::Custom:
     38         return "cubic-bezier(" + String::numberToStringECMAScript(this->x1()) + ", " +
     39             String::numberToStringECMAScript(this->y1()) + ", " + String::numberToStringECMAScript(this->x2()) +
     40             ", " + String::numberToStringECMAScript(this->y2()) + ")";
     41     default:
     42         ASSERT_NOT_REACHED();
     43     }
     44     return "";
     45 }
     46 
     47 double CubicBezierTimingFunction::evaluate(double fraction, double accuracy) const
     48 {
     49     if (!m_bezier)
     50         m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2));
     51     return m_bezier->solve(fraction, accuracy);
     52 }
     53 
     54 // This works by taking taking the derivative of the cubic bezier, on the y
     55 // axis. We can then solve for where the derivative is zero to find the min
     56 // and max distace along the line. We the have to solve those in terms of time
     57 // rather than distance on the x-axis
     58 void CubicBezierTimingFunction::range(double* minValue, double* maxValue) const
     59 {
     60     if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) {
     61         return;
     62     }
     63 
     64     double a = 3.0 * (m_y1 - m_y2) + 1.0;
     65     double b = 2.0 * (m_y2 - 2.0 * m_y1);
     66     double c = m_y1;
     67 
     68     if (std::abs(a) < std::numeric_limits<double>::epsilon()
     69         && std::abs(b) < std::numeric_limits<double>::epsilon()) {
     70         return;
     71     }
     72 
     73     double t1 = 0.0;
     74     double t2 = 0.0;
     75 
     76     if (std::abs(a) < std::numeric_limits<double>::epsilon()) {
     77         t1 = -c / b;
     78     } else {
     79         double discriminant = b * b - 4 * a * c;
     80         if (discriminant < 0)
     81             return;
     82         double discriminantSqrt = sqrt(discriminant);
     83         t1 = (-b + discriminantSqrt) / (2 * a);
     84         t2 = (-b - discriminantSqrt) / (2 * a);
     85     }
     86 
     87     double solution1 = 0.0;
     88     double solution2 = 0.0;
     89 
     90     // If the solution is in the range [0,1] then we include it, otherwise we
     91     // ignore it.
     92     if (!m_bezier)
     93         m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2));
     94 
     95     // An interesting fact about these beziers is that they are only
     96     // actually evaluated in [0,1]. After that we take the tangent at that point
     97     // and linearly project it out.
     98     if (0 < t1 && t1 < 1)
     99         solution1= m_bezier->sampleCurveY(t1);
    100 
    101     if (0 < t2 && t2 < 1)
    102         solution2 = m_bezier->sampleCurveY(t2);
    103 
    104     // Since our input values can be out of the range 0->1 so we must also
    105     // consider the minimum and maximum points.
    106     double solutionMin = m_bezier->solve(*minValue, std::numeric_limits<double>::epsilon());
    107     double solutionMax = m_bezier->solve(*maxValue, std::numeric_limits<double>::epsilon());
    108     *minValue = std::min(std::min(solutionMin, solutionMax), 0.0);
    109     *maxValue = std::max(std::max(solutionMin, solutionMax), 1.0);
    110     *minValue = std::min(std::min(*minValue, solution1), solution2);
    111     *maxValue = std::max(std::max(*maxValue, solution1), solution2);
    112 }
    113 
    114 String StepsTimingFunction::toString() const
    115 {
    116     const char* positionString = nullptr;
    117     switch (stepAtPosition()) {
    118     case Start:
    119         positionString = "start";
    120         break;
    121     case Middle:
    122         positionString = "middle";
    123         break;
    124     case End:
    125         positionString = "end";
    126         break;
    127     }
    128 
    129     StringBuilder builder;
    130     if (this->numberOfSteps() == 1) {
    131         builder.append("step-");
    132         builder.append(positionString);
    133     } else {
    134         builder.append("steps(" + String::numberToStringECMAScript(this->numberOfSteps()) + ", ");
    135         builder.append(positionString);
    136         builder.append(')');
    137     }
    138     return builder.toString();
    139 }
    140 
    141 void StepsTimingFunction::range(double* minValue, double* maxValue) const
    142 {
    143     *minValue = 0;
    144     *maxValue = 1;
    145 }
    146 
    147 double StepsTimingFunction::evaluate(double fraction, double) const
    148 {
    149     double startOffset = 0;
    150     switch (m_stepAtPosition) {
    151     case Start:
    152         startOffset = 1;
    153         break;
    154     case Middle:
    155         startOffset = 0.5;
    156         break;
    157     case End:
    158         startOffset = 0;
    159         break;
    160     default:
    161         ASSERT_NOT_REACHED();
    162         break;
    163     }
    164     return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0);
    165 }
    166 
    167 // Equals operators
    168 bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs)
    169 {
    170     return rhs.type() == TimingFunction::LinearFunction;
    171 }
    172 
    173 bool operator==(const CubicBezierTimingFunction& lhs, const TimingFunction& rhs)
    174 {
    175     if (rhs.type() != TimingFunction::CubicBezierFunction)
    176         return false;
    177 
    178     const CubicBezierTimingFunction& ctf = toCubicBezierTimingFunction(rhs);
    179     if ((lhs.subType() == CubicBezierTimingFunction::Custom) && (ctf.subType() == CubicBezierTimingFunction::Custom))
    180         return (lhs.x1() == ctf.x1()) && (lhs.y1() == ctf.y1()) && (lhs.x2() == ctf.x2()) && (lhs.y2() == ctf.y2());
    181 
    182     return lhs.subType() == ctf.subType();
    183 }
    184 
    185 bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs)
    186 {
    187     if (rhs.type() != TimingFunction::StepsFunction)
    188         return false;
    189 
    190     const StepsTimingFunction& stf = toStepsTimingFunction(rhs);
    191     return (lhs.numberOfSteps() == stf.numberOfSteps()) && (lhs.stepAtPosition() == stf.stepAtPosition());
    192 }
    193 
    194 // The generic operator== *must* come after the
    195 // non-generic operator== otherwise it will end up calling itself.
    196 bool operator==(const TimingFunction& lhs, const TimingFunction& rhs)
    197 {
    198     switch (lhs.type()) {
    199     case TimingFunction::LinearFunction: {
    200         const LinearTimingFunction& linear = toLinearTimingFunction(lhs);
    201         return (linear == rhs);
    202     }
    203     case TimingFunction::CubicBezierFunction: {
    204         const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs);
    205         return (cubic == rhs);
    206     }
    207     case TimingFunction::StepsFunction: {
    208         const StepsTimingFunction& step = toStepsTimingFunction(lhs);
    209         return (step == rhs);
    210     }
    211     default:
    212         ASSERT_NOT_REACHED();
    213     }
    214     return false;
    215 }
    216 
    217 // No need to define specific operator!= as they can all come via this function.
    218 bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs)
    219 {
    220     return !(lhs == rhs);
    221 }
    222 
    223 } // namespace blink
    224