use super::funejson::Value; use super::math_round_float::round_float; 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 { 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 = 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(), ) }