use crate::{ layout::{Axis, Corner, Vec2, vec2}, util::{F32Util, impl_op}, }; #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)] pub struct UiPos { pub anchor: Vec2, pub offset: Vec2, } impl UiPos { /// 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 center() -> Self { Self::anchor(vec2(0.5, 0.5)) } pub const fn anchor(anchor: Vec2) -> Self { Self { anchor, offset: Vec2::ZERO, } } pub const fn offset(offset: Vec2) -> Self { Self { anchor: Vec2::ZERO, offset, } } pub const fn corner(corner: Corner) -> Self { Self::anchor(corner.anchor()) } pub const fn shift(&mut self, offset: impl const Into) { self.offset += offset.into(); } pub const fn shifted(mut self, offset: Vec2) -> Self { self.shift(offset); self } pub const fn within(&self, region: &UiRegion) -> UiPos { let anchor = self .anchor .lerp(region.top_left.anchor, region.bot_right.anchor); let offset = self.offset + self .anchor .lerp(region.top_left.offset, region.bot_right.offset); UiPos { anchor, offset } } pub fn axis_mut(&mut self, axis: Axis) -> UIScalarView<'_> { match axis { Axis::X => UIScalarView { anchor: &mut self.anchor.x, offset: &mut self.offset.x, }, Axis::Y => UIScalarView { anchor: &mut self.anchor.y, offset: &mut self.offset.y, }, } } pub fn flip(&mut self) { self.anchor = 1.0 - self.anchor; self.offset = -self.offset; } pub fn flipped(mut self) -> Self { self.flip(); self } pub fn to_size(&self, size: Vec2) -> Vec2 { self.anchor * size + self.offset } } #[derive(Clone, Copy, Debug)] pub struct UIScalar { pub anchor: f32, pub offset: f32, } impl_op!(UIScalar Add add; anchor offset); impl_op!(UIScalar Sub sub; anchor offset); impl UIScalar { pub fn new(anchor: f32, offset: f32) -> Self { Self { anchor, offset } } pub fn min() -> Self { Self::new(0.0, 0.0) } pub fn max() -> Self { Self::new(1.0, 0.0) } pub fn from_anchor(anchor: f32) -> Self { Self::new(anchor, 0.0) } pub fn offset(mut self, amt: f32) -> Self { self.offset += amt; self } pub fn within(&self, start: UIScalar, end: UIScalar) -> Self { let anchor = self.anchor.lerp(start.anchor, end.anchor); let offset = self.offset + self.anchor.lerp(start.offset, end.offset); Self { anchor, offset } } } #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct UiRegion { pub top_left: UiPos, pub bot_right: UiPos, } impl UiRegion { pub const fn full() -> Self { Self { top_left: UiPos::corner(Corner::TopLeft), bot_right: UiPos::corner(Corner::BotRight), } } pub fn anchor(anchor: Vec2) -> Self { Self { top_left: UiPos::anchor(anchor), bot_right: UiPos::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 select(&mut self, inner: &Self) { *self = inner.within(self); } 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) -> ScreenRect { ScreenRect { top_left: self.top_left.anchor * size + self.top_left.offset, bot_right: self.bot_right.anchor * size + self.bot_right.offset, } } pub fn center(&self) -> UiPos { UiPos::center().within(self) } pub fn in_size(&self, size: Vec2) -> Vec2 { self.bot_right.to_size(size) - self.top_left.to_size(size) } } #[derive(Debug)] pub struct ScreenRect { top_left: Vec2, bot_right: Vec2, } impl ScreenRect { 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 anchor: &'a mut f32, pub offset: &'a mut f32, } impl UIScalarView<'_> { pub fn set(&mut self, scalar: UIScalar) { *self.anchor = scalar.anchor; *self.offset = scalar.offset; } pub fn get(self) -> UIScalar { UIScalar { anchor: *self.anchor, offset: *self.offset, } } }