use crate::{ layout::{Align, Axis, Vec2}, util::{LerpUtil, impl_op}, }; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable, Default)] pub struct UiVec2 { pub rel: Vec2, pub abs: Vec2, } impl UiVec2 { /// expands this position into a sized region centered at self pub fn expand(&self, size: impl Into) -> UiRegion { let size = size.into(); UiRegion { top_left: self.shifted(-size / 2.0), bot_right: self.shifted(size / 2.0), } } pub const fn anchor(anchor: Vec2) -> Self { Self::rel(anchor) } pub const fn offset(offset: Vec2) -> Self { Self::abs(offset) } pub const fn abs(abs: Vec2) -> Self { Self { rel: Vec2::ZERO, abs, } } pub const fn rel(rel: Vec2) -> Self { Self { rel, abs: Vec2::ZERO, } } pub const fn shift(&mut self, offset: impl const Into) { self.abs += offset.into(); } pub const fn shifted(mut self, offset: Vec2) -> Self { self.shift(offset); self } pub const fn within(&self, region: &UiRegion) -> UiVec2 { let rel = self.rel.lerp(region.top_left.rel, region.bot_right.rel); let abs = self.abs + self.rel.lerp(region.top_left.abs, region.bot_right.abs); UiVec2 { rel, abs } } pub const fn outside(&self, region: &UiRegion) -> UiVec2 { let rel = self.rel.lerp_inv(region.top_left.rel, region.bot_right.rel); let abs = self.abs - rel.lerp(region.top_left.abs, region.bot_right.abs); UiVec2 { rel, abs } } pub fn axis_mut(&mut self, axis: Axis) -> UiScalarView<'_> { match axis { Axis::X => UiScalarView { rel: &mut self.rel.x, abs: &mut self.abs.x, }, Axis::Y => UiScalarView { rel: &mut self.rel.y, abs: &mut self.abs.y, }, } } pub fn axis(&self, axis: Axis) -> UiScalar { match axis { Axis::X => UiScalar { rel: self.rel.x, abs: self.abs.x, }, Axis::Y => UiScalar { rel: self.rel.y, abs: self.abs.y, }, } } pub fn flip(&mut self) { self.rel = 1.0 - self.rel; self.abs = -self.abs; } pub fn flipped(mut self) -> Self { self.flip(); self } pub fn to_size(&self, size: Vec2) -> Vec2 { self.rel * size + self.abs } pub const MAX_SIZE: Self = Self::rel(Vec2::ONE); pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self { Self { rel: Vec2::from_axis(axis, aligned.rel, ortho.rel), abs: Vec2::from_axis(axis, aligned.abs, ortho.abs), } } } impl_op!(UiVec2 Add add; rel abs); impl_op!(UiVec2 Sub sub; rel abs); impl const From for UiVec2 { fn from(align: Align) -> Self { Self::anchor(align.anchor()) } } impl Align { pub fn pos(self) -> UiVec2 { UiVec2::from(self) } } #[derive(Clone, Copy, Debug, Default)] pub struct UiScalar { pub rel: f32, pub abs: f32, } impl_op!(UiScalar Add add; rel abs); impl_op!(UiScalar Sub sub; rel abs); impl UiScalar { pub const ZERO: Self = Self { rel: 0.0, abs: 0.0 }; pub fn new(rel: f32, abs: f32) -> Self { Self { rel, abs } } pub fn rel_min() -> Self { Self::new(0.0, 0.0) } pub fn rel_max() -> Self { Self::new(1.0, 0.0) } pub fn max(&self, other: Self) -> Self { Self { rel: self.rel.max(other.rel), abs: self.abs.max(other.abs), } } pub fn min(&self, other: Self) -> Self { Self { rel: self.rel.min(other.rel), abs: self.abs.min(other.abs), } } pub fn from_anchor(anchor: f32) -> Self { Self::new(anchor, 0.0) } pub fn offset(mut self, amt: f32) -> Self { self.abs += amt; self } pub fn within(&self, start: UiScalar, end: UiScalar) -> Self { let anchor = self.rel.lerp(start.rel, end.rel); let offset = self.abs + self.rel.lerp(start.abs, end.abs); Self { rel: anchor, abs: offset, } } } #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct UiRegion { pub top_left: UiVec2, pub bot_right: UiVec2, } impl UiRegion { pub const fn full() -> Self { Self { top_left: Align::TopLeft.into(), bot_right: Align::BotRight.into(), } } pub fn anchor(anchor: Vec2) -> Self { Self { top_left: UiVec2::anchor(anchor), bot_right: UiVec2::anchor(anchor), } } pub fn within(&self, parent: &Self) -> Self { Self { top_left: self.top_left.within(parent), bot_right: self.bot_right.within(parent), } } pub fn outside(&self, parent: &Self) -> Self { Self { top_left: self.top_left.outside(parent), bot_right: self.bot_right.outside(parent), } } pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> { UIRegionAxisView { top_left: self.top_left.axis_mut(axis), bot_right: self.bot_right.axis_mut(axis), } } pub fn flip(&mut self) { self.top_left.flip(); self.bot_right.flip(); std::mem::swap(&mut self.top_left, &mut self.bot_right); } pub fn shift(&mut self, offset: impl Into) { let offset = offset.into(); self.top_left.shift(offset); self.bot_right.shift(offset); } pub fn shifted(mut self, offset: impl Into) -> Self { self.shift(offset); self } pub fn to_screen(&self, size: Vec2) -> ScreenRegion { ScreenRegion { top_left: self.top_left.rel * size + self.top_left.abs, bot_right: self.bot_right.rel * size + self.bot_right.abs, } } pub fn center(&self) -> UiVec2 { Align::Center.pos().within(self) } pub fn in_size(&self, size: Vec2) -> Vec2 { self.bot_right.to_size(size) - self.top_left.to_size(size) } pub fn size(&self) -> UiVec2 { self.bot_right - self.top_left } pub fn select_aligned(&self, size: Vec2, align: Align) -> Self { Self::from_size_align(size, align).within(self) } pub fn from_size_align(size: Vec2, align: Align) -> Self { let mut top_left = UiVec2::from(align); top_left.abs -= size * align.anchor(); let mut bot_right = UiVec2::from(align); bot_right.abs += size * (Vec2::ONE - align.anchor()); Self { top_left, bot_right, } } pub fn from_ui_size_align(size: UiVec2, align: Align) -> Self { let mut top_left = UiVec2::from(align); top_left.abs -= size.abs * align.anchor(); top_left.rel -= size.rel * align.anchor(); let mut bot_right = UiVec2::from(align); bot_right.abs += size.abs * (Vec2::ONE - align.anchor()); bot_right.rel += size.rel * (Vec2::ONE - align.anchor()); Self { top_left, bot_right, } } } #[derive(Debug)] pub struct ScreenRegion { pub top_left: Vec2, pub bot_right: Vec2, } impl ScreenRegion { pub fn contains(&self, pos: Vec2) -> bool { pos.x >= self.top_left.x && pos.x <= self.bot_right.x && pos.y >= self.top_left.y && pos.y <= self.bot_right.y } } pub struct UIRegionAxisView<'a> { pub top_left: UiScalarView<'a>, pub bot_right: UiScalarView<'a>, } pub struct UiScalarView<'a> { pub rel: &'a mut f32, pub abs: &'a mut f32, } impl UiScalarView<'_> { pub fn set(&mut self, scalar: UiScalar) { *self.rel = scalar.rel; *self.abs = scalar.abs; } pub fn get(self) -> UiScalar { UiScalar { rel: *self.rel, abs: *self.abs, } } }