use crate::geometry::Size;
use core::{
convert::{TryFrom, TryInto},
ops::{Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Neg, Sub, SubAssign},
};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl Point {
pub const fn new(x: i32, y: i32) -> Self {
Point { x, y }
}
pub const fn zero() -> Self {
Point { x: 0, y: 0 }
}
pub fn abs(self) -> Self {
Point::new(self.x.abs(), self.y.abs())
}
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point::new(self.x + other.x, self.y + other.y)
}
}
impl Add<Size> for Point {
type Output = Point;
fn add(self, other: Size) -> Point {
let width = other.width as i32;
let height = other.height as i32;
debug_assert!(width >= 0, "width is too large");
debug_assert!(height >= 0, "height is too large");
Point::new(self.x + width, self.y + height)
}
}
impl AddAssign for Point {
fn add_assign(&mut self, other: Point) {
self.x += other.x;
self.y += other.y;
}
}
impl AddAssign<Size> for Point {
fn add_assign(&mut self, other: Size) {
let width = other.width as i32;
let height = other.height as i32;
debug_assert!(width >= 0, "width is too large");
debug_assert!(height >= 0, "height is too large");
self.x += width;
self.y += height;
}
}
impl Sub for Point {
type Output = Point;
fn sub(self, other: Point) -> Point {
Point::new(self.x - other.x, self.y - other.y)
}
}
impl Sub<Size> for Point {
type Output = Point;
fn sub(self, other: Size) -> Point {
let width = other.width as i32;
let height = other.height as i32;
debug_assert!(width >= 0, "width is too large");
debug_assert!(height >= 0, "height is too large");
Point::new(self.x - width, self.y - height)
}
}
impl SubAssign for Point {
fn sub_assign(&mut self, other: Point) {
self.x -= other.x;
self.y -= other.y;
}
}
impl SubAssign<Size> for Point {
fn sub_assign(&mut self, other: Size) {
let width = other.width as i32;
let height = other.height as i32;
debug_assert!(width >= 0, "width is too large");
debug_assert!(height >= 0, "height is too large");
self.x -= width;
self.y -= height;
}
}
impl Mul<i32> for Point {
type Output = Point;
fn mul(self, rhs: i32) -> Point {
Point::new(self.x * rhs, self.y * rhs)
}
}
impl MulAssign<i32> for Point {
fn mul_assign(&mut self, rhs: i32) {
self.x *= rhs;
self.y *= rhs;
}
}
impl Div<i32> for Point {
type Output = Point;
fn div(self, rhs: i32) -> Point {
Point::new(self.x / rhs, self.y / rhs)
}
}
impl DivAssign<i32> for Point {
fn div_assign(&mut self, rhs: i32) {
self.x /= rhs;
self.y /= rhs;
}
}
impl Index<usize> for Point {
type Output = i32;
fn index(&self, idx: usize) -> &i32 {
match idx {
0 => &self.x,
1 => &self.y,
_ => panic!("index out of bounds: the len is 2 but the index is {}", idx),
}
}
}
impl Neg for Point {
type Output = Point;
fn neg(self) -> Self::Output {
Point::new(-self.x, -self.y)
}
}
impl From<(i32, i32)> for Point {
fn from(other: (i32, i32)) -> Self {
Point::new(other.0, other.1)
}
}
impl From<[i32; 2]> for Point {
fn from(other: [i32; 2]) -> Self {
Point::new(other[0], other[1])
}
}
impl From<&[i32; 2]> for Point {
fn from(other: &[i32; 2]) -> Self {
Point::new(other[0], other[1])
}
}
impl From<Point> for (i32, i32) {
fn from(other: Point) -> (i32, i32) {
(other.x, other.y)
}
}
impl From<Point> for [i32; 2] {
fn from(other: Point) -> [i32; 2] {
[other.x, other.y]
}
}
impl From<&Point> for (i32, i32) {
fn from(other: &Point) -> (i32, i32) {
(other.x, other.y)
}
}
impl TryFrom<Point> for (u32, u32) {
type Error = core::num::TryFromIntError;
fn try_from(point: Point) -> Result<Self, Self::Error> {
Ok((point.x.try_into()?, point.y.try_into()?))
}
}
impl TryFrom<(u32, u32)> for Point {
type Error = core::num::TryFromIntError;
fn try_from(point: (u32, u32)) -> Result<Self, Self::Error> {
let x = point.0.try_into()?;
let y = point.1.try_into()?;
Ok(Point::new(x, y))
}
}
impl TryFrom<Point> for [u32; 2] {
type Error = core::num::TryFromIntError;
fn try_from(point: Point) -> Result<Self, Self::Error> {
Ok([point.x.try_into()?, point.y.try_into()?])
}
}
impl TryFrom<[u32; 2]> for Point {
type Error = core::num::TryFromIntError;
fn try_from(point: [u32; 2]) -> Result<Self, Self::Error> {
let x = point[0].try_into()?;
let y = point[1].try_into()?;
Ok(Point::new(x, y))
}
}
impl TryFrom<&[u32; 2]> for Point {
type Error = core::num::TryFromIntError;
fn try_from(point: &[u32; 2]) -> Result<Self, Self::Error> {
let x = point[0].try_into()?;
let y = point[1].try_into()?;
Ok(Point::new(x, y))
}
}
#[cfg(feature = "nalgebra_support")]
use nalgebra::{base::Scalar, Vector2};
#[cfg(feature = "nalgebra_support")]
impl<N> From<Vector2<N>> for Point
where
N: Into<i32> + Scalar,
{
fn from(other: Vector2<N>) -> Self {
Self::new(other[0].into(), other[1].into())
}
}
#[cfg(feature = "nalgebra_support")]
impl<N> From<&Vector2<N>> for Point
where
N: Into<i32> + Scalar,
{
fn from(other: &Vector2<N>) -> Self {
Self::new(other[0].into(), other[1].into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn convert_positive_to_u32_tuple() {
let p = Point::new(10, 20);
let tuple: (u32, u32) = p.try_into().unwrap();
let array: [u32; 2] = p.try_into().unwrap();
assert_eq!(tuple, (10, 20));
assert_eq!(array, [10, 20]);
}
#[test]
fn convert_i32_max_to_u32_tuple() {
let p = Point::new(i32::max_value(), i32::max_value());
let tuple: (u32, u32) = p.try_into().unwrap();
let array: [u32; 2] = p.try_into().unwrap();
assert_eq!(tuple, (2147483647, 2147483647));
assert_eq!(array, [2147483647, 2147483647]);
}
#[test]
fn convert_negative_to_u32_tuple() {
let p = Point::new(-50, -10);
let tuple: Result<(u32, u32), _> = p.try_into();
let array: Result<[u32; 2], _> = p.try_into();
assert!(tuple.is_err());
assert!(array.is_err());
}
#[test]
fn convert_i32_min_to_u32_tuple() {
let p = Point::new(i32::min_value(), i32::min_value());
let tuple: Result<(u32, u32), _> = p.try_into();
let array: Result<[u32; 2], _> = p.try_into();
assert!(tuple.is_err());
assert!(array.is_err());
}
#[test]
fn points_can_be_added() {
let mut left = Point::new(10, 20);
let right = Point::new(30, 40);
assert_eq!(left + right, Point::new(40, 60));
left += right;
assert_eq!(left, Point::new(40, 60));
}
#[test]
fn point_and_size_can_be_added() {
let mut left = Point::new(11, 21);
let right = Size::new(30, 40);
assert_eq!(left + right, Point::new(41, 61));
left += right;
assert_eq!(left, Point::new(41, 61));
}
#[test]
fn points_can_be_subtracted() {
let mut left = Point::new(30, 50);
let right = Point::new(10, 20);
assert_eq!(left - right, Point::new(20, 30));
left -= right;
assert_eq!(left, Point::new(20, 30));
}
#[test]
fn point_and_size_can_be_subtracted() {
let mut left = Point::new(30, 40);
let right = Size::new(11, 22);
assert_eq!(left - right, Point::new(19, 18));
left -= right;
assert_eq!(left, Point::new(19, 18));
}
#[test]
fn points_can_be_negative_after_subtraction() {
let left = Point::new(10, 20);
let right = Point::new(30, 50);
assert_eq!(left - right, Point::new(-20, -30));
let left = Point::new(10, 20);
let right = Size::new(31, 42);
assert_eq!(left - right, Point::new(-21, -22));
}
#[test]
fn points_can_be_multiplied_by_scalar() {
let p = Point::new(1, 2);
assert_eq!(p * 3, Point::new(3, 6));
let mut p = Point::new(3, 4);
p *= -5;
assert_eq!(p, Point::new(-15, -20));
}
#[test]
fn points_can_be_divided_by_scalar() {
let p = Point::new(10, 20);
assert_eq!(p / 2, Point::new(5, 10));
let mut p = Point::new(-10, 10);
p /= -5;
assert_eq!(p, Point::new(2, -2));
}
#[test]
#[should_panic(expected = "width is too large")]
#[cfg(debug_assertions)]
fn too_large_width_can_not_be_added() {
let p = Point::zero();
let _ = p + Size::new(u32::max_value(), 0);
}
#[test]
#[should_panic(expected = "width is too large")]
#[cfg(debug_assertions)]
fn too_large_width_can_not_be_add_assigned() {
let mut p = Point::zero();
p += Size::new(u32::max_value(), 0);
}
#[test]
#[should_panic(expected = "height is too large")]
#[cfg(debug_assertions)]
fn too_large_height_can_not_be_added() {
let p = Point::zero();
let _ = p + Size::new(0, 0x80000000);
}
#[test]
#[should_panic(expected = "height is too large")]
#[cfg(debug_assertions)]
fn too_large_height_can_not_be_add_assigned() {
let mut p = Point::zero();
p += Size::new(0, 0x80000000);
}
#[test]
#[should_panic(expected = "width is too large")]
#[cfg(debug_assertions)]
fn too_large_width_can_not_be_subtracted() {
let p = Point::zero();
let _ = p - Size::new(u32::max_value(), 0);
}
#[test]
#[should_panic(expected = "width is too large")]
#[cfg(debug_assertions)]
fn too_large_width_can_not_be_sub_assigned() {
let mut p = Point::zero();
p -= Size::new(u32::max_value(), 0);
}
#[test]
#[should_panic(expected = "height is too large")]
#[cfg(debug_assertions)]
fn too_large_height_can_not_be_subtracted() {
let p = Point::zero();
let _ = p - Size::new(0, 0x80000000);
}
#[test]
#[should_panic(expected = "height is too large")]
#[cfg(debug_assertions)]
fn too_large_height_can_not_be_sub_assigned() {
let mut p = Point::zero();
p -= Size::new(0, 0x80000000);
}
#[test]
fn from_tuple() {
assert_eq!(Point::from((20i32, 30i32)), Point::new(20, 30));
assert_eq!(Point::from((20i32, 30i32)), Point::new(20, 30));
}
#[test]
fn from_array() {
assert_eq!(Point::from([20i32, 30i32]), Point::new(20, 30));
assert_eq!(Point::from([20i32, 30i32]), Point::new(20, 30));
}
#[test]
fn from_array_ref() {
assert_eq!(Point::from(&[20i32, 30i32]), Point::new(20, 30));
assert_eq!(Point::from(&[20i32, 30i32]), Point::new(20, 30));
}
#[test]
fn neg() {
assert_eq!(-Point::new(10, 20), Point::new(-10, -20));
assert_eq!(-Point::new(-40, -50), Point::new(40, 50));
}
#[test]
fn index() {
let point = Point::new(12, -34);
assert_eq!(point.x, point[0]);
assert_eq!(point.y, point[1]);
}
#[test]
#[should_panic]
fn index_out_of_bounds() {
let point = Point::new(1, 2);
let _ = point[2];
}
#[test]
#[cfg(feature = "nalgebra_support")]
fn nalgebra_support() {
let left = nalgebra::Vector2::new(30, 40);
let right = nalgebra::Vector2::new(10, 20);
assert_eq!(Point::from(left - right), Point::new(20, 20));
}
#[test]
#[cfg(feature = "nalgebra_support")]
fn convert_ref() {
let left = nalgebra::Vector2::new(30, 40);
let right = nalgebra::Vector2::new(10, 20);
let c = left - right;
assert_eq!(Point::from(&c), Point::new(20, 20));
}
}