// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: Apache-2.0 use bitflags::bitflags; use bytemuck::{Pod, Zeroable}; pub use glam::Vec2; use num_derive::{FromPrimitive, ToPrimitive}; pub mod api; #[repr(C)] #[derive(Copy, Clone, Debug, Default, Pod, Zeroable)] pub struct Rect { pub tl: Vec2, pub br: Vec2, } impl Rect { pub const NEG_INFINITY: Self = Self { tl: Vec2::splat(f32::INFINITY), br: Vec2::splat(f32::NEG_INFINITY), }; pub fn from_xy_size(xy: Vec2, size: Vec2) -> Self { Self { tl: xy, br: xy + size, } } pub fn from_circle_bounds(center: Vec2, radius: f32) -> Self { Self { tl: center - radius, br: center + radius, } } pub fn from_triangle_bounds(tri: &ColoredTriangle) -> Self { Self { tl: tri.v1.min(tri.v2).min(tri.v3), br: tri.v1.max(tri.v2).max(tri.v3), } } pub fn inset(&self, d: f32) -> Self { Self { tl: self.tl + d, br: self.br - d, } } pub fn bl(&self) -> Vec2 { Vec2::new(self.tl.x, self.br.y) } pub fn tr(&self) -> Vec2 { Vec2::new(self.br.x, self.tl.y) } pub fn offset(&self, offset: Vec2) -> Self { Self { tl: self.tl + offset, br: self.br + offset, } } pub fn scale(&self, scale: f32) -> Self { Self { tl: self.tl * scale, br: self.br * scale, } } pub fn is_valid(&self) -> bool { self.tl.cmplt(self.br).all() } pub fn intersects_rect(&self, other: &Self) -> bool { self.tl.cmple(other.br).all() && self.br.cmpge(other.tl).all() } pub fn intersection(&self, other: &Self) -> Option { let clipped = Self { tl: self.tl.max(other.tl), br: self.br.min(other.br), }; if clipped.is_valid() { Some(clipped) } else { None } } pub fn union(&self, other: &Self) -> Self { Self { tl: self.tl.min(other.tl), br: self.br.max(other.br), } } pub fn union_point(&self, point: Vec2) -> Self { Self { tl: self.tl.min(point), br: self.br.max(point), } } pub fn contains_rect(&self, other: &Self) -> bool { self.tl.x < other.tl.x && self.tl.y < other.tl.y && self.br.x > other.br.x && self.br.y > other.br.y } pub fn contains_point(&self, xy: Vec2) -> bool { self.tl.x < xy.x && self.tl.y < xy.y && self.br.x > xy.x && self.br.y > xy.y } pub fn size(&self) -> Vec2 { self.br - self.tl } pub fn width(&self) -> f32 { self.br.x - self.tl.x } pub fn height(&self) -> f32 { self.br.y - self.tl.y } } #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct Color(pub u32); impl From for Color { fn from(other: glam::Vec4) -> Self { let map = |v: f32| (v * 255.0).floor() as u8; Self::new(map(other.x), map(other.y), map(other.z), map(other.w)) } } impl From for glam::Vec4 { fn from(other: Color) -> Self { let (r, g, b, a) = other.to_rgba_unmultiplied(); let map = |v: u8| (v as f32) / 255.0; Self::new(map(r), map(g), map(b), map(a)) } } impl Color { pub const WHITE: Self = Self(0xffffffff); pub const BLACK: Self = Self(0x000000ff); pub const TRANSPARENT: Self = Self(0); pub const RED: Self = Self(0xff0000ff); pub const GREEN: Self = Self(0x00ff00ff); pub const BLUE: Self = Self(0x0000ffff); pub const YELLOW: Self = Self(0xffff00ff); pub const MAGENTA: Self = Self(0xff00ffff); pub const CYAN: Self = Self(0x00ffffff); pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self { Color(((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | (a as u32)) } pub fn to_rgba_unmultiplied(&self) -> (u8, u8, u8, u8) { ( (self.0 >> 24) as u8, (self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8, ) } pub const fn alpha_multiply(&self, mul: u8) -> Self { let a = self.0 as u8 as u16; let multiplied = ((a * (mul as u16)) >> 8) as u8; self.with_alpha(multiplied) } pub const fn with_alpha(&self, alpha: u8) -> Self { Self(self.0 & 0xffffff00 | alpha as u32) } pub fn lerp(self, target: Self, blend: f32) -> Self { let s: glam::Vec4 = self.into(); let o: glam::Vec4 = target.into(); (o * blend + s * (1.0 - blend)).into() } } #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct MeshVertex { pub position: Vec2, pub color: Color, } pub type MeshIndex = u32; #[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)] pub enum CursorEventKind { Hover = 0, Select = 1, Drag = 2, Deselect = 3, } #[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)] pub enum Corner { TopRight = 0, BottomRight = 1, BottomLeft = 2, TopLeft = 3, } bitflags! { pub struct CornerFlags: u8 { const TOP_RIGHT = 0x01; const BOTTOM_RIGHT = 0x02; const BOTTOM_LEFT = 0x04; const TOP_LEFT = 0x08; const TOP = 0x09; const RIGHT = 0x03; const BOTTOM = 0x06; const LEFT = 0x0C; const ALL = 0x0F; } } #[derive(Copy, Clone, Debug)] pub enum Side { Top, Right, Bottom, Left, } #[derive(Copy, Clone)] pub struct ColoredTriangle { pub v1: Vec2, pub v2: Vec2, pub v3: Vec2, pub color: Color, }