465 lines
10 KiB
Rust
465 lines
10 KiB
Rust
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<Vec2>) -> Self {
|
|
let abs = abs.into();
|
|
Self {
|
|
x: UiScalar::abs(abs.x),
|
|
y: UiScalar::abs(abs.y),
|
|
}
|
|
}
|
|
|
|
pub const fn rel(rel: impl const Into<Vec2>) -> 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<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 {
|
|
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<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)
|
|
}
|
|
}
|
|
|
|
#[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<H: std::hash::Hasher>(&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<UiVec2>) {
|
|
let offset = offset.into();
|
|
self.x.shift(offset.x);
|
|
self.y.shift(offset.y);
|
|
}
|
|
|
|
pub fn offset(mut self, offset: impl Into<UiVec2>) -> 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;
|
|
}
|
|
}
|