#![deny(missing_docs)]
#![deny(unsafe_code)]
use std::{
ops::{Add, Div, Mul, Neg, Sub},
vec,
};
pub use Rectangle as _;
pub mod f32 {
pub type Dim = f32;
pub type Vec2 = [Dim; 2];
pub type Rect = [Dim; 4];
pub type Circ = ([Dim; 2], Dim);
}
pub mod f64 {
pub type Dim = f64;
pub type Vec2 = [Dim; 2];
pub type Rect = [Dim; 4];
pub type Circ = ([Dim; 2], Dim);
}
pub trait Pair {
type Item;
fn first(self) -> Self::Item;
fn second(self) -> Self::Item;
fn from_items(a: Self::Item, b: Self::Item) -> Self;
}
impl<T> Pair for (T, T)
where
T: Clone,
{
type Item = T;
fn first(self) -> Self::Item {
self.0
}
fn second(self) -> Self::Item {
self.1
}
fn from_items(a: Self::Item, b: Self::Item) -> Self {
(a, b)
}
}
impl<T> Pair for [T; 2]
where
T: Clone,
{
type Item = T;
fn first(self) -> Self::Item {
self[0].clone()
}
fn second(self) -> Self::Item {
self[1].clone()
}
fn from_items(a: Self::Item, b: Self::Item) -> Self {
[a, b]
}
}
impl<T> Pair for (T, T, T, T)
where
T: Clone,
{
type Item = (T, T);
fn first(self) -> Self::Item {
(self.0, self.1)
}
fn second(self) -> Self::Item {
(self.2, self.3)
}
fn from_items(a: Self::Item, b: Self::Item) -> Self {
(a.0, a.1, b.0, b.1)
}
}
impl<T> Pair for [T; 4]
where
T: Clone,
{
type Item = [T; 2];
fn first(self) -> Self::Item {
[self[0].clone(), self[1].clone()]
}
fn second(self) -> Self::Item {
[self[2].clone(), self[3].clone()]
}
fn from_items(a: Self::Item, b: Self::Item) -> Self {
[a[0].clone(), a[1].clone(), b[0].clone(), b[1].clone()]
}
}
pub trait Sin {
type Output;
fn sin(self) -> Self::Output;
}
impl Sin for f32 {
type Output = f32;
fn sin(self) -> Self::Output {
f32::sin(self)
}
}
impl Sin for f64 {
type Output = f64;
fn sin(self) -> Self::Output {
f64::sin(self)
}
}
pub trait Cos {
type Output;
fn cos(self) -> Self::Output;
}
impl Cos for f32 {
type Output = f32;
fn cos(self) -> Self::Output {
f32::cos(self)
}
}
impl Cos for f64 {
type Output = f64;
fn cos(self) -> Self::Output {
f64::cos(self)
}
}
pub trait Abs {
fn abs(self) -> Self;
}
macro_rules! abs_unsigned_impl {
($type:ty) => {
impl Abs for $type {
fn abs(self) -> Self {
self
}
}
};
}
macro_rules! abs_signed_impl {
($type:ty) => {
impl Abs for $type {
fn abs(self) -> Self {
Self::abs(self)
}
}
};
}
abs_unsigned_impl! {u8}
abs_unsigned_impl! {u16}
abs_unsigned_impl! {u32}
abs_unsigned_impl! {u64}
abs_unsigned_impl! {u128}
abs_unsigned_impl! {usize}
abs_signed_impl! {i8}
abs_signed_impl! {i16}
abs_signed_impl! {i32}
abs_signed_impl! {i64}
abs_signed_impl! {i128}
abs_signed_impl! {isize}
abs_signed_impl! {f32}
abs_signed_impl! {f64}
pub trait Pow<P> {
type Output;
fn pow(self, power: P) -> Self::Output;
}
macro_rules! pow_float_impl {
($type:ty) => {
impl Pow<Self> for $type {
type Output = Self;
fn pow(self, power: Self) -> Self::Output {
self.powf(power)
}
}
};
}
pow_float_impl! {f32}
pow_float_impl! {f64}
pub trait ZeroOneTwo: Copy {
const ZERO: Self;
const ONE: Self;
const TWO: Self;
}
macro_rules! zot_int_impl {
($type:ty) => {
impl ZeroOneTwo for $type {
const ZERO: Self = 0;
const ONE: Self = 1;
const TWO: Self = 2;
}
};
}
zot_int_impl! {u8}
zot_int_impl! {u16}
zot_int_impl! {u32}
zot_int_impl! {u64}
zot_int_impl! {u128}
zot_int_impl! {usize}
zot_int_impl! {i8}
zot_int_impl! {i16}
zot_int_impl! {i32}
zot_int_impl! {i64}
zot_int_impl! {i128}
zot_int_impl! {isize}
macro_rules! zot_float_impl {
($type:ty) => {
impl ZeroOneTwo for $type {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
const TWO: Self = 2.0;
}
};
}
zot_float_impl! {f32}
zot_float_impl! {f64}
pub trait Scalar:
Add<Self, Output = Self>
+ Copy
+ PartialEq
+ PartialOrd
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ Abs
+ ZeroOneTwo
{
fn maxx(self, other: Self) -> Self {
if self > other {
self
} else {
other
}
}
fn minn(self, other: Self) -> Self {
if self < other {
self
} else {
other
}
}
fn square(self) -> [Self; 2] {
Vector2::square(self)
}
}
impl<T> Scalar for T where
T: Copy
+ PartialEq
+ PartialOrd
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Mul<T, Output = T>
+ Div<T, Output = T>
+ Abs
+ ZeroOneTwo
{
}
pub trait FloatingScalar:
Scalar + Pow<Self, Output = Self> + Sin<Output = Self> + Cos<Output = Self>
{
const PI: Self;
const EPSILON: Self;
fn lerp(self, other: Self, t: Self) -> Self {
(Self::ONE - t) * self + t * other
}
}
impl FloatingScalar for f32 {
const PI: Self = std::f32::consts::PI;
const EPSILON: Self = std::f32::EPSILON;
}
impl FloatingScalar for f64 {
const PI: Self = std::f64::consts::PI;
const EPSILON: Self = std::f64::EPSILON;
}
pub trait Vector2: Copy {
type Scalar: Scalar;
fn x(self) -> Self::Scalar;
fn y(self) -> Self::Scalar;
fn new(x: Self::Scalar, y: Self::Scalar) -> Self;
fn set_x(&mut self, x: Self::Scalar) {
*self = Vector2::new(x, self.y())
}
fn set_y(&mut self, y: Self::Scalar) {
*self = Vector2::new(self.x(), y)
}
fn square(s: Self::Scalar) -> Self {
Self::new(s, s)
}
fn map<V>(self) -> V
where
V: Vector2,
V::Scalar: From<Self::Scalar>,
{
V::new(V::Scalar::from(self.x()), V::Scalar::from(self.y()))
}
fn map_f32(self) -> [f32; 2]
where
f32: From<Self::Scalar>,
{
self.map()
}
fn map_f64(self) -> [f64; 2]
where
f64: From<Self::Scalar>,
{
self.map()
}
fn map_with<V, F>(self, mut f: F) -> V
where
V: Vector2,
F: FnMut(Self::Scalar) -> V::Scalar,
{
V::new(f(self.x()), f(self.y()))
}
fn neg(self) -> Self
where
Self::Scalar: Neg<Output = Self::Scalar>,
{
Self::new(-self.x(), -self.y())
}
fn add<V>(self, other: V) -> Self
where
V: Vector2<Scalar = Self::Scalar>,
{
Self::new(self.x() + other.x(), self.y() + other.y())
}
fn sub<V>(self, other: V) -> Self
where
V: Vector2<Scalar = Self::Scalar>,
{
Self::new(self.x() - other.x(), self.y() - other.y())
}
fn mul(self, by: Self::Scalar) -> Self {
Self::new(self.x() * by, self.y() * by)
}
fn mul2<V>(self, other: V) -> Self
where
V: Vector2<Scalar = Self::Scalar>,
{
Self::new(self.x() * other.x(), self.y() * other.y())
}
fn div(self, by: Self::Scalar) -> Self {
Self::new(self.x() / by, self.y() / by)
}
fn div2<V>(self, other: V) -> Self
where
V: Vector2<Scalar = Self::Scalar>,
{
Self::new(self.x() / other.x(), self.y() / other.y())
}
fn max_dim(self) -> Self::Scalar {
if self.x().abs() > self.y().abs() {
self.x()
} else {
self.y()
}
}
fn min_dim(self) -> Self::Scalar {
if self.x().abs() < self.y().abs() {
self.x()
} else {
self.y()
}
}
fn dot<V>(self, other: V) -> Self::Scalar
where
V: Vector2<Scalar = Self::Scalar>,
{
self.x() * other.x() + self.y() * other.y()
}
}
impl<P> Vector2 for P
where
P: Pair + Copy,
P::Item: Scalar,
{
type Scalar = P::Item;
fn x(self) -> P::Item {
self.first()
}
fn y(self) -> P::Item {
self.second()
}
fn new(x: P::Item, y: P::Item) -> Self {
Self::from_items(x, y)
}
}
pub trait FloatingVector2: Vector2
where
Self::Scalar: FloatingScalar,
{
fn dist<V>(self, to: V) -> Self::Scalar
where
V: Vector2<Scalar = Self::Scalar>,
{
((self.x() - to.x()).pow(Self::Scalar::TWO) + (self.y() - to.y()).pow(Self::Scalar::TWO))
.pow(Self::Scalar::ONE / Self::Scalar::TWO)
}
fn mag(self) -> Self::Scalar {
(self.x().pow(Self::Scalar::TWO) + self.y().pow(Self::Scalar::TWO))
.pow(Self::Scalar::ONE / Self::Scalar::TWO)
}
fn unit(self) -> Self {
let mag = self.mag();
if mag < Self::Scalar::EPSILON {
Self::new(Self::Scalar::ZERO, Self::Scalar::ZERO)
} else {
self.div(mag)
}
}
fn rotate_about<V>(self, pivot: V, radians: Self::Scalar) -> Self
where
V: Vector2<Scalar = Self::Scalar> + Clone,
{
let sin = radians.sin();
let cos = radians.cos();
let origin_point = self.sub(pivot);
let rotated_point = Self::new(
origin_point.x() * cos - origin_point.y() * sin,
origin_point.x() * sin + origin_point.y() * cos,
);
rotated_point.add(pivot)
}
fn lerp<V>(self, other: V, t: Self::Scalar) -> Self
where
V: Vector2<Scalar = Self::Scalar>,
{
Self::new(self.x().lerp(other.x(), t), self.y().lerp(other.y(), t))
}
}
impl<T> FloatingVector2 for T
where
T: Vector2,
T::Scalar: FloatingScalar,
{
}
pub trait Rectangle: Copy {
type Scalar: Scalar;
type Vector: Vector2<Scalar = Self::Scalar>;
fn new(top_left: Self::Vector, size: Self::Vector) -> Self;
fn top_left(self) -> Self::Vector;
fn size(self) -> Self::Vector;
fn square(top_left: Self::Vector, side_length: Self::Scalar) -> Self {
Self::new(top_left, Self::Vector::square(side_length))
}
fn centered(center: Self::Vector, size: Self::Vector) -> Self {
Self::new(center.sub(size.div(Self::Scalar::TWO)), size)
}
fn square_centered(center: Self::Vector, side_length: Self::Scalar) -> Self {
Self::centered(center, Self::Vector::square(side_length))
}
fn map<R>(self) -> R
where
R: Rectangle,
R::Scalar: From<Self::Scalar>,
{
R::new(
R::Vector::new(R::Scalar::from(self.left()), R::Scalar::from(self.top())),
R::Vector::new(
R::Scalar::from(self.width()),
R::Scalar::from(self.height()),
),
)
}
fn map_f32(self) -> [f32; 4]
where
f32: From<Self::Scalar>,
{
self.map()
}
fn map_f64(self) -> [f64; 4]
where
f64: From<Self::Scalar>,
{
self.map()
}
fn map_with<R, F>(self, mut f: F) -> R
where
R: Rectangle,
F: FnMut(Self::Scalar) -> <<R as Rectangle>::Vector as Vector2>::Scalar,
{
R::new(
R::Vector::new(f(self.left()), f(self.top())),
R::Vector::new(f(self.width()), f(self.height())),
)
}
fn abs_size(self) -> Self::Vector {
Self::Vector::new(self.size().x().abs(), self.size().y().abs())
}
fn top_right(self) -> Self::Vector {
Self::Vector::new(self.top_left().x() + self.size().x(), self.top_left().y())
}
fn bottom_left(self) -> Self::Vector {
Self::Vector::new(self.top_left().x(), self.top_left().y() + self.size().y())
}
fn bottom_right(self) -> Self::Vector {
self.top_left().add(self.size())
}
fn abs_top_left(self) -> Self::Vector {
let tl = self.top_left();
let size = self.size();
Self::Vector::new(
tl.x().minn(tl.x() + size.x()),
tl.y().minn(tl.y() + size.y()),
)
}
fn abs_top_right(self) -> Self::Vector {
Self::Vector::new(
self.abs_top_left().x() + self.abs_size().x(),
self.abs_top_left().y(),
)
}
fn abs_bottom_left(self) -> Self::Vector {
Self::Vector::new(
self.abs_top_left().x(),
self.abs_top_left().y() + self.abs_size().y(),
)
}
fn abs_bottom_right(self) -> Self::Vector {
self.abs_top_left().add(self.abs_size())
}
fn top(self) -> Self::Scalar {
self.top_left().y()
}
fn bottom(self) -> Self::Scalar {
self.top_left().y() + self.size().y()
}
fn left(self) -> Self::Scalar {
self.top_left().x()
}
fn right(self) -> Self::Scalar {
self.top_left().x() + self.size().x()
}
fn abs_top(self) -> Self::Scalar {
self.abs_top_left().y()
}
fn abs_bottom(self) -> Self::Scalar {
self.abs_top_left().y() + self.abs_size().y()
}
fn abs_left(self) -> Self::Scalar {
self.abs_top_left().x()
}
fn abs_right(self) -> Self::Scalar {
self.abs_top_left().x() + self.abs_size().x()
}
fn width(self) -> Self::Scalar {
self.size().x()
}
fn height(self) -> Self::Scalar {
self.size().y()
}
fn abs_width(self) -> Self::Scalar {
self.abs_size().x()
}
fn abs_height(self) -> Self::Scalar {
self.abs_size().y()
}
fn center(self) -> Self::Vector {
self.top_left().add(self.size().div(Self::Scalar::TWO))
}
fn with_top_left(self, top_left: Self::Vector) -> Self {
Self::new(top_left, self.size())
}
fn with_center(self, center: Self::Vector) -> Self {
Self::centered(center, self.size())
}
fn with_size(self, size: Self::Vector) -> Self {
Self::new(self.top_left(), size)
}
fn perimeter(self) -> Self::Scalar {
self.width() * Self::Scalar::TWO + self.height() * Self::Scalar::TWO
}
fn area(self) -> Self::Scalar {
self.width() * self.height()
}
fn translated(self, offset: Self::Vector) -> Self {
self.with_top_left(self.top_left().add(offset))
}
fn scaled(self, scale: Self::Scalar) -> Self {
self.with_size(self.size().mul(scale))
}
fn scaled2(self, scale: Self::Vector) -> Self {
self.with_size(self.size().mul2(scale))
}
fn corners(self) -> vec::IntoIter<Self::Vector> {
vec![
self.top_left(),
self.top_right(),
self.bottom_right(),
self.bottom_left(),
]
.into_iter()
}
fn contains(self, point: Self::Vector) -> bool {
let in_x_bounds = self.abs_left() <= point.x() && point.x() <= self.abs_right();
let in_y_bounds = || self.abs_top() <= point.y() && point.y() <= self.abs_bottom();
in_x_bounds && in_y_bounds()
}
fn contains_all<I>(self, points: I) -> bool
where
I: IntoIterator<Item = Self::Vector>,
{
points.into_iter().all(|point| self.contains(point))
}
fn contains_any<I>(self, points: I) -> bool
where
I: IntoIterator<Item = Self::Vector>,
{
points.into_iter().any(|point| self.contains(point))
}
fn bounding<I>(points: I) -> Option<Self>
where
I: IntoIterator<Item = Self::Vector>,
{
let mut points = points.into_iter();
if let Some(first) = points.next() {
let mut tl = first;
let mut br = first;
for point in points {
tl = Self::Vector::new(tl.x().minn(point.x()), tl.y().minn(point.y()));
br = Self::Vector::new(br.x().maxx(point.x()), br.y().maxx(point.y()));
}
Some(Self::new(tl, br.sub(tl)))
} else {
None
}
}
fn inner_margin(self, margin: Self::Scalar) -> Self {
Self::new(
self.abs_top_left().add(margin.square()),
self.abs_size().sub((margin * Self::Scalar::TWO).square()),
)
}
fn inner_margins(self, margins: [Self::Scalar; 4]) -> Self {
let [left, right, top, bottom] = margins;
Self::new(
self.abs_top_left().add(Self::Vector::new(left, top)),
self.abs_size()
.sub(Self::Vector::new(left + right, top + bottom)),
)
}
fn outer_margin(self, margin: Self::Scalar) -> Self {
Self::new(
self.abs_top_left().sub(margin.square()),
self.abs_size().sub((margin * Self::Scalar::TWO).square()),
)
}
fn outer_margins(self, margins: [Self::Scalar; 4]) -> Self {
let [left, right, top, bottom] = margins;
Self::new(
self.abs_top_left().sub(Self::Vector::new(left, top)),
self.abs_size()
.add(Self::Vector::new(left + right, top + bottom)),
)
}
}
impl<P> Rectangle for P
where
P: Pair + Copy,
P::Item: Vector2,
{
type Scalar = <P::Item as Vector2>::Scalar;
type Vector = P::Item;
fn new(top_left: Self::Vector, size: Self::Vector) -> Self {
Self::from_items(top_left, size)
}
fn top_left(self) -> Self::Vector {
self.first()
}
fn size(self) -> Self::Vector {
self.second()
}
}
pub trait Circle: Copy {
type Scalar: FloatingScalar;
type Vector: FloatingVector2<Scalar = Self::Scalar>;
fn new(center: Self::Vector, radius: Self::Scalar) -> Self;
fn center(self) -> Self::Vector;
fn radius(self) -> Self::Scalar;
fn map<C>(self) -> C
where
C: Circle,
C::Scalar: From<Self::Scalar>,
{
C::new(
C::Vector::new(
C::Scalar::from(self.center().x()),
C::Scalar::from(self.center().y()),
),
C::Scalar::from(self.radius()),
)
}
fn map_with<C, F>(self, mut f: F) -> C
where
C: Circle,
F: FnMut(Self::Scalar) -> <<C as Circle>::Vector as Vector2>::Scalar,
{
C::new(
C::Vector::new(f(self.center().x()), f(self.center().y())),
f(self.radius()),
)
}
fn with_center(self, center: Self::Vector) -> Self {
Self::new(center, self.radius())
}
fn with_radius(self, radius: Self::Scalar) -> Self {
Self::new(self.center(), radius)
}
fn diameter(self) -> Self::Scalar {
self.radius() * Self::Scalar::TWO
}
fn circumference(self) -> Self::Scalar {
self.diameter() * Self::Scalar::PI
}
fn area(self) -> Self::Scalar {
self.radius().pow(Self::Scalar::TWO) * Self::Scalar::PI
}
fn translated(self, offset: Self::Vector) -> Self {
self.with_center(self.center().add(offset))
}
fn scaled(self, scale: Self::Scalar) -> Self {
self.with_radius(self.radius() * scale)
}
fn to_square<R>(self) -> R
where
R: Rectangle<Scalar = Self::Scalar, Vector = Self::Vector>,
{
R::new(
self.center().sub(R::Vector::square(self.radius())),
R::Vector::square(self.radius() * R::Scalar::TWO),
)
}
fn contains(self, point: Self::Vector) -> bool {
self.center().dist(point) <= self.radius().abs()
}
fn cntains(self, point: Self::Vector) -> bool {
self.contains(point)
}
fn contains_all<I>(self, points: I) -> bool
where
I: IntoIterator<Item = Self::Vector>,
{
points.into_iter().all(|point| self.contains(point))
}
fn contains_any<I>(self, points: I) -> bool
where
I: IntoIterator<Item = Self::Vector>,
{
points.into_iter().any(|point| self.contains(point))
}
}
impl<S, V> Circle for (V, S)
where
S: FloatingScalar,
V: FloatingVector2<Scalar = S>,
{
type Scalar = S;
type Vector = V;
fn new(center: Self::Vector, radius: Self::Scalar) -> Self {
(center, radius)
}
fn center(self) -> Self::Vector {
self.0
}
fn radius(self) -> Self::Scalar {
self.1
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn margins() {
let rect = [0, 0, 8, 8];
assert!(rect.contains([1, 1]));
assert!(!rect.inner_margin(2).contains([1, 1]));
}
}