use super::funejson::Value; /// The tick step for about `count` ticks: 1, 2 or 5 times a power of ten, as /// d3-array's tickIncrement chooses it, found without log10 and returned as one /// correctly rounded division so 0.2 is exactly the double 0.2. /// /// # Panics /// Panics if start or stop is not finite or `count` is less than 1. pub fn tick_step(start: f64, stop: f64, count: i64) -> f64 { let (mul, div) = tick_spec(start, stop, count); mul / div } // Exported for nice_ticks, which needs the step as a multiplier or a divisor. /// The step as (multiplier, divisor), one of which is 1; (0, 1) when start /// equals stop. pub fn tick_spec(start: f64, stop: f64, count: i64) -> (f64, f64) { if !start.is_finite() || !stop.is_finite() { panic!("start and stop must be finite numbers; got {} and {}", start, stop); } if count < 1 { panic!("count must be a whole number of at least 1, got {}", count); } let raw = (stop - start).abs() / count as f64; if raw == 0.0 { return (0.0, 1.0); } if raw >= 1.0 { let mut power = 1.0; while power * 10.0 <= raw { power *= 10.0; } return (nice_factor(raw / power) * power, 1.0); } let mut inverse = 1.0; while raw * inverse < 1.0 { inverse *= 10.0; } let factor = nice_factor(raw * inverse); if factor == inverse { (1.0, 1.0) } else { (1.0, inverse / factor) } } fn nice_factor(error: f64) -> f64 { if error >= 50f64.sqrt() { 10.0 } else if error >= 10f64.sqrt() { 5.0 } else if error >= 2f64.sqrt() { 2.0 } else { 1.0 } } pub fn fune_vector(args: &[Value]) -> Value { // Refuse what the typed signature cannot hold, with the wording TypeScript // and Python use, rather than let the conversion below quietly change it. if matches!(args[2], Value::Float(f) if f.fract() != 0.0) { panic!("count must be a whole number of at least 1"); } Value::Float(tick_step(args[0].as_f64(), args[1].as_f64(), args[2].as_i64())) }