mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
226 lines
7.2 KiB
C++
226 lines
7.2 KiB
C++
// 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 "flutter/sky/engine/platform/animation/TimingFunction.h"
|
|
|
|
#include "flutter/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
|