use palette::Oklab;
pub fn sample_quadratic_bezier_oklab_curve(control: Oklab, l_values: &[f32]) -> Vec<Oklab> {
let start = if control.l <= 0.5 {
let mut point = control;
point.l = 0.0;
point
} else {
Oklab {
l: 0.0,
a: 0.0,
b: 0.0,
}
};
let end = if control.l >= 0.5 {
let mut point = control;
point.l = 1.0;
point
} else {
Oklab {
l: 1.0,
a: 0.0,
b: 0.0,
}
};
let mut l_points = Vec::with_capacity(l_values.len());
for target_l in l_values {
let a = start.l - 2.0 * control.l + end.l;
let b = -2.0 * start.l + 2.0 * control.l;
let c = start.l - target_l;
let discriminant = b * b - 4.0 * a * c;
assert!(discriminant >= 0.0, "no real root");
let sqrt_disc = discriminant.sqrt();
let t1 = (-b + sqrt_disc) / (2.0 * a);
let t2 = (-b - sqrt_disc) / (2.0 * a);
if (0.0..=1.0).contains(&t1) {
l_points.push(t1);
} else if (0.0..=1.0).contains(&t2) {
l_points.push(t2);
} else {
unimplemented!()
}
}
l_points
.into_iter()
.map(|t| {
let l = quadratic_bezier_point(t, start.l, control.l, end.l);
let a = quadratic_bezier_point(t, start.a, control.a, end.a);
let b = quadratic_bezier_point(t, start.b, control.b, end.b);
Oklab { l, a, b }
})
.collect()
}
fn quadratic_bezier_point(t: f32, start: f32, control: f32, end: f32) -> f32 {
assert!((0.0..=1.0).contains(&t));
let one_minus_t = 1.0 - t;
one_minus_t * one_minus_t * start + 2.0 * one_minus_t * t * control + t * t * end
}