use abstracttui::base::{Point, Rect, Size};
use crate::desc::Direction;
pub(crate) fn flow_extent(dir: Direction, size: Size) -> f64 {
if dir.is_vertical() {
f64::from(size.h)
} else {
f64::from(size.w)
}
}
pub(crate) fn cross_extent(dir: Direction, size: Size) -> f64 {
if dir.is_vertical() {
f64::from(size.w)
} else {
f64::from(size.h)
}
}
pub(crate) fn map_rect(dir: Direction, cross: f64, flow: f64, size: Size) -> Rect {
let fe = flow_extent(dir, size);
let flow = if dir.is_reversed() {
-(flow + fe)
} else {
flow
};
let (x, y) = match dir {
Direction::TopDown | Direction::BottomTop => (cross, flow),
Direction::LeftRight | Direction::RightLeft => (flow, cross),
};
Rect::new(round(x), round(y), size.w, size.h)
}
pub(crate) fn map_point(dir: Direction, cross: f64, flow: f64) -> Point {
let flow = if dir.is_reversed() {
-(flow + 1.0)
} else {
flow
};
let (x, y) = match dir {
Direction::TopDown | Direction::BottomTop => (cross, flow),
Direction::LeftRight | Direction::RightLeft => (flow, cross),
};
Point::new(round(x), round(y))
}
pub(crate) fn round(v: f64) -> i32 {
v.round() as i32
}
pub(crate) fn clip_border(rect: Rect, toward: (f64, f64)) -> Point {
let cx = f64::from(rect.x) + f64::from(rect.w) / 2.0;
let cy = f64::from(rect.y) + f64::from(rect.h) / 2.0;
let dx = toward.0 - cx;
let dy = toward.1 - cy;
let hw = f64::from(rect.w) / 2.0;
let hh = f64::from(rect.h) / 2.0;
let tx = if dx.abs() > 1e-9 {
hw / dx.abs()
} else {
f64::INFINITY
};
let ty = if dy.abs() > 1e-9 {
hh / dy.abs()
} else {
f64::INFINITY
};
let t = tx.min(ty);
if !t.is_finite() {
return Point::new(round(cx), round(cy));
}
Point::new(round(cx + dx * t), round(cy + dy * t))
}
pub(crate) fn self_loop(rect: Rect) -> Vec<Point> {
let cy = rect.y + rect.h / 2;
let x = rect.right();
vec![
Point::new(x, cy - 1),
Point::new(x + 2, cy - 1),
Point::new(x + 2, cy + 1),
Point::new(x, cy + 1),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn directions_are_transposes_and_mirrors() {
let size = Size::new(5, 5);
let td = map_rect(Direction::TopDown, 3.0, 7.0, size);
let lr = map_rect(Direction::LeftRight, 3.0, 7.0, size);
assert_eq!((td.x, td.y), (3, 7));
assert_eq!((lr.x, lr.y), (7, 3), "LR transposes TD");
let bt = map_rect(Direction::BottomTop, 3.0, 7.0, size);
assert_eq!((bt.x, bt.y), (3, -12), "BT mirrors flow by negation");
let rl = map_rect(Direction::RightLeft, 3.0, 7.0, size);
assert_eq!((rl.x, rl.y), (-12, 3));
}
#[test]
fn border_clip_hits_the_facing_side() {
let r = Rect::new(0, 0, 10, 4);
assert_eq!(clip_border(r, (100.0, 2.0)), Point::new(10, 2));
assert_eq!(clip_border(r, (5.0, 50.0)), Point::new(5, 4));
assert_eq!(clip_border(r, (5.0, 2.0)), Point::new(5, 2));
}
}