use std::ops::Not; use crate::{ layout::{UiNum, UiScalar, UiVec2, Vec2, vec2}, util::impl_op, }; #[derive(Copy, Clone, Eq, PartialEq)] pub enum Axis { X, Y, } impl Not for Axis { type Output = Self; fn not(self) -> Self::Output { match self { Self::X => Self::Y, Self::Y => Self::X, } } } #[derive(Clone, Copy, Eq, PartialEq)] pub struct Dir { pub axis: Axis, pub sign: Sign, } impl Dir { pub const fn new(axis: Axis, dir: Sign) -> Self { Self { axis, sign: dir } } pub const LEFT: Self = Self::new(Axis::X, Sign::Neg); pub const RIGHT: Self = Self::new(Axis::X, Sign::Pos); pub const UP: Self = Self::new(Axis::Y, Sign::Neg); pub const DOWN: Self = Self::new(Axis::Y, Sign::Pos); } #[derive(Clone, Copy, Eq, PartialEq)] pub enum Sign { Neg, Pos, } impl Vec2 { pub fn axis(&self, axis: Axis) -> f32 { match axis { Axis::X => self.x, Axis::Y => self.y, } } pub fn axis_mut(&mut self, axis: Axis) -> &mut f32 { match axis { Axis::X => &mut self.x, Axis::Y => &mut self.y, } } pub const fn from_axis(axis: Axis, aligned: f32, ortho: f32) -> Self { Self { x: match axis { Axis::X => aligned, Axis::Y => ortho, }, y: match axis { Axis::Y => aligned, Axis::X => ortho, }, } } } #[derive(Clone, Copy, PartialEq, Eq)] pub enum Align { TopLeft, Top, TopRight, Left, Center, Right, BotLeft, Bot, BotRight, } impl Align { pub const fn rel(&self) -> Vec2 { match self { Self::TopLeft => vec2(0.0, 0.0), Self::Top => vec2(0.5, 0.0), Self::TopRight => vec2(1.0, 0.0), Self::Left => vec2(0.0, 0.5), Self::Center => vec2(0.5, 0.5), Self::Right => vec2(1.0, 0.5), Self::BotLeft => vec2(0.0, 1.0), Self::Bot => vec2(0.5, 1.0), Self::BotRight => vec2(1.0, 1.0), } } } #[derive(Default, Clone, Copy, PartialEq)] pub struct Size { pub x: Len, pub y: Len, } #[derive(Clone, Copy, PartialEq)] pub struct Len { pub abs: f32, pub rel: f32, pub rest: f32, } impl From for Len { fn from(value: N) -> Self { Len::abs(value.to_f32()) } } impl From<(Nx, Ny)> for Size { fn from((x, y): (Nx, Ny)) -> Self { Self { x: x.into(), y: y.into(), } } } impl Size { pub const ZERO: Self = Self { x: Len::ZERO, y: Len::ZERO, }; pub fn abs(v: Vec2) -> Self { Self { x: Len::abs(v.x), y: Len::abs(v.y), } } pub fn to_uivec2(self, rel_len: Vec2) -> UiVec2 { UiVec2::from_scalars(self.x.apply_rest(rel_len.x), self.y.apply_rest(rel_len.y)) } pub fn from_axis(axis: Axis, aligned: Len, ortho: Len) -> Self { match axis { Axis::X => Self { x: aligned, y: ortho, }, Axis::Y => Self { x: ortho, y: aligned, }, } } pub fn axis(&self, axis: Axis) -> Len { match axis { Axis::X => self.x, Axis::Y => self.y, } } } impl Len { pub const ZERO: Self = Self { abs: 0.0, rel: 0.0, rest: 0.0, }; pub fn apply_rest(&self, rel_len: f32) -> UiScalar { UiScalar { rel: if self.rest > 0.0 { self.rel.max(1.0) } else { self.rel } * rel_len, abs: if self.rest > 0.0 { 0.0 } else { self.abs }, } } pub fn abs(abs: impl UiNum) -> Self { Self { abs: abs.to_f32(), rel: 0.0, rest: 0.0, } } pub fn rel(rel: impl UiNum) -> Self { Self { abs: 0.0, rel: rel.to_f32(), rest: 0.0, } } pub fn rest(ratio: impl UiNum) -> Self { Self { abs: 0.0, rel: 0.0, rest: ratio.to_f32(), } } } pub mod len_fns { use super::*; pub fn abs(abs: impl UiNum) -> Len { Len { abs: abs.to_f32(), rel: 0.0, rest: 0.0, } } pub fn rel(rel: impl UiNum) -> Len { Len { abs: 0.0, rel: rel.to_f32(), rest: 0.0, } } pub fn rest(ratio: impl UiNum) -> Len { Len { abs: 0.0, rel: 0.0, rest: ratio.to_f32(), } } } impl_op!(Len Add add; abs rel rest); impl_op!(Len Sub sub; abs rel rest); impl_op!(Size Add add; x y); impl_op!(Size Sub sub; x y); impl Default for Len { fn default() -> Self { Self::rest(1.0) } } impl std::fmt::Display for Size { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "({}, {})", self.x, self.y) } } impl std::fmt::Display for Len { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.abs != 0.0 { write!(f, "{} abs;", self.abs)?; } if self.rel != 0.0 { write!(f, "{} rel;", self.abs)?; } if self.rest != 0.0 { write!(f, "{} leftover;", self.abs)?; } Ok(()) } }