use super::funejson::Value; use super::charts_extent::extent; use super::charts_scale_linear_scale::linear_scale; use super::charts_shape_line_path::line_path; use super::charts_shape_types::Point; use super::math_round_float::round_float; fn check_size(value: f64, what: &str) { if !value.is_finite() || value < 0.0 { panic!("{} must be a finite number of zero or more, received {}", what, value); } } /// A sparkline in a box; see the README for flat and single-value series. /// /// # Panics /// Panics on a negative or non-finite size, or a box too small for its padding. pub fn sparkline(values: &[Option], width: f64, height: f64, padding: f64) -> Sparkline { check_size(width, "width"); check_size(height, "height"); check_size(padding, "padding"); if width - 2.0 * padding <= 0.0 || height - 2.0 * padding <= 0.0 { panic!( "the box is too small for its padding: {} by {} with {} on each side", width, height, padding ); } let range = match extent(values) { None => return Sparkline { path: String::new(), end: None }, Some(r) => r, }; let (lo, hi) = (range[0], range[1]); let n = values.len(); let points: Vec = values .iter() .enumerate() .map(|(i, v)| { let x = if n == 1 { width - padding } else { linear_scale(&[0.0, (n - 1) as f64], &[padding, width - padding], i as f64, false) }; let y = v.map(|v| if lo == hi { height / 2.0 } else { linear_scale(&[lo, hi], &[height - padding, padding], v, false) }); Point { x, y } }) .collect(); let end = points .iter() .filter_map(|p| p.y.map(|y| SparkPoint { x: round_float(p.x, 2), y: round_float(y, 2) })) .last(); Sparkline { path: line_path(&points), end } } pub fn sparkline_to_value(s: &Sparkline) -> Value { Value::obj(vec![ ("path", Value::str(&s.path)), ( "end", match &s.end { None => Value::Null, Some(p) => Value::obj(vec![("x", Value::Float(p.x)), ("y", Value::Float(p.y))]), }, ), ]) } pub fn fune_vector(args: &[Value]) -> Value { let values: Vec> = args[0] .as_arr() .iter() .map(|v| if v.is_null() { None } else { Some(v.as_f64()) }) .collect(); sparkline_to_value(&sparkline(&values, args[1].as_f64(), args[2].as_f64(), args[3].as_f64())) }