Functional Weave
Code in TypeScript

charts.downsample@1.0.0

impl/rust.rs

2,213 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one

/// 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<Sample> {
    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<Sample> = args[0].as_arr().iter().map(sample_from_value).collect();
    Value::Arr(downsample(&points, args[1].as_i64()).iter().map(sample_to_value).collect())
}