mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
style: Port bezier edge cases handling from C++ to Rust
Differential Revision: https://phabricator.services.mozilla.com/D150569
This commit is contained in:
parent
168c868330
commit
bb0f857dfa
2 changed files with 68 additions and 13 deletions
|
@ -23,15 +23,65 @@ pub struct Bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bezier {
|
impl Bezier {
|
||||||
/// Create a unit cubic Bézier curve from the two middle control points.
|
/// Calculate the output of a unit cubic Bézier curve from the two middle control points.
|
||||||
///
|
///
|
||||||
/// X coordinate is time, Y coordinate is function advancement.
|
/// X coordinate is time, Y coordinate is function advancement.
|
||||||
/// The nominal range for both is 0 to 1.
|
/// The nominal range for both is 0 to 1.
|
||||||
///
|
///
|
||||||
/// The start and end points are always (0, 0) and (1, 1) so that a transition or animation
|
/// The start and end points are always (0, 0) and (1, 1) so that a transition or animation
|
||||||
/// starts at 0% and ends at 100%.
|
/// starts at 0% and ends at 100%.
|
||||||
|
pub fn calculate_bezier_output(
|
||||||
|
progress: f64,
|
||||||
|
epsilon: f64,
|
||||||
|
x1: f32,
|
||||||
|
y1: f32,
|
||||||
|
x2: f32,
|
||||||
|
y2: f32,
|
||||||
|
) -> f64 {
|
||||||
|
// Check for a linear curve.
|
||||||
|
if x1 == y1 && x2 == y2 {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that we return 0 or 1 on both edges.
|
||||||
|
if progress == 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if progress == 1.0 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For negative values, try to extrapolate with tangent (p1 - p0) or,
|
||||||
|
// if p1 is coincident with p0, with (p2 - p0).
|
||||||
|
if progress < 0.0 {
|
||||||
|
if x1 > 0.0 {
|
||||||
|
return progress * y1 as f64 / x1 as f64;
|
||||||
|
}
|
||||||
|
if y1 == 0.0 && x2 > 0.0 {
|
||||||
|
return progress * y2 as f64 / x2 as f64;
|
||||||
|
}
|
||||||
|
// If we can't calculate a sensible tangent, don't extrapolate at all.
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For values greater than 1, try to extrapolate with tangent (p2 - p3) or,
|
||||||
|
// if p2 is coincident with p3, with (p1 - p3).
|
||||||
|
if progress > 1.0 {
|
||||||
|
if x2 < 1.0 {
|
||||||
|
return 1.0 + (progress - 1.0) * (y2 as f64 - 1.0) / (x2 as f64 - 1.0);
|
||||||
|
}
|
||||||
|
if y2 == 1.0 && x1 < 1.0 {
|
||||||
|
return 1.0 + (progress - 1.0) * (y1 as f64 - 1.0) / (x1 as f64 - 1.0);
|
||||||
|
}
|
||||||
|
// If we can't calculate a sensible tangent, don't extrapolate at all.
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bezier::new(x1, y1, x2, y2).solve(progress, epsilon)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(x1: CSSFloat, y1: CSSFloat, x2: CSSFloat, y2: CSSFloat) -> Bezier {
|
fn new(x1: CSSFloat, y1: CSSFloat, x2: CSSFloat, y2: CSSFloat) -> Bezier {
|
||||||
let cx = 3. * x1 as f64;
|
let cx = 3. * x1 as f64;
|
||||||
let bx = 3. * (x2 as f64 - x1 as f64) - cx;
|
let bx = 3. * (x2 as f64 - x1 as f64) - cx;
|
||||||
|
|
||||||
|
@ -109,7 +159,7 @@ impl Bezier {
|
||||||
/// Solve the bezier curve for a given `x` and an `epsilon`, that should be
|
/// Solve the bezier curve for a given `x` and an `epsilon`, that should be
|
||||||
/// between zero and one.
|
/// between zero and one.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn solve(&self, x: f64, epsilon: f64) -> f64 {
|
fn solve(&self, x: f64, epsilon: f64) -> f64 {
|
||||||
self.sample_curve_y(self.solve_curve_x(x, epsilon))
|
self.sample_curve_y(self.solve_curve_x(x, epsilon))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl ComputedTimingFunction {
|
||||||
pub fn calculate_output(&self, progress: f64, before_flag: BeforeFlag, epsilon: f64) -> f64 {
|
pub fn calculate_output(&self, progress: f64, before_flag: BeforeFlag, epsilon: f64) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
TimingFunction::CubicBezier { x1, y1, x2, y2 } => {
|
TimingFunction::CubicBezier { x1, y1, x2, y2 } => {
|
||||||
Bezier::new(*x1, *y1, *x2, *y2).solve(progress, epsilon)
|
Bezier::calculate_bezier_output(progress, epsilon, *x1, *y1, *x2, *y2)
|
||||||
},
|
},
|
||||||
TimingFunction::Steps(steps, pos) => {
|
TimingFunction::Steps(steps, pos) => {
|
||||||
Self::calculate_step_output(*steps, *pos, progress, before_flag)
|
Self::calculate_step_output(*steps, *pos, progress, before_flag)
|
||||||
|
@ -102,15 +102,20 @@ impl ComputedTimingFunction {
|
||||||
.at(progress as f32)
|
.at(progress as f32)
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
TimingFunction::Keyword(keyword) => {
|
TimingFunction::Keyword(keyword) => match keyword {
|
||||||
let bezier = match keyword {
|
|
||||||
TimingKeyword::Linear => return progress,
|
TimingKeyword::Linear => return progress,
|
||||||
TimingKeyword::Ease => Bezier::new(0.25, 0.1, 0.25, 1.),
|
TimingKeyword::Ease => {
|
||||||
TimingKeyword::EaseIn => Bezier::new(0.42, 0., 1., 1.),
|
Bezier::calculate_bezier_output(progress, epsilon, 0.25, 0.1, 0.25, 1.)
|
||||||
TimingKeyword::EaseOut => Bezier::new(0., 0., 0.58, 1.),
|
},
|
||||||
TimingKeyword::EaseInOut => Bezier::new(0.42, 0., 0.58, 1.),
|
TimingKeyword::EaseIn => {
|
||||||
};
|
Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 1., 1.)
|
||||||
bezier.solve(progress, epsilon)
|
},
|
||||||
|
TimingKeyword::EaseOut => {
|
||||||
|
Bezier::calculate_bezier_output(progress, epsilon, 0., 0., 0.58, 1.)
|
||||||
|
},
|
||||||
|
TimingKeyword::EaseInOut => {
|
||||||
|
Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 0.58, 1.)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue