| // Copyright 2014 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 "sky/engine/platform/animation/TimingFunction.h" |
| |
| #include "sky/engine/wtf/MathExtras.h" |
| |
| namespace blink { |
| |
| String LinearTimingFunction::toString() const |
| { |
| return "linear"; |
| } |
| |
| double LinearTimingFunction::evaluate(double fraction, double) const |
| { |
| return fraction; |
| } |
| |
| void LinearTimingFunction::range(double* minValue, double* maxValue) const |
| { |
| } |
| |
| String CubicBezierTimingFunction::toString() const |
| { |
| switch (this->subType()) { |
| case CubicBezierTimingFunction::Ease: |
| return "ease"; |
| case CubicBezierTimingFunction::EaseIn: |
| return "ease-in"; |
| case CubicBezierTimingFunction::EaseOut: |
| return "ease-out"; |
| case CubicBezierTimingFunction::EaseInOut: |
| return "ease-in-out"; |
| case CubicBezierTimingFunction::Custom: |
| return "cubic-bezier(" + String::numberToStringECMAScript(this->x1()) + ", " + |
| String::numberToStringECMAScript(this->y1()) + ", " + String::numberToStringECMAScript(this->x2()) + |
| ", " + String::numberToStringECMAScript(this->y2()) + ")"; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| return ""; |
| } |
| |
| double CubicBezierTimingFunction::evaluate(double fraction, double accuracy) const |
| { |
| if (!m_bezier) |
| m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); |
| return m_bezier->solve(fraction, accuracy); |
| } |
| |
| // This works by taking taking the derivative of the cubic bezier, on the y |
| // axis. We can then solve for where the derivative is zero to find the min |
| // and max distace along the line. We the have to solve those in terms of time |
| // rather than distance on the x-axis |
| void CubicBezierTimingFunction::range(double* minValue, double* maxValue) const |
| { |
| if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) { |
| return; |
| } |
| |
| double a = 3.0 * (m_y1 - m_y2) + 1.0; |
| double b = 2.0 * (m_y2 - 2.0 * m_y1); |
| double c = m_y1; |
| |
| if (std::abs(a) < std::numeric_limits<double>::epsilon() |
| && std::abs(b) < std::numeric_limits<double>::epsilon()) { |
| return; |
| } |
| |
| double t1 = 0.0; |
| double t2 = 0.0; |
| |
| if (std::abs(a) < std::numeric_limits<double>::epsilon()) { |
| t1 = -c / b; |
| } else { |
| double discriminant = b * b - 4 * a * c; |
| if (discriminant < 0) |
| return; |
| double discriminantSqrt = sqrt(discriminant); |
| t1 = (-b + discriminantSqrt) / (2 * a); |
| t2 = (-b - discriminantSqrt) / (2 * a); |
| } |
| |
| double solution1 = 0.0; |
| double solution2 = 0.0; |
| |
| // If the solution is in the range [0,1] then we include it, otherwise we |
| // ignore it. |
| if (!m_bezier) |
| m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); |
| |
| // An interesting fact about these beziers is that they are only |
| // actually evaluated in [0,1]. After that we take the tangent at that point |
| // and linearly project it out. |
| if (0 < t1 && t1 < 1) |
| solution1= m_bezier->sampleCurveY(t1); |
| |
| if (0 < t2 && t2 < 1) |
| solution2 = m_bezier->sampleCurveY(t2); |
| |
| // Since our input values can be out of the range 0->1 so we must also |
| // consider the minimum and maximum points. |
| double solutionMin = m_bezier->solve(*minValue, std::numeric_limits<double>::epsilon()); |
| double solutionMax = m_bezier->solve(*maxValue, std::numeric_limits<double>::epsilon()); |
| *minValue = std::min(std::min(solutionMin, solutionMax), 0.0); |
| *maxValue = std::max(std::max(solutionMin, solutionMax), 1.0); |
| *minValue = std::min(std::min(*minValue, solution1), solution2); |
| *maxValue = std::max(std::max(*maxValue, solution1), solution2); |
| } |
| |
| String StepsTimingFunction::toString() const |
| { |
| StringBuilder builder; |
| switch (this->subType()) { |
| case StepsTimingFunction::Start: |
| return "step-start"; |
| case StepsTimingFunction::Middle: |
| return "step-middle"; |
| case StepsTimingFunction::End: |
| return "step-end"; |
| case StepsTimingFunction::Custom: |
| builder.append("steps(" + String::numberToStringECMAScript(this->numberOfSteps()) + ", "); |
| |
| if (this->stepAtPosition() == StepsTimingFunction::StepAtStart) |
| builder.appendLiteral("start"); |
| else if (this->stepAtPosition() == StepsTimingFunction::StepAtMiddle) |
| builder.appendLiteral("middle"); |
| else if (this->stepAtPosition() == StepsTimingFunction::StepAtEnd) |
| builder.appendLiteral("end"); |
| else |
| ASSERT_NOT_REACHED(); |
| |
| builder.append(')'); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| return builder.toString(); |
| } |
| |
| void StepsTimingFunction::range(double* minValue, double* maxValue) const |
| { |
| *minValue = 0; |
| *maxValue = 1; |
| } |
| |
| double StepsTimingFunction::evaluate(double fraction, double) const |
| { |
| double startOffset = 0; |
| switch (m_stepAtPosition) { |
| case StepAtStart: |
| startOffset = 1; |
| break; |
| case StepAtMiddle: |
| startOffset = 0.5; |
| break; |
| case StepAtEnd: |
| startOffset = 0; |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0); |
| } |
| |
| // Equals operators |
| bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs) |
| { |
| return rhs.type() == TimingFunction::LinearFunction; |
| } |
| |
| bool operator==(const CubicBezierTimingFunction& lhs, const TimingFunction& rhs) |
| { |
| if (rhs.type() != TimingFunction::CubicBezierFunction) |
| return false; |
| |
| const CubicBezierTimingFunction& ctf = toCubicBezierTimingFunction(rhs); |
| if ((lhs.subType() == CubicBezierTimingFunction::Custom) && (ctf.subType() == CubicBezierTimingFunction::Custom)) |
| return (lhs.x1() == ctf.x1()) && (lhs.y1() == ctf.y1()) && (lhs.x2() == ctf.x2()) && (lhs.y2() == ctf.y2()); |
| |
| return lhs.subType() == ctf.subType(); |
| } |
| |
| bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) |
| { |
| if (rhs.type() != TimingFunction::StepsFunction) |
| return false; |
| |
| const StepsTimingFunction& stf = toStepsTimingFunction(rhs); |
| if ((lhs.subType() == StepsTimingFunction::Custom) && (stf.subType() == StepsTimingFunction::Custom)) |
| return (lhs.numberOfSteps() == stf.numberOfSteps()) && (lhs.stepAtPosition() == stf.stepAtPosition()); |
| |
| return lhs.subType() == stf.subType(); |
| } |
| |
| // The generic operator== *must* come after the |
| // non-generic operator== otherwise it will end up calling itself. |
| bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) |
| { |
| switch (lhs.type()) { |
| case TimingFunction::LinearFunction: { |
| const LinearTimingFunction& linear = toLinearTimingFunction(lhs); |
| return (linear == rhs); |
| } |
| case TimingFunction::CubicBezierFunction: { |
| const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs); |
| return (cubic == rhs); |
| } |
| case TimingFunction::StepsFunction: { |
| const StepsTimingFunction& step = toStepsTimingFunction(lhs); |
| return (step == rhs); |
| } |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| return false; |
| } |
| |
| // No need to define specific operator!= as they can all come via this function. |
| bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs) |
| { |
| return !(lhs == rhs); |
| } |
| |
| } // namespace blink |