Files
iris/src/layout/pos.rs
2025-09-09 21:53:32 -04:00

251 lines
6.1 KiB
Rust

use crate::{
layout::{Align, Axis, Vec2},
util::{F32Util, impl_op},
};
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, 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<Vec2>) -> 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 {
anchor,
offset: Vec2::ZERO,
}
}
pub const fn offset(offset: Vec2) -> Self {
Self {
anchor: Vec2::ZERO,
offset,
}
}
pub const fn shift(&mut self, offset: impl const Into<Vec2>) {
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
}
}
impl const From<Align> for UiPos {
fn from(align: Align) -> Self {
Self::anchor(align.anchor())
}
}
impl Align {
pub fn pos(self) -> UiPos {
UiPos::from(self)
}
}
#[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, PartialEq, 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: Align::TopLeft.into(),
bot_right: Align::BotRight.into(),
}
}
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 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<Vec2>) {
let offset = offset.into();
self.top_left.shift(offset);
self.bot_right.shift(offset);
}
pub fn shifted(mut self, offset: impl Into<Vec2>) -> Self {
self.shift(offset);
self
}
pub fn to_screen(&self, size: Vec2) -> ScreenRegion {
ScreenRegion {
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 {
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 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 = UiPos::from(align);
top_left.offset -= size * align.anchor();
let mut bot_right = UiPos::from(align);
bot_right.offset += size * (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 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,
}
}
}