use crate::math::Point;
pub type P = Point<f64>;
fn subdivide(control_points: &[P], l: &mut [P], r: &mut [P], midpoints_buf: &mut [P]) {
let count = control_points.len();
midpoints_buf.copy_from_slice(control_points);
for i in 0..count {
l[i] = midpoints_buf[0];
r[count - i - 1] = midpoints_buf[count - i - 1];
for j in 0..count - i - 1 {
midpoints_buf[j] = (midpoints_buf[j] + midpoints_buf[j + 1]) / P::new(2.0, 2.0);
}
}
}
fn approximate(
control_points: &[P],
output: &mut Vec<P>,
l_buf: &mut [P],
r_buf: &mut [P],
midpoints_buf: &mut [P],
) {
let count = control_points.len();
subdivide(control_points, l_buf, r_buf, midpoints_buf);
l_buf[count..(count * 2) - 1].clone_from_slice(&r_buf[1..count]);
output.push(control_points[0]);
for i in 1..count - 1 {
let index = 2 * i;
let p = (l_buf[index] * P::new(2.0, 2.0) + l_buf[index - 1] + l_buf[index + 1])
* P::new(0.25, 0.25);
output.push(p);
}
}
fn is_flat_enough(control_points: &[P], tolerance_sq: f64) -> bool {
for i in 1..control_points.len() - 1 {
if (control_points[i - 1] - control_points[i] * P::new(2.0, 2.0) + control_points[i + 1])
.length_squared()
> tolerance_sq
{
return false;
}
}
true
}
pub fn create_singlebezier(output: &mut Vec<P>, control_points: &[P]) {
let count = control_points.len();
const TOLERANCE: f64 = 0.25;
const TOLERANCE_SQ: f64 = TOLERANCE * TOLERANCE;
if count == 0 {
return;
}
let mut to_flatten: Vec<Vec<P>> = Vec::new();
let mut free_buffers: Vec<Vec<P>> = Vec::new();
let last_control_point = control_points[count - 1];
to_flatten.push(control_points.to_vec());
let mut left_child = vec![P::new(0.0, 0.0); count * 2 - 1];
let mut l_buf = vec![P::new(0.0, 0.0); count * 2 - 1];
let mut r_buf = vec![P::new(0.0, 0.0); count];
let mut midpoints_buf = vec![P::new(0.0, 0.0); count];
while !to_flatten.is_empty() {
let mut parent = to_flatten.pop().unwrap();
if is_flat_enough(&parent, TOLERANCE_SQ) {
approximate(
&parent,
output,
&mut l_buf[..count * 2 - 1],
&mut r_buf[..count],
&mut midpoints_buf[..count],
);
free_buffers.push(parent);
continue;
}
let mut right_child = free_buffers
.pop()
.unwrap_or_else(|| vec![P::new(0.0, 0.0); count]);
subdivide(
&parent,
&mut left_child,
&mut right_child,
&mut midpoints_buf[..count],
);
parent[..count].clone_from_slice(&left_child[..count]);
to_flatten.push(right_child);
to_flatten.push(parent);
}
output.push(last_control_point);
}