use num_traits::Float;
use std::borrow::Borrow;
use strum_macros::EnumString;
use crate::geometry::Size;
pub mod algorithm;
mod tree;
pub use algorithm::Algorithm;
pub use tree::{Layout, LayoutNode, LayoutTree, MeasureFunc};
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct LayoutAnchor<T>
where
T: Float,
{
x: T,
y: T,
}
impl<T> LayoutAnchor<T>
where
T: Float,
{
pub fn new(x: T, y: T) -> LayoutAnchor<T> {
LayoutAnchor { x, y }
}
pub fn distance(self, other: Self) -> LayoutDistance<T> {
LayoutDistance {
dx: other.borrow().x - self.x,
dy: other.borrow().y - self.y,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumString)]
pub enum LayoutDirection {
#[strum(serialize = "ltr")]
LTR,
#[strum(serialize = "rtl")]
RTL,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum LayoutAxisX<T> {
DirectionDependent {
leading: T,
trailing: T,
},
DirectionIndependent {
left: T,
right: T,
},
}
impl<T> LayoutAxisX<T> {
pub fn dependent(leading: T, trailing: T) -> LayoutAxisX<T> {
LayoutAxisX::DirectionDependent { leading, trailing }
}
pub fn independent(left: T, right: T) -> LayoutAxisX<T> {
LayoutAxisX::DirectionIndependent { left, right }
}
pub fn left(&self, direction: LayoutDirection) -> &T {
match self {
LayoutAxisX::DirectionDependent { leading, trailing } => match direction {
LayoutDirection::LTR => leading,
LayoutDirection::RTL => trailing,
},
LayoutAxisX::DirectionIndependent { left, .. } => left,
}
}
pub fn right(&self, direction: LayoutDirection) -> &T {
match self {
LayoutAxisX::DirectionDependent { leading, trailing } => match direction {
LayoutDirection::LTR => trailing,
LayoutDirection::RTL => leading,
},
LayoutAxisX::DirectionIndependent { right, .. } => right,
}
}
}
impl<T> Default for LayoutAxisX<T>
where
T: Default,
{
fn default() -> Self {
LayoutAxisX::DirectionIndependent {
left: T::default(),
right: T::default(),
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct LayoutAxisY<T> {
pub top: T,
pub bottom: T,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct LayoutGuide<T>
where
T: Float,
{
origin: LayoutAnchor<T>,
size: Size<T>,
direction: LayoutDirection,
}
impl<T> LayoutGuide<T>
where
T: Float,
{
pub fn new(
origin: LayoutAnchor<T>,
size: Size<T>,
direction: LayoutDirection,
) -> LayoutGuide<T> {
LayoutGuide {
origin,
size,
direction,
}
}
pub fn top_left(&self) -> LayoutAnchor<T> {
self.origin
}
pub fn top_right(&self) -> LayoutAnchor<T> {
let mut anchor = self.origin;
anchor.x = anchor.x + self.size.width;
anchor
}
pub fn top_leading(&self) -> LayoutAnchor<T> {
match self.direction {
LayoutDirection::LTR => self.top_left(),
LayoutDirection::RTL => self.top_right(),
}
}
pub fn top_trailing(&self) -> LayoutAnchor<T> {
match self.direction {
LayoutDirection::LTR => self.top_right(),
LayoutDirection::RTL => self.top_left(),
}
}
pub fn bottom_left(&self) -> LayoutAnchor<T> {
let mut anchor = self.origin;
anchor.y = anchor.y + self.size.height;
anchor
}
pub fn bottom_right(&self) -> LayoutAnchor<T> {
let mut anchor = self.origin;
anchor.x = anchor.x + self.size.width;
anchor.y = anchor.y + self.size.height;
anchor
}
pub fn bottom_leading(&self) -> LayoutAnchor<T> {
match self.direction {
LayoutDirection::LTR => self.bottom_left(),
LayoutDirection::RTL => self.bottom_right(),
}
}
pub fn bottom_trailing(&self) -> LayoutAnchor<T> {
match self.direction {
LayoutDirection::LTR => self.bottom_right(),
LayoutDirection::RTL => self.bottom_left(),
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct LayoutDistance<T> {
dx: T,
dy: T,
}
impl<T> LayoutDistance<T>
where
T: Float,
{
pub fn l1_norm(&self) -> T
where
T: Float,
{
self.dx.abs() + self.dy.abs()
}
pub fn l2_norm(&self) -> T {
self.dx * self.dx + self.dy * self.dy
}
}
#[cfg(test)]
mod tests {
use super::LayoutAnchor;
#[test]
fn test_l1_norm() {
let a = LayoutAnchor::new(4.0, -42.0);
let b = LayoutAnchor::new(-2.0, -8.0);
assert_eq!(a.distance(b).l1_norm(), 40.0);
assert_eq!(b.distance(a).l1_norm(), 40.0);
}
#[test]
fn test_l2_norm() {
let a = LayoutAnchor::new(4.0, -42.0);
let b = LayoutAnchor::new(-2.0, -8.0);
assert_eq!(a.distance(b).l2_norm(), 1192.0);
assert_eq!(b.distance(a).l2_norm(), 1192.0);
}
}