Functional Weave
Code in Python

charts.ticks@1.0.0

impl/rust/nice_ticks.rs

2,016 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::charts_ticks_tick_step::tick_spec;  ← tickStep, another function of this group · built into the same file, even by a slim install
use super::math_round_float::round_float;  ← from math.round-float ^1.0.0 · built alongside by fune

/// Ticks at round values between start and stop, as d3.ticks, each one exact
/// operation on a whole number (i * step or i / divisor), so 0.1 steps give 0.3
/// and not 0.30000000000000004.
///
/// # Panics
/// Panics if start or stop is not finite or `count` is less than 1.
pub fn nice_ticks(start: f64, stop: f64, count: i64) -> Vec<f64> {
    let (mut mul, mut div) = tick_spec(start, stop, count);
    if start == stop {
        return vec![start + 0.0];
    }
    let reverse = stop < start;
    let (lo, hi) = if reverse { (stop, start) } else { (start, stop) };
    let (mut i1, mut i2) = indexes(lo, hi, mul, div);
    if i2 < i1 && count == 1 {
        // No multiple of the step for one tick falls inside; d3 retries with two.
        let spec = tick_spec(start, stop, 2);
        mul = spec.0;
        div = spec.1;
        let idx = indexes(lo, hi, mul, div);
        i1 = idx.0;
        i2 = idx.1;
    }
    let mut ticks = Vec::new();
    let mut i = i1;
    while i <= i2 {
        ticks.push((if div > 1.0 { i / div } else { i * mul }) + 0.0);
        i += 1.0;
    }
    if reverse {
        ticks.reverse();
    }
    ticks
}

fn indexes(lo: f64, hi: f64, mul: f64, div: f64) -> (f64, f64) {
    if div > 1.0 {
        let mut i1 = round_float(lo * div, 0);
        let mut i2 = round_float(hi * div, 0);
        if i1 / div < lo {
            i1 += 1.0;
        }
        if i2 / div > hi {
            i2 -= 1.0;
        }
        return (i1, i2);
    }
    let mut i1 = round_float(lo / mul, 0);
    let mut i2 = round_float(hi / mul, 0);
    if i1 * mul < lo {
        i1 += 1.0;
    }
    if i2 * mul > hi {
        i2 -= 1.0;
    }
    (i1, i2)
}

pub fn fune_vector(args: &[Value]) -> Value {
    Value::Arr(
        nice_ticks(args[0].as_f64(), args[1].as_f64(), args[2].as_i64())
            .into_iter()
            .map(Value::Float)
            .collect(),
    )
}