Functional Weave
Code in Rust

charts.shape@1.0.0

impl/rust/pie_angles.rs

1,995 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::math_round_float::round_float;  ← from math.round-float ^1.0.0 · built alongside by fune

const TAU: f64 = 6.283185307179586;

/// Start and end angles of pie slices in input order: d3.pie with
/// sort(null), then half the pad trimmed from each side, so the angles go
/// straight into arc_path and neighbouring slices show a gap of pad_angle.
///
/// # Panics
/// Panics on a negative value or a negative pad angle.
pub fn pie_angles(values: &[f64], start_angle: f64, end_angle: f64, pad_angle: f64) -> Vec<PieSlice> {
    if !(pad_angle >= 0.0) {
        panic!("padAngle must not be negative, received {}", pad_angle);
    }
    let n = values.len() as f64;
    let mut sum = 0.0;
    for v in values {
        if !(*v >= 0.0) {
            panic!("pie values must be zero or more, received {}", v);
        }
        sum += v;
    }
    let mut a0 = start_angle;
    let da = TAU.min((-TAU).max(end_angle - a0));
    let p = if !values.is_empty() { (da.abs() / n).min(pad_angle) } else { 0.0 };
    let pa = p * (if da < 0.0 { -1.0 } else { 1.0 });
    let k = if sum > 0.0 { (da - n * pa) / sum } else { 0.0 };
    let mut out = Vec::new();
    for v in values {
        let a1 = a0 + (if *v > 0.0 { v * k } else { 0.0 }) + pa;
        out.push(PieSlice {
            value: *v,
            start_angle: round_float(a0 + pa / 2.0, 6),
            end_angle: round_float(a1 - pa / 2.0, 6),
        });
        a0 = a1;
    }
    out
}

pub fn fune_vector(args: &[Value]) -> Value {
    let values: Vec<f64> = args[0].as_arr().iter().map(|v| v.as_f64()).collect();
    let slices = pie_angles(&values, args[1].as_f64(), args[2].as_f64(), args[3].as_f64());
    Value::Arr(
        slices
            .iter()
            .map(|s| {
                Value::obj(vec![
                    ("value", Value::Float(s.value)),
                    ("startAngle", Value::Float(s.start_angle)),
                    ("endAngle", Value::Float(s.end_angle)),
                ])
            })
            .collect(),
    )
}