/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Parametric Bézier curves. //! //! This is based on `WebCore/platform/graphics/UnitBezier.h` in WebKit.
#![deny(missing_docs)]
usecrate::values::CSSFloat;
const NEWTON_METHOD_ITERATIONS: u8 = 8;
/// A unit cubic Bézier curve, used for timing functions in CSS transitions and animations. pubstruct Bezier {
ax: f64,
bx: f64,
cx: f64,
ay: f64,
by: f64,
cy: f64,
}
impl Bezier { /// 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. /// 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 /// starts at 0% and ends at 100%. pubfn 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 { return0.0;
} if progress == 1.0 { return1.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. return0.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 { return1.0 + (progress - 1.0) * (y2 as f64 - 1.0) / (x2 as f64 - 1.0);
} if y2 == 1.0 && x1 < 1.0 { return1.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. return1.0;
}
#[inline] fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 { // Fast path: Use Newton's method. letmut t = x; for _ in0..NEWTON_METHOD_ITERATIONS { let x2 = self.sample_curve_x(t); if x2.approx_eq(x, epsilon) { return t;
} let dx = self.sample_curve_derivative_x(t); if dx.approx_eq(0.0, 1e-6) { break;
}
t -= (x2 - x) / dx;
}
// Slow path: Use bisection. let (mut lo, mut hi, mut t) = (0.0, 1.0, x);
if t < lo { return lo;
} if t > hi { return hi;
}
while lo < hi { let x2 = self.sample_curve_x(t); if x2.approx_eq(x, epsilon) { return t;
} if x > x2 {
lo = t
} else {
hi = t
}
t = (hi - lo) / 2.0 + lo
}
t
}
/// Solve the bezier curve for a given `x` and an `epsilon`, that should be /// between zero and one. #[inline] fn solve(&self, x: f64, epsilon: f64) -> f64 { self.sample_curve_y(self.solve_curve_x(x, epsilon))
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.