arkham/src/geometry.rs

256 lines
5.2 KiB
Rust

use std::ops::{Add, AddAssign, Sub};
/// Pos represents a coordinate position within the termianl screen.
///
/// *NOTE* Most functions accept a value that can be converted into a Pos.
/// For these a simple tuple of coordinates is sufficient.
#[derive(Debug, Clone, Copy)]
pub struct Pos {
pub x: usize,
pub y: usize,
}
impl Pos {
/// Generate a new Pos from a given set of coordinates.
///
/// Example:
///
/// ```
/// use arkham::prelude::*;
/// let pos = Pos::new(3,1);
/// assert_eq!(pos.x, 3);
/// assert_eq!(pos.y, 1);
/// ```
pub fn new(x: usize, y: usize) -> Self {
Self { x, y }
}
}
impl From<(usize, usize)> for Pos {
fn from(value: (usize, usize)) -> Self {
Self {
x: value.0,
y: value.1,
}
}
}
impl From<usize> for Pos {
fn from(value: usize) -> Self {
Self { x: value, y: value }
}
}
impl Add<Pos> for Pos {
type Output = Pos;
fn add(mut self, rhs: Pos) -> Self::Output {
self.x += rhs.x;
self.y += rhs.y;
self
}
}
impl AddAssign<Pos> for Pos {
fn add_assign(&mut self, rhs: Pos) {
self.x += rhs.x;
self.y += rhs.y;
}
}
/// An area that can be operated on.
///
/// ```
/// use arkham::prelude::*;
///
/// let s = Size::new(3,3);
/// assert_eq!(s.width, 3);
/// assert_eq!(s.height, 3);
/// ```
///
/// Sizes can be added and subtracted to mutate them easily:
///
/// ```
/// use arkham::prelude::*;
///
/// let s1 = Size::new(3,3);
/// let s2 = Size::new(0,1);
/// let s = s1 - s2;
/// assert_eq!(s.width, 3);
/// assert_eq!(s.height, 2);
/// ```
#[derive(Debug, Clone, Copy)]
pub struct Size {
pub width: usize,
pub height: usize,
}
impl Size {
pub fn new(width: usize, height: usize) -> Self {
Self { width, height }
}
}
impl Add<Size> for Size {
type Output = Size;
fn add(self, rhs: Size) -> Self::Output {
Self {
width: self.width + rhs.width,
height: self.height + rhs.height,
}
}
}
impl Sub<Size> for Size {
type Output = Size;
fn sub(self, rhs: Size) -> Self::Output {
Self {
width: self.width - rhs.width,
height: self.height - rhs.height,
}
}
}
impl Add<i32> for Size {
type Output = Size;
fn add(self, rhs: i32) -> Self::Output {
Self {
width: self.width + rhs as usize,
height: self.height + rhs as usize,
}
}
}
impl Sub<i32> for Size {
type Output = Size;
fn sub(self, rhs: i32) -> Self::Output {
Self {
width: self.width - rhs as usize,
height: self.height - rhs as usize,
}
}
}
impl From<(usize, usize)> for Size {
fn from(value: (usize, usize)) -> Self {
Self {
width: value.0,
height: value.1,
}
}
}
impl From<(u16, u16)> for Size {
fn from(value: (u16, u16)) -> Self {
Self {
width: value.0 as usize,
height: value.1 as usize,
}
}
}
impl From<(i32, i32)> for Size {
fn from(value: (i32, i32)) -> Self {
Self {
width: value.0 as usize,
height: value.1 as usize,
}
}
}
impl From<i32> for Size {
fn from(value: i32) -> Self {
Self {
width: value as usize,
height: value as usize,
}
}
}
/// An area of the screen with a given size and postiion. The position
/// represents the top-left corner of the rectangle.
#[derive(Debug, Clone, Copy)]
pub struct Rect {
pub pos: Pos,
pub size: Size,
}
impl Rect {
pub fn new<P, S>(pos: P, size: S) -> Self
where
P: Into<Pos>,
S: Into<Size>,
{
Self {
pos: pos.into(),
size: size.into(),
}
}
pub fn zero() -> Self {
Self::new(0, 0)
}
pub fn with_size<S>(size: S) -> Self
where
S: Into<Size>,
{
Rect {
pos: (0, 0).into(),
size: size.into(),
}
}
/// Move the Rect's origin, without chaging its size.
///
/// Example:
///
/// ```
/// use arkham::prelude::*;
///
/// let mut rect = Rect::new((0,0), (15,5));
/// rect.translate(5,0);
/// assert_eq!(rect.pos.x, 5);
/// ```
pub fn translate(&mut self, x: i32, y: i32) {
self.pos.x = (self.pos.x as i32 + x).max(0) as usize;
self.pos.y = (self.pos.y as i32 + y).max(0) as usize;
}
/// Change the Rect's size without altering its position.
///
/// Example:
///
/// ```
/// use arkham::prelude::*;
///
/// let mut rect = Rect::new((0,0), (15,5));
/// rect.expand(5,0);
/// assert_eq!(rect.size.width, 20);
/// ```
pub fn expand(&mut self, width: i32, height: i32) {
self.size.width = (self.size.width as i32 + width).max(1) as usize;
self.size.height = (self.size.height as i32 + height).max(1) as usize;
}
}
impl From<Size> for Rect {
fn from(value: Size) -> Self {
Rect::with_size(value)
}
}
impl<P, S> From<(P, S)> for Rect
where
P: Into<Pos>,
S: Into<Size>,
{
fn from(value: (P, S)) -> Self {
Rect::new(value.0.into(), value.1.into())
}
}