// Ported from georust/geo
// https://github.com/georust/geo/blob/main/geo/src/algorithm/chaikin_smoothing.rs
///|
/// Performs one iteration of Chaikin smoothing: each segment `(p_i, p_{i+1})`
/// becomes two new coords at 1/4 and 3/4 along the segment. Repeated for
/// `n_iterations` rounds. The first and last coords are preserved for open
/// linestrings; closed linestrings smooth uniformly.
pub fn chaikin_smoothing(
ls : @type.LineString,
n_iterations : Int,
) -> @type.LineString {
(0).until(n_iterations).fold(init=ls, fn(acc, _) { chaikin_step(acc) })
}
///|
fn chaikin_step(ls : @type.LineString) -> @type.LineString {
let coords = ls.coords()
let n = coords.length()
if n < 2 {
return ls
}
let is_closed = coords[0] == coords[n - 1]
// Each of the (n-1) segments contributes 2 cut-points (q1 at 1/4, q2 at
// 3/4). Build them all in one Array::makei pass: index j ∈ [0, 2(n-1))
// maps to segment i = j / 2 and side k = j % 2.
let cuts = Array::makei(2 * (n - 1), fn(j) {
let i = j / 2
let p = coords[i]
let q = coords[i + 1]
if j % 2 == 0 {
@type.Coord(0.75 * p.x() + 0.25 * q.x(), 0.75 * p.y() + 0.25 * q.y())
} else {
@type.Coord(0.25 * p.x() + 0.75 * q.x(), 0.25 * p.y() + 0.75 * q.y())
}
})
let result = if is_closed {
// Closed ring: smooth uniformly + repeat the first cut as the closing
// coord (so first == last).
[..cuts, cuts[0]]
} else {
// Open: prepend original first coord, append original last coord.
[coords[0], ..cuts, coords[n - 1]]
}
@type.LineString::LineString(result)
}