use std::{fmt::Display, hash::Hash, marker::Destruct}; use super::*; use crate::{ UiNum, util::{LerpUtil, impl_op}, }; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)] pub struct UiVec2 { pub x: UiScalar, pub y: UiScalar, } impl UiVec2 { pub const ZERO: Self = Self { x: UiScalar::ZERO, y: UiScalar::ZERO, }; pub const fn new(x: UiScalar, y: UiScalar) -> Self { Self { x, y } } pub const fn abs(abs: impl const Into) -> Self { let abs = abs.into(); Self { x: UiScalar::abs(abs.x), y: UiScalar::abs(abs.y), } } pub const fn rel(rel: impl const Into) -> Self { let rel = rel.into(); Self { x: UiScalar::rel(rel.x), y: UiScalar::rel(rel.y), } } pub const fn shift(&mut self, offset: impl const Into) { let offset = offset.into(); *self += offset; } pub const fn offset(mut self, offset: impl const Into) -> Self { self.shift(offset); self } pub const fn within(&self, region: &UiRegion) -> UiVec2 { UiVec2 { x: self.x.within(®ion.x), y: self.y.within(®ion.y), } } pub const fn outside(&self, region: &UiRegion) -> UiVec2 { UiVec2 { x: self.x.outside(®ion.x), y: self.y.outside(®ion.y), } } pub fn axis_mut(&mut self, axis: Axis) -> &mut UiScalar { match axis { Axis::X => &mut self.x, Axis::Y => &mut self.y, } } pub fn axis(&self, axis: Axis) -> UiScalar { match axis { Axis::X => self.x, Axis::Y => self.y, } } pub fn to_abs(&self, rel: Vec2) -> Vec2 { Vec2 { x: self.x.to_abs(rel.x), y: self.y.to_abs(rel.y), } } pub const FULL_SIZE: Self = Self::rel(Vec2::ONE); pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self { match axis { Axis::X => Self { x: aligned, y: ortho, }, Axis::Y => Self { x: ortho, y: aligned, }, } } pub fn get_abs(&self) -> Vec2 { (self.x.abs, self.y.abs).into() } pub fn get_rel(&self) -> Vec2 { (self.x.rel, self.y.rel).into() } pub fn abs_mut(&mut self) -> Vec2View<'_> { Vec2View { x: &mut self.x.abs, y: &mut self.y.abs, } } } impl Display for UiVec2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "rel{};abs{}", self.get_rel(), self.get_abs()) } } impl_op!(UiVec2 Add add; x y); impl_op!(UiVec2 Sub sub; x y); impl const From for UiVec2 { fn from(abs: Vec2) -> Self { Self::abs(abs) } } impl const From<(T, U)> for UiVec2 where (T, U): const Destruct, { fn from(abs: (T, U)) -> Self { Self::abs(abs) } } #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, Default, bytemuck::Zeroable)] pub struct UiScalar { pub rel: f32, pub abs: f32, } impl Eq for UiScalar {} impl Hash for UiScalar { fn hash(&self, state: &mut H) { state.write_u32(self.rel.to_bits()); state.write_u32(self.abs.to_bits()); } } 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 const FULL: Self = Self { rel: 1.0, abs: 0.0 }; pub const fn new(rel: f32, abs: f32) -> Self { Self { rel, abs } } pub const fn rel(rel: f32) -> Self { Self { rel, abs: 0.0 } } pub const fn abs(abs: f32) -> Self { Self { rel: 0.0, abs } } pub const fn rel_min() -> Self { Self::new(0.0, 0.0) } pub const fn rel_max() -> Self { Self::new(1.0, 0.0) } pub const fn max(&self, other: Self) -> Self { Self { rel: self.rel.max(other.rel), abs: self.abs.max(other.abs), } } pub const fn min(&self, other: Self) -> Self { Self { rel: self.rel.min(other.rel), abs: self.abs.min(other.abs), } } pub const fn offset(mut self, amt: f32) -> Self { self.abs += amt; self } pub const fn within(&self, span: &UiSpan) -> Self { let anchor = self.rel.lerp(span.start.rel, span.end.rel); let offset = self.abs + self.rel.lerp(span.start.abs, span.end.abs); Self { rel: anchor, abs: offset, } } pub const fn outside(&self, span: &UiSpan) -> Self { let rel = self.rel.lerp_inv(span.start.rel, span.end.rel); let abs = self.abs - rel.lerp(span.start.abs, span.end.abs); Self { rel, abs } } pub fn within_len(&self, len: UiScalar) -> Self { self.within(&UiSpan { start: UiScalar::ZERO, end: len, }) } pub fn select_len(&self, len: UiScalar) -> Self { len.within_len(*self) } pub const fn flip(&mut self) { self.rel = 1.0 - self.rel; self.abs = -self.abs; } pub const fn to(&self, end: Self) -> UiSpan { UiSpan { start: *self, end } } pub const fn to_abs(&self, rel: f32) -> f32 { self.rel * rel + self.abs } } #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct UiSpan { pub start: UiScalar, pub end: UiScalar, } impl UiSpan { pub const FULL: Self = Self { start: UiScalar::ZERO, end: UiScalar::FULL, }; pub const fn rel(rel: f32) -> Self { Self { start: UiScalar::rel(rel), end: UiScalar::rel(rel), } } pub const fn new(start: UiScalar, end: UiScalar) -> Self { Self { start, end } } pub const fn flip(&mut self) { self.start.flip(); self.end.flip(); std::mem::swap(&mut self.start.rel, &mut self.end.rel); std::mem::swap(&mut self.start.abs, &mut self.end.abs); } pub const fn shift(&mut self, offset: UiScalar) { self.start += offset; self.end += offset; } pub const fn within(&self, parent: &Self) -> Self { Self { start: self.start.within(parent), end: self.end.within(parent), } } pub const fn outside(&self, parent: &Self) -> Self { Self { start: self.start.outside(parent), end: self.end.outside(parent), } } pub const fn len(&self) -> UiScalar { self.end - self.start } } #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct UiRegion { pub x: UiSpan, pub y: UiSpan, } impl UiRegion { pub const FULL: Self = Self { x: UiSpan::FULL, y: UiSpan::FULL, }; pub const fn new(x: UiSpan, y: UiSpan) -> Self { Self { x, y } } pub const fn rel(rel: Vec2) -> Self { Self { x: UiSpan::rel(rel.x), y: UiSpan::rel(rel.y), } } pub const fn within(&self, parent: &Self) -> Self { Self { x: self.x.within(&parent.x), y: self.y.within(&parent.y), } } pub const fn outside(&self, parent: &Self) -> Self { Self { x: self.x.outside(&parent.x), y: self.y.outside(&parent.y), } } pub const fn axis(&mut self, axis: Axis) -> &UiSpan { match axis { Axis::X => &self.x, Axis::Y => &self.y, } } pub const fn axis_mut(&mut self, axis: Axis) -> &mut UiSpan { match axis { Axis::X => &mut self.x, Axis::Y => &mut self.y, } } pub const fn flip(&mut self, axis: Axis) { match axis { Axis::X => self.x.flip(), Axis::Y => self.y.flip(), } } pub fn shift(&mut self, offset: impl Into) { let offset = offset.into(); self.x.shift(offset.x); self.y.shift(offset.y); } pub fn offset(mut self, offset: impl Into) -> Self { self.shift(offset); self } pub fn to_px(&self, size: Vec2) -> PixelRegion { PixelRegion { top_left: self.top_left().get_rel() * size + self.top_left().get_abs(), bot_right: self.bot_right().get_rel() * size + self.bot_right().get_abs(), } } pub const fn center(&self) -> UiVec2 { Align::CENTER.pos().within(self) } pub const fn size(&self) -> UiVec2 { UiVec2 { x: self.x.len(), y: self.y.len(), } } pub const fn top_left(&self) -> UiVec2 { UiVec2 { x: self.x.start, y: self.y.start, } } pub const fn bot_right(&self) -> UiVec2 { UiVec2 { x: self.x.end, y: self.y.end, } } pub const fn from_axis(axis: Axis, aligned: UiSpan, ortho: UiSpan) -> Self { Self { x: match axis { Axis::X => aligned, Axis::Y => ortho, }, y: match axis { Axis::X => ortho, Axis::Y => aligned, }, } } } impl Display for UiRegion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{} -> {} (size: {})", self.top_left(), self.bot_right(), self.size() ) } } #[derive(Debug)] pub struct PixelRegion { pub top_left: Vec2, pub bot_right: Vec2, } impl PixelRegion { 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 fn size(&self) -> Vec2 { self.bot_right - self.top_left } } impl Display for PixelRegion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} -> {}", self.top_left, self.bot_right) } } pub struct Vec2View<'a> { pub x: &'a mut f32, pub y: &'a mut f32, } impl Vec2View<'_> { pub fn set(&mut self, other: Vec2) { *self.x = other.x; *self.y = other.y; } pub fn add(&mut self, other: Vec2) { *self.x += other.x; *self.y += other.y; } }