358 lines
8.9 KiB
Rust
358 lines
8.9 KiB
Rust
use std::{fmt::Display, marker::Destruct};
|
|
|
|
use crate::{
|
|
layout::{Align, Axis, UiNum, 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 {
|
|
pub const ZERO: Self = Self {
|
|
rel: Vec2::ZERO,
|
|
abs: Vec2::ZERO,
|
|
};
|
|
|
|
/// 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.offset(-size / 2.0),
|
|
bot_right: self.offset(size / 2.0),
|
|
}
|
|
}
|
|
|
|
pub const fn abs(abs: impl const Into<Vec2>) -> Self {
|
|
Self {
|
|
rel: Vec2::ZERO,
|
|
abs: abs.into(),
|
|
}
|
|
}
|
|
|
|
pub const fn rel(rel: impl const Into<Vec2>) -> Self {
|
|
Self {
|
|
rel: rel.into(),
|
|
abs: Vec2::ZERO,
|
|
}
|
|
}
|
|
|
|
pub const fn shift(&mut self, offset: impl const Into<UiVec2>) {
|
|
let offset = offset.into();
|
|
*self += offset;
|
|
}
|
|
|
|
pub const fn offset(mut self, offset: impl const Into<UiVec2>) -> 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,
|
|
},
|
|
}
|
|
}
|
|
|
|
/// reflection about an axis
|
|
pub fn flip(&mut self, axis: Axis) {
|
|
*self.rel.axis_mut(axis) = 1.0 - self.rel.axis(axis);
|
|
*self.abs.axis_mut(axis) = -self.abs.axis(axis);
|
|
}
|
|
|
|
pub fn flipped(mut self, axis: Axis) -> Self {
|
|
self.flip(axis);
|
|
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 Display for UiVec2 {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "rel {} / abs {}", self.rel, self.abs)
|
|
}
|
|
}
|
|
|
|
impl_op!(UiVec2 Add add; rel abs);
|
|
impl_op!(UiVec2 Sub sub; rel abs);
|
|
|
|
impl const From<Align> for UiVec2 {
|
|
fn from(align: Align) -> Self {
|
|
Self::rel(align.rel())
|
|
}
|
|
}
|
|
|
|
impl Align {
|
|
pub fn pos(self) -> UiVec2 {
|
|
UiVec2::from(self)
|
|
}
|
|
}
|
|
|
|
impl const From<Vec2> for UiVec2 {
|
|
fn from(abs: Vec2) -> Self {
|
|
Self::abs(abs)
|
|
}
|
|
}
|
|
|
|
impl<T: const UiNum, U: const UiNum> const From<(T, U)> for UiVec2
|
|
where
|
|
(T, U): const Destruct,
|
|
{
|
|
fn from(abs: (T, U)) -> Self {
|
|
Self::abs(abs)
|
|
}
|
|
}
|
|
|
|
#[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 rel(anchor: Vec2) -> Self {
|
|
Self {
|
|
top_left: UiVec2::rel(anchor),
|
|
bot_right: UiVec2::rel(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, axis: Axis) {
|
|
self.top_left.flip(axis);
|
|
self.bot_right.flip(axis);
|
|
let tl = self.top_left.axis_mut(axis);
|
|
let br = self.bot_right.axis_mut(axis);
|
|
std::mem::swap(tl.rel, br.rel);
|
|
std::mem::swap(tl.abs, br.abs);
|
|
}
|
|
|
|
pub fn shift(&mut self, offset: impl Into<UiVec2>) {
|
|
let offset = offset.into();
|
|
self.top_left.shift(offset);
|
|
self.bot_right.shift(offset);
|
|
}
|
|
|
|
pub fn offset(mut self, offset: impl Into<UiVec2>) -> 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.rel();
|
|
let mut bot_right = UiVec2::from(align);
|
|
bot_right.abs += size * (Vec2::ONE - align.rel());
|
|
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.rel();
|
|
top_left.rel -= size.rel * align.rel();
|
|
let mut bot_right = UiVec2::from(align);
|
|
bot_right.abs += size.abs * (Vec2::ONE - align.rel());
|
|
bot_right.rel += size.rel * (Vec2::ONE - align.rel());
|
|
Self {
|
|
top_left,
|
|
bot_right,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for UiRegion {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{} -> {}", self.top_left, self.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,
|
|
}
|
|
}
|
|
}
|