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