use super::funejson::Value; use super::math_round_float::round_float; /// 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], offset: &str) -> Vec> { 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> = 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(), ) }