use super::funejson::Value; use super::charts_scale_linear_scale::{floats, interpolate, pair}; use super::dates_days_between::days_between; use super::math_round_float::round_float; /// Where an ISO date lands on the range, linearly by calendar days, counted /// with dates.days-between so no time zone can move a point by a day. /// /// # Panics /// Panics if the domain or range does not have two values, a date is not a /// real ISO date, or the domain's ends are the same day. pub fn time_scale(domain: &[String], range: &[f64], value: &str, clamp: bool) -> f64 { if domain.len() != 2 { panic!("domain must have exactly 2 values, [from, to]; got {}", domain.len()); } let (r0, r1) = pair(range, "range"); let span = days_between(&domain[0], &domain[1]); if span == 0 { panic!("domain has no width: both ends are {}", domain[0]); } let mut t = days_between(&domain[0], value) as f64 / span as f64; if clamp { t = t.max(0.0).min(1.0); } round_float(interpolate(r0, r1, t), 6) } pub fn fune_vector(args: &[Value]) -> Value { let domain: Vec = args[0].as_arr().iter().map(|v| v.as_str().to_string()).collect(); Value::Float(time_scale(&domain, &floats(&args[1]), args[2].as_str(), args[3].as_bool())) }