use super::super::{Bounds, fmt};
pub(super) fn is_arch_dir_x(dir: char) -> bool {
matches!(dir, 'L' | 'R')
}
pub(super) fn is_arch_dir_y(dir: char) -> bool {
matches!(dir, 'T' | 'B')
}
pub(super) fn arrow_points(dir: char, arrow_size: f64) -> String {
match dir {
'L' => format!(
"{s},{hs} 0,{s} 0,0",
s = fmt(arrow_size),
hs = fmt(arrow_size / 2.0)
),
'R' => format!(
"0,{hs} {s},0 {s},{s}",
s = fmt(arrow_size),
hs = fmt(arrow_size / 2.0)
),
'T' => format!(
"0,0 {s},0 {hs},{s}",
s = fmt(arrow_size),
hs = fmt(arrow_size / 2.0)
),
'B' => format!(
"{hs},0 {s},{s} 0,{s}",
s = fmt(arrow_size),
hs = fmt(arrow_size / 2.0)
),
_ => arrow_points('R', arrow_size),
}
}
pub(super) fn arrow_shift(dir: char, orig: f64, arrow_size: f64) -> f64 {
match dir {
'L' | 'T' => orig - arrow_size + 2.0,
'R' | 'B' => orig - 2.0,
_ => orig,
}
}
pub(super) fn edge_id(prefix: &str, from: &str, to: &str, counter: usize) -> String {
format!("{prefix}_{from}_{to}_{counter}")
}
pub(super) fn extend_bounds(bounds: &mut Option<Bounds>, other: Bounds) {
let b = bounds.get_or_insert(other.clone());
b.min_x = b.min_x.min(other.min_x);
b.min_y = b.min_y.min(other.min_y);
b.max_x = b.max_x.max(other.max_x);
b.max_y = b.max_y.max(other.max_y);
}
pub(super) fn bounds_from_rect(x: f64, y: f64, w: f64, h: f64) -> Bounds {
Bounds {
min_x: x,
min_y: y,
max_x: x + w,
max_y: y + h,
}
}
#[derive(Clone, Copy)]
pub(super) struct GroupRect<'a> {
pub(super) id: &'a str,
pub(super) x: f64,
pub(super) y: f64,
pub(super) w: f64,
pub(super) h: f64,
pub(super) icon: Option<&'a str>,
pub(super) title: Option<&'a str>,
}
pub(super) struct GroupRectComputer<'a> {
icon_size_px: f64,
services_in_group: &'a rustc_hash::FxHashMap<&'a str, Vec<&'a str>>,
junctions_in_group: &'a rustc_hash::FxHashMap<&'a str, Vec<&'a str>>,
child_groups: &'a rustc_hash::FxHashMap<&'a str, Vec<&'a str>>,
service_bounds: &'a rustc_hash::FxHashMap<&'a str, Bounds>,
junction_bounds: &'a rustc_hash::FxHashMap<&'a str, Bounds>,
group_rects: rustc_hash::FxHashMap<&'a str, Bounds>,
visiting: rustc_hash::FxHashSet<&'a str>,
}
impl<'a> GroupRectComputer<'a> {
pub(super) fn new(
icon_size_px: f64,
services_in_group: &'a rustc_hash::FxHashMap<&'a str, Vec<&'a str>>,
junctions_in_group: &'a rustc_hash::FxHashMap<&'a str, Vec<&'a str>>,
child_groups: &'a rustc_hash::FxHashMap<&'a str, Vec<&'a str>>,
service_bounds: &'a rustc_hash::FxHashMap<&'a str, Bounds>,
junction_bounds: &'a rustc_hash::FxHashMap<&'a str, Bounds>,
) -> Self {
Self {
icon_size_px,
services_in_group,
junctions_in_group,
child_groups,
service_bounds,
junction_bounds,
group_rects: rustc_hash::FxHashMap::default(),
visiting: rustc_hash::FxHashSet::default(),
}
}
pub(super) fn get(&self, group_id: &str) -> Option<&Bounds> {
self.group_rects.get(group_id)
}
pub(super) fn compute(&mut self, group_id: &'a str) -> Option<Bounds> {
if let Some(b) = self.group_rects.get(group_id) {
return Some(b.clone());
}
if self.visiting.contains(group_id) {
return None;
}
self.visiting.insert(group_id);
let mut content: Option<Bounds> = None;
if let Some(svcs) = self.services_in_group.get(group_id) {
for id in svcs {
if let Some(b) = self.service_bounds.get(id) {
let mut tmp = content;
extend_bounds(&mut tmp, b.clone());
content = tmp;
}
}
}
if let Some(junctions) = self.junctions_in_group.get(group_id) {
for id in junctions {
if let Some(b) = self.junction_bounds.get(id) {
let mut tmp = content;
extend_bounds(&mut tmp, b.clone());
content = tmp;
}
}
}
if let Some(children) = self.child_groups.get(group_id) {
let child_group_inset = 1.0;
for child in children {
if let Some(b) = self.compute(child) {
let b = if (b.max_x - b.min_x) > 2.0 * child_group_inset
&& (b.max_y - b.min_y) > 2.0 * child_group_inset
{
Bounds {
min_x: b.min_x + child_group_inset,
min_y: b.min_y + child_group_inset,
max_x: b.max_x - child_group_inset,
max_y: b.max_y - child_group_inset,
}
} else {
b
};
let mut tmp = content;
extend_bounds(&mut tmp, b);
content = tmp;
}
}
}
let _has_child_groups = self
.child_groups
.get(group_id)
.is_some_and(|v| !v.is_empty());
let extra = 2.5;
let pad = self.icon_size_px / 2.0 + extra;
let b = if let Some(content) = content {
Bounds {
min_x: content.min_x - pad,
min_y: content.min_y - pad,
max_x: content.max_x + pad,
max_y: content.max_y + pad,
}
} else {
Bounds {
min_x: 0.0,
min_y: 0.0,
max_x: self.icon_size_px.max(1.0),
max_y: self.icon_size_px.max(1.0),
}
};
self.group_rects.insert(group_id, b.clone());
self.visiting.remove(group_id);
Some(b)
}
}