use super::funejson::Value; /// Largest-Triangle-Three-Buckets (Steinarsson 2013). Same bucket arithmetic, /// in the same order, as the TypeScript and Python versions and the reference /// implementation. /// /// # Panics /// Panics if `threshold` is less than 3. pub fn downsample(points: &[Sample], threshold: i64) -> Vec { if threshold < 3 { panic!("threshold must be a whole number of at least 3, received {}", threshold); } let n = points.len(); if threshold as usize >= n { return points.to_vec(); } let every = (n - 2) as f64 / (threshold - 2) as f64; let mut sampled = vec![points[0].clone()]; let mut a = 0usize; for i in 0..(threshold - 2) as usize { let avg_start = ((i + 1) as f64 * every).floor() as usize + 1; let avg_end = (((i + 2) as f64 * every).floor() as usize + 1).min(n); let mut avg_x = 0.0; let mut avg_y = 0.0; for p in &points[avg_start..avg_end] { avg_x += p.x; avg_y += p.y; } avg_x /= (avg_end - avg_start) as f64; avg_y /= (avg_end - avg_start) as f64; let from = (i as f64 * every).floor() as usize + 1; let to = ((i + 1) as f64 * every).floor() as usize + 1; let ax = points[a].x; let ay = points[a].y; let mut max_area = -1.0; let mut next = from; for j in from..to { let area = ((ax - avg_x) * (points[j].y - ay) - (ax - points[j].x) * (avg_y - ay)).abs(); if area > max_area { max_area = area; next = j; } } sampled.push(points[next].clone()); a = next; } sampled.push(points[n - 1].clone()); sampled } pub fn sample_from_value(v: &Value) -> Sample { Sample { x: v.get("x").as_f64(), y: v.get("y").as_f64() } } pub fn sample_to_value(s: &Sample) -> Value { Value::obj(vec![("x", Value::Float(s.x)), ("y", Value::Float(s.y))]) } pub fn fune_vector(args: &[Value]) -> Value { let points: Vec = args[0].as_arr().iter().map(sample_from_value).collect(); Value::Arr(downsample(&points, args[1].as_i64()).iter().map(sample_to_value).collect()) }