// Adaptive curve subdivision for deterministic polyline consumers.
///|
fn cubic_is_flat(curve : CubicBezier, tolerance_squared : Double) -> Bool {
let first = distance_squared_to_segment(curve.p1, curve.p0, curve.p3)
let second = distance_squared_to_segment(curve.p2, curve.p0, curve.p3)
first <= tolerance_squared && second <= tolerance_squared
}
///|
fn append_cubic_points(
curve : CubicBezier,
tolerance_squared : Double,
depth : Int,
max_depth : Int,
output : Array[Vec2],
) -> Unit {
if depth >= max_depth || cubic_is_flat(curve, tolerance_squared) {
output.push(curve.p3)
} else {
let (left, right) = curve.split(0.5)
append_cubic_points(left, tolerance_squared, depth + 1, max_depth, output)
append_cubic_points(right, tolerance_squared, depth + 1, max_depth, output)
}
}
///|
pub fn flatten_cubic(
curve : CubicBezier,
tolerance : Double,
max_depth : Int,
) -> Array[Vec2] {
let output = [curve.p0]
let effective_tolerance = if tolerance <= 0.0 { 0.000001 } else { tolerance }
let effective_depth = if max_depth < 1 { 1 } else { max_depth }
append_cubic_points(
curve,
effective_tolerance * effective_tolerance,
0,
effective_depth,
output,
)
output
}
///|
pub fn flatten_quadratic(
curve : QuadraticBezier,
tolerance : Double,
max_depth : Int,
) -> Array[Vec2] {
flatten_cubic(curve.to_cubic(), tolerance, max_depth)
}