use crate::cast::*;
use crate::dir::Directional;
mod vector;
pub use vector::{DVec2, Quad, Vec2, Vec3};
macro_rules! impl_common {
($T:ty) => {
impl $T {
pub const ZERO: Self = Self(0, 0);
pub const MIN: Self = Self(i32::MIN, i32::MIN);
pub const MAX: Self = Self(i32::MAX, i32::MAX);
#[inline]
pub fn lt(self, rhs: Self) -> bool {
self.0 < rhs.0 && self.1 < rhs.1
}
#[inline]
pub fn le(self, rhs: Self) -> bool {
self.0 <= rhs.0 && self.1 <= rhs.1
}
#[inline]
pub fn ge(self, rhs: Self) -> bool {
self.0 >= rhs.0 && self.1 >= rhs.1
}
#[inline]
pub fn gt(self, rhs: Self) -> bool {
self.0 > rhs.0 && self.1 > rhs.1
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn min(self, other: Self) -> Self {
Self(self.0.min(other.0), self.1.min(other.1))
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn max(self, other: Self) -> Self {
Self(self.0.max(other.0), self.1.max(other.1))
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn clamp(self, min: Self, max: Self) -> Self {
debug_assert!(min.le(max));
self.min(max).max(min)
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn transpose(self) -> Self {
Self(self.1, self.0)
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn cwise_mul(self, rhs: Self) -> Self {
Self(self.0 * rhs.0, self.1 * rhs.1)
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn cwise_div(self, rhs: Self) -> Self {
Self(self.0 / rhs.0, self.1 / rhs.1)
}
#[inline]
pub fn distance_l1(self) -> i32 {
self.0.abs() + self.1.abs()
}
#[inline]
pub fn distance_l_inf(self) -> i32 {
self.0.abs().max(self.1.abs())
}
#[inline]
pub fn extract<D: Directional>(self, dir: D) -> i32 {
match dir.is_vertical() {
false => self.0,
true => self.1,
}
}
#[inline]
pub fn set_component<D: Directional>(&mut self, dir: D, value: i32) {
match dir.is_vertical() {
false => self.0 = value,
true => self.1 = value,
}
}
}
impl From<(i32, i32)> for $T {
#[inline]
fn from(v: (i32, i32)) -> Self {
Self(v.0, v.1)
}
}
impl Conv<(i32, i32)> for $T {
#[inline]
fn conv(v: (i32, i32)) -> Self {
Self(v.0, v.1)
}
#[inline]
fn try_conv(v: (i32, i32)) -> Result<Self> {
Ok(Self::conv(v))
}
}
};
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Coord(pub i32, pub i32);
impl_common!(Coord);
impl Coord {
#[inline]
pub fn new(x: i32, y: i32) -> Self {
Self(x, y)
}
#[inline]
pub const fn splat(n: i32) -> Self {
Self(n, n)
}
}
impl std::ops::Sub for Coord {
type Output = Offset;
#[inline]
fn sub(self, other: Self) -> Offset {
Offset(self.0 - other.0, self.1 - other.1)
}
}
impl std::ops::Add<Offset> for Coord {
type Output = Self;
#[inline]
fn add(self, other: Offset) -> Self {
Coord(self.0 + other.0, self.1 + other.1)
}
}
impl std::ops::AddAssign<Offset> for Coord {
#[inline]
fn add_assign(&mut self, rhs: Offset) {
self.0 += rhs.0;
self.1 += rhs.1;
}
}
impl std::ops::Sub<Offset> for Coord {
type Output = Self;
#[inline]
fn sub(self, other: Offset) -> Self {
Coord(self.0 - other.0, self.1 - other.1)
}
}
impl std::ops::SubAssign<Offset> for Coord {
#[inline]
fn sub_assign(&mut self, rhs: Offset) {
self.0 -= rhs.0;
self.1 -= rhs.1;
}
}
impl std::ops::Add<Size> for Coord {
type Output = Self;
#[inline]
fn add(self, other: Size) -> Self {
Coord(self.0 + other.0, self.1 + other.1)
}
}
impl std::ops::AddAssign<Size> for Coord {
#[inline]
fn add_assign(&mut self, rhs: Size) {
self.0 += rhs.0;
self.1 += rhs.1;
}
}
impl std::ops::Sub<Size> for Coord {
type Output = Self;
#[inline]
fn sub(self, other: Size) -> Self {
Coord(self.0 - other.0, self.1 - other.1)
}
}
impl std::ops::SubAssign<Size> for Coord {
#[inline]
fn sub_assign(&mut self, rhs: Size) {
self.0 -= rhs.0;
self.1 -= rhs.1;
}
}
impl Conv<Coord> for kas_text::Vec2 {
#[inline]
fn try_conv(pos: Coord) -> Result<Self> {
Ok(Vec2::try_conv(pos)?.into())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Size(pub i32, pub i32);
impl_common!(Size);
impl Size {
#[inline]
pub fn new(w: i32, h: i32) -> Self {
debug_assert!(w >= 0 && h >= 0, "Size::new({w}, {h}): negative value");
Self(w, h)
}
#[inline]
pub fn splat(n: i32) -> Self {
debug_assert!(n >= 0, "Size::splat({n}): negative value");
Self(n, n)
}
pub fn aspect_scale_to(self, target: Size) -> Option<Size> {
if self.0 == 0 || self.1 == 0 {
return None;
}
let h = i32::conv((i64::conv(self.1) * i64::conv(target.0)) / i64::conv(self.0));
if h <= target.1 {
Some(Size(target.0, h))
} else {
let w = i32::conv((i64::conv(self.0) * i64::conv(target.1)) / i64::conv(self.1));
Some(Size(w, target.1))
}
}
}
impl std::ops::Add for Size {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Size(self.0 + other.0, self.1 + other.1)
}
}
impl std::ops::AddAssign for Size {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
self.1 += rhs.1;
}
}
impl std::ops::Sub for Size {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Size(self.0 - rhs.0, self.1 - rhs.1).max(Size::ZERO)
}
}
impl std::ops::SubAssign for Size {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl std::ops::Mul<i32> for Size {
type Output = Self;
#[inline]
fn mul(self, x: i32) -> Self {
debug_assert!(x >= 0);
Size(self.0 * x, self.1 * x)
}
}
impl std::ops::Div<i32> for Size {
type Output = Self;
#[inline]
fn div(self, x: i32) -> Self {
debug_assert!(x >= 0);
Size(self.0 / x, self.1 / x)
}
}
impl Conv<Offset> for Coord {
#[inline]
fn try_conv(v: Offset) -> Result<Self> {
debug_assert!(v.0 >= 0 && v.1 >= 0, "Coord::conv({v:?}): negative value");
Ok(Self(v.0, v.1))
}
}
impl Conv<Offset> for Size {
#[inline]
fn try_conv(v: Offset) -> Result<Self> {
debug_assert!(v.0 >= 0 && v.1 >= 0, "Size::conv({v:?}): negative value");
Ok(Self(v.0, v.1))
}
}
impl Conv<Size> for (u16, u16) {
#[inline]
fn try_conv(size: Size) -> Result<Self> {
Ok((size.0.try_cast()?, size.1.try_cast()?))
}
}
impl Conv<(u16, u16)> for Size {
#[inline]
fn try_conv(v: (u16, u16)) -> Result<Self> {
Ok(Self(i32::try_conv(v.0)?, i32::try_conv(v.1)?))
}
}
impl Conv<(u32, u32)> for Size {
#[inline]
fn try_conv(v: (u32, u32)) -> Result<Self> {
Ok(Self(i32::try_conv(v.0)?, i32::try_conv(v.1)?))
}
}
impl Conv<Size> for (u32, u32) {
#[inline]
fn try_conv(size: Size) -> Result<Self> {
Ok((u32::try_conv(size.0)?, u32::try_conv(size.1)?))
}
}
impl Conv<Size> for kas_text::Vec2 {
#[inline]
fn try_conv(size: Size) -> Result<Self> {
Ok(Vec2::try_conv(size)?.into())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Offset(pub i32, pub i32);
impl_common!(Offset);
impl Offset {
#[inline]
pub fn new(x: i32, y: i32) -> Self {
Self(x, y)
}
#[inline]
pub const fn splat(n: i32) -> Self {
Self(n, n)
}
}
impl std::ops::Add for Offset {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Offset(self.0 + other.0, self.1 + other.1)
}
}
impl std::ops::AddAssign for Offset {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
self.1 += rhs.1;
}
}
impl std::ops::Sub for Offset {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
Offset(self.0 - other.0, self.1 - other.1)
}
}
impl std::ops::SubAssign for Offset {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
self.1 -= rhs.1;
}
}
impl std::ops::Mul<i32> for Offset {
type Output = Self;
#[inline]
fn mul(self, x: i32) -> Self {
Offset(self.0 * x, self.1 * x)
}
}
impl std::ops::Div<i32> for Offset {
type Output = Self;
#[inline]
fn div(self, x: i32) -> Self {
Offset(self.0 / x, self.1 / x)
}
}
impl Conv<Coord> for Offset {
#[inline]
fn try_conv(v: Coord) -> Result<Self> {
Ok(Self(v.0, v.1))
}
}
impl Conv<Size> for Offset {
#[inline]
fn try_conv(v: Size) -> Result<Self> {
Ok(Self(v.0, v.1))
}
}
impl Conv<Offset> for kas_text::Vec2 {
#[inline]
fn try_conv(v: Offset) -> Result<Self> {
Ok(Vec2::try_conv(v)?.into())
}
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rect {
pub pos: Coord,
pub size: Size,
}
impl Rect {
pub const ZERO: Self = Self::new(Coord::ZERO, Size::ZERO);
#[inline]
pub const fn new(pos: Coord, size: Size) -> Self {
Rect { pos, size }
}
#[inline]
pub fn pos2(&self) -> Coord {
self.pos + self.size
}
#[inline]
pub fn contains(&self, c: Coord) -> bool {
c.0 >= self.pos.0
&& c.0 < self.pos.0 + (self.size.0)
&& c.1 >= self.pos.1
&& c.1 < self.pos.1 + (self.size.1)
}
#[inline]
pub fn intersection(&self, rhs: &Rect) -> Option<Rect> {
let (l1, l2) = (self.pos, self.pos2());
let (r1, r2) = (rhs.pos, rhs.pos2());
let pos = l1.max(r1);
let pos2 = l2.min(r2);
if pos.le(pos2) {
Some(Rect::new(pos, (pos2 - pos).cast()))
} else {
None
}
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn shrink(&self, n: i32) -> Rect {
let pos = self.pos + Offset::splat(n);
let size = self.size - Size::splat(n + n);
Rect { pos, size }
}
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn expand(&self, n: i32) -> Rect {
debug_assert!(n >= 0);
let pos = self.pos - Offset::splat(n);
let size = self.size + Size::splat(n + n);
Rect { pos, size }
}
}
impl std::ops::Add<Offset> for Rect {
type Output = Self;
#[inline]
fn add(self, offset: Offset) -> Self {
Rect::new(self.pos + offset, self.size)
}
}
impl std::ops::AddAssign<Offset> for Rect {
#[inline]
fn add_assign(&mut self, offset: Offset) {
self.pos += offset;
}
}
impl std::ops::Sub<Offset> for Rect {
type Output = Self;
#[inline]
fn sub(self, offset: Offset) -> Self {
Rect::new(self.pos - offset, self.size)
}
}
impl std::ops::SubAssign<Offset> for Rect {
#[inline]
fn sub_assign(&mut self, offset: Offset) {
self.pos -= offset;
}
}
#[cfg(winit)]
#[cfg_attr(doc_cfg, doc(cfg(feature = "winit")))]
mod winit_impls {
use super::{Coord, Size};
use crate::cast::{Cast, CastApprox, Conv, ConvApprox};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
impl<X: CastApprox<i32>> ConvApprox<PhysicalPosition<X>> for Coord {
#[inline]
fn try_conv_approx(pos: PhysicalPosition<X>) -> cast::Result<Self> {
Ok(Coord(pos.x.cast_approx(), pos.y.cast_approx()))
}
}
impl<X: Cast<i32>> Conv<PhysicalSize<X>> for Size {
#[inline]
fn try_conv(size: PhysicalSize<X>) -> cast::Result<Self> {
Ok(Size(size.width.cast(), size.height.cast()))
}
}
impl Size {
#[inline]
pub fn as_physical(self) -> winit::dpi::Size {
let (w, h): (u32, u32) = self.cast();
winit::dpi::Size::Physical(PhysicalSize::new(w, h))
}
#[inline]
pub fn as_logical(self) -> winit::dpi::Size {
let (w, h) = (self.0 as f64, self.1 as f64);
winit::dpi::Size::Logical(LogicalSize::new(w, h))
}
}
}