use crate::graph::grid::{Point, Segment};
pub(super) fn calc_label_position(segments: &[Segment]) -> Option<Point> {
let first = segments.first()?;
let total_length: usize = segments.iter().map(Segment::length).sum();
if total_length == 0 {
return Some(first.start_point());
}
let target = total_length / 2;
let mut accumulated = 0usize;
for seg in segments {
let seg_len = seg.length();
if accumulated + seg_len >= target {
return Some(seg.point_at_offset(target - accumulated));
}
accumulated += seg_len;
}
segments.last().map(Segment::end_point)
}
#[derive(Debug)]
pub(super) struct LabelBlock<'a> {
pub(super) lines: Vec<&'a str>,
pub(super) width: usize,
pub(super) height: usize,
}
pub(super) fn label_block(label: &str) -> LabelBlock<'_> {
let lines: Vec<&str> = label.split('\n').collect();
let width = lines
.iter()
.map(|line| line.chars().count())
.max()
.unwrap_or(0);
let height = lines.len().max(1);
LabelBlock {
lines,
width,
height,
}
}
pub(super) fn effective_edge_label<'a>(
edge: &'a crate::graph::Edge,
) -> Option<std::borrow::Cow<'a, str>> {
if let Some(lines) = edge.wrapped_label_lines.as_deref() {
return Some(std::borrow::Cow::Owned(lines.join("\n")));
}
edge.label.as_deref().map(std::borrow::Cow::Borrowed)
}
pub(super) fn label_top_for_center(center_y: usize, height: usize) -> usize {
center_y.saturating_sub(height / 2)
}
pub(super) fn clamp_label_x(
label_x: usize,
label_width: usize,
containment: Option<(usize, usize)>,
) -> usize {
let Some((c_min, c_max)) = containment else {
return label_x;
};
let avail = c_max.saturating_sub(c_min);
if label_width <= avail {
label_x.max(c_min).min(c_max.saturating_sub(label_width))
} else {
c_min + avail.saturating_sub(label_width) / 2
}
}