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