Functional Weave
Code in Rust

charts.layout@1.0.1

impl/rust/avoid_collisions.rs

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

/// Which labels to show so none overlap: greedily, highest priority first
/// (then input order), keeping a label only if it clears every kept label by
/// at least padding. Touching is not overlapping. Result in input order.
///
/// # Panics
/// Panics on a negative padding or a box with negative size.
pub fn avoid_collisions(boxes: &[LabelBox], padding: f64) -> Vec<bool> {
    if !(padding >= 0.0) {
        panic!("padding must not be negative, received {}", padding);
    }
    for b in boxes {
        if !(b.width >= 0.0 && b.height >= 0.0) {
            panic!("label boxes must not have negative size");
        }
    }
    let mut order: Vec<usize> = (0..boxes.len()).collect();
    order.sort_by(|&a, &b| boxes[b].priority.cmp(&boxes[a].priority).then(a.cmp(&b)));
    let mut shown = vec![false; boxes.len()];
    let mut kept: Vec<&LabelBox> = Vec::new();
    for i in order {
        let a = &boxes[i];
        let clash = kept.iter().any(|k| {
            a.x < k.x + k.width + padding
                && k.x < a.x + a.width + padding
                && a.y < k.y + k.height + padding
                && k.y < a.y + a.height + padding
        });
        if !clash {
            kept.push(a);
            shown[i] = true;
        }
    }
    shown
}

pub fn fune_vector(args: &[Value]) -> Value {
    let boxes: Vec<LabelBox> = args[0]
        .as_arr()
        .iter()
        .map(|b| LabelBox {
            x: b.get("x").as_f64(),
            y: b.get("y").as_f64(),
            width: b.get("width").as_f64(),
            height: b.get("height").as_f64(),
            priority: b.get("priority").as_i64(),
        })
        .collect();
    Value::Arr(avoid_collisions(&boxes, args[1].as_f64()).into_iter().map(Value::Bool).collect())
}