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
/// Widen a domain to multiples of a round tick step (1, 2 or 5 times a power
/// of ten) chosen for about `count` ticks. Widening can change the best step,
/// so it repeats until the step settles, as d3's nice() does.
///
/// # Panics
/// Panics if the domain does not have exactly two values or `count` is less
/// than 1.
pub fn nice_domain(domain: &[f64], count: i64) -> NiceDomain {
if domain.len() != 2 {
panic!("domain must have exactly 2 values, [from, to]; got {}", domain.len());
}
if count < 1 {
panic!("count must be a whole number of at least 1, got {}", count);
}
let reversed = domain[1] < domain[0];
let (mut lo, mut hi) = if reversed { (domain[1], domain[0]) } else { (domain[0], domain[1]) };
if lo == hi {
return NiceDomain { domain: vec![domain[0], domain[1]], step: 0.0 };
}
let mut step = 0.0;
for _ in 0..10 {
let next = tick_step(lo, hi, count as f64);
if next == step {
break;
}
step = next;
if step >= 1.0 {
lo = (lo / step).floor() * step;
hi = (hi / step).ceil() * step;
} else {
// A fractional step divides badly (0.3 / 0.1 is 2.9999999999999996),
// so multiply by its whole-number inverse instead.
let inverse = (1.0 / step + 0.5).floor();
lo = (lo * inverse).floor() / inverse;
hi = (hi * inverse).ceil() / inverse;
}
}
let ends = if reversed { vec![round6(hi), round6(lo)] } else { vec![round6(lo), round6(hi)] };
NiceDomain { domain: ends, step: round6(step) }
}
fn tick_step(lo: f64, hi: f64, count: f64) -> f64 {
let raw = (hi - lo) / count;
// Powers of ten by repeated multiplication rather than log10, whose last
// digit is not guaranteed to agree between languages.
let mut power = 1.0;
while power * 10.0 <= raw {
power *= 10.0;
}
while power > raw {
power /= 10.0;
}
let error = raw / power;
let factor = if error >= 50f64.sqrt() {
10.0
} else if error >= 10f64.sqrt() {
5.0
} else if error >= 2f64.sqrt() {
2.0
} else {
1.0
};
factor * power
}
fn round6(x: f64) -> f64 {
(x * 1e6 + 0.5).floor() / 1e6
}
pub fn nice_domain_to_value(nice: &NiceDomain) -> Value {
Value::obj(vec![
("domain", Value::Arr(nice.domain.iter().map(|x| Value::Float(*x)).collect())),
("step", Value::Float(nice.step)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
let domain: Vec<f64> = args[0].as_arr().iter().map(|v| v.as_f64()).collect();
nice_domain_to_value(&nice_domain(&domain, args[1].as_i64()))
}