Functional Weave
Code in Python

charts.stack@1.0.0

impl/rust.rs

3,212 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
use super::math_round_float::round_float;  ← from math.round-float ^1.0.0 · built alongside by fune

/// Stack series position by position, as d3.stack does with the series in
/// the given order: stackOffsetNone (zero), stackOffsetExpand and
/// stackOffsetDiverging. Running totals stay unrounded; only the returned
/// edges are rounded.
///
/// # Panics
/// Panics on an unknown offset, series of different lengths, a non-finite
/// value, or a negative value with expand.
pub fn stack(series: &[Vec<f64>], offset: &str) -> Vec<Vec<StackedValue>> {
    if offset != "zero" && offset != "expand" && offset != "diverging" {
        panic!("stack offset must be zero, expand or diverging, received {}", offset);
    }
    let n = series.len();
    if n == 0 {
        return Vec::new();
    }
    let m = series[0].len();
    for s in series {
        if s.len() != m {
            panic!("every series must have the same number of values");
        }
        for v in s {
            if !v.is_finite() {
                panic!("stack values must be finite numbers, received {}", v);
            }
            if offset == "expand" && *v < 0.0 {
                panic!("expand needs values of zero or more, received {}", v);
            }
        }
    }
    let mut lower = vec![vec![0.0; m]; n];
    let mut upper = vec![vec![0.0; m]; n];
    for j in 0..m {
        if offset == "diverging" {
            let mut up = 0.0;
            let mut down = 0.0;
            for i in 0..n {
                let v = series[i][j];
                if v > 0.0 {
                    lower[i][j] = up;
                    up += v;
                    upper[i][j] = up;
                } else if v < 0.0 {
                    upper[i][j] = down;
                    down += v;
                    lower[i][j] = down;
                } else {
                    lower[i][j] = 0.0;
                    upper[i][j] = 0.0;
                }
            }
        } else {
            let mut total = 0.0;
            if offset == "expand" {
                for i in 0..n {
                    total += series[i][j];
                }
            }
            let mut top = 0.0;
            for i in 0..n {
                let mut v = series[i][j];
                if offset == "expand" {
                    v = if total > 0.0 { v / total } else { 0.0 };
                }
                lower[i][j] = top;
                top += v;
                upper[i][j] = top;
            }
        }
    }
    (0..n)
        .map(|i| {
            (0..m)
                .map(|j| StackedValue { y0: round_float(lower[i][j], 6), y1: round_float(upper[i][j], 6) })
                .collect()
        })
        .collect()
}

pub fn stacked_value_to_value(s: &StackedValue) -> Value {
    Value::obj(vec![("y0", Value::Float(s.y0)), ("y1", Value::Float(s.y1))])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let series: Vec<Vec<f64>> = args[0]
        .as_arr()
        .iter()
        .map(|s| s.as_arr().iter().map(|v| v.as_f64()).collect())
        .collect();
    Value::Arr(
        stack(&series, args[1].as_str())
            .iter()
            .map(|row| Value::Arr(row.iter().map(stacked_value_to_value).collect()))
            .collect(),
    )
}