Functional Weave
Code in Python

charts.scale@1.0.0

impl/rust/linear_scale.rs

1,867 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

/// Where `value` lands on the range, as a straight-line map from the domain.
/// Outside the domain the line carries on, unless `clamp` stops it at the
/// range's ends.
///
/// # Panics
/// Panics if the domain or range does not have exactly two values, or the
/// domain's ends are equal.
pub fn linear_scale(domain: &[f64], range: &[f64], value: f64, clamp: bool) -> f64 {
    let (d0, d1) = pair(domain, "domain");
    let (r0, r1) = pair(range, "range");
    let mut t = normalise(d0, d1, value, "domain");
    if clamp {
        t = t.max(0.0).min(1.0);
    }
    round6(interpolate(r0, r1, t))
}

// The helpers below are shared with invert_linear, which is the same line read
// the other way; they are not part of the group's contract.

pub fn pair(values: &[f64], what: &str) -> (f64, f64) {
    if values.len() != 2 {
        panic!("{} must have exactly 2 values, [from, to]; got {}", what, values.len());
    }
    (values[0], values[1])
}

pub fn normalise(from: f64, to: f64, value: f64, what: &str) -> f64 {
    // Equal ends would divide by zero and answer NaN or infinity, which then
    // draws nothing without saying why.
    if from == to {
        panic!("{} has no width: both ends are {}", what, from);
    }
    (value - from) / (to - from)
}

pub fn interpolate(from: f64, to: f64, t: f64) -> f64 {
    from + t * (to - from)
}

/// Half up to 6 decimal places, the same way in every language.
pub fn round6(x: f64) -> f64 {
    (x * 1e6 + 0.5).floor() / 1e6
}

/// A JSON list of numbers, for the vector adapters.
pub fn floats(value: &Value) -> Vec<f64> {
    value.as_arr().iter().map(|v| v.as_f64()).collect()
}

pub fn fune_vector(args: &[Value]) -> Value {
    Value::Float(linear_scale(
        &floats(&args[0]),
        &floats(&args[1]),
        args[2].as_f64(),
        args[3].as_bool(),
    ))
}