use std::fmt;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use crate::PixelPoint;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct PixelVector {
pub x: i32,
pub y: i32,
}
impl PixelVector {
#[inline]
pub const fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
#[inline]
pub fn length(&self) -> f64 {
(self
.x
.wrapping_mul(self.x)
.wrapping_add(self.y.wrapping_mul(self.y)) as f64)
.sqrt()
}
#[inline]
pub const fn equals(&self, other: PixelVector) -> bool {
self.x == other.x && self.y == other.y
}
pub fn nearly_equals(&self, other: PixelVector) -> bool {
const TOLERANCE: f32 = f32::from_bits(1);
const OVERFLOW: &str = "Negating the minimum value of a twos complement number is invalid.";
let dx = self.x.wrapping_sub(other.x).checked_abs().expect(OVERFLOW);
let dy = self.y.wrapping_sub(other.y).checked_abs().expect(OVERFLOW);
(dx as f32) < TOLERANCE && (dy as f32) < TOLERANCE
}
#[inline]
pub const fn with_x(&self, x: i32) -> PixelVector {
PixelVector::new(x, self.y)
}
#[inline]
pub const fn with_y(&self, y: i32) -> PixelVector {
PixelVector::new(self.x, y)
}
}
impl From<PixelVector> for PixelPoint {
#[inline]
fn from(a: PixelVector) -> PixelPoint {
PixelPoint::new(a.x, a.y)
}
}
impl Mul for PixelVector {
type Output = i32;
#[inline]
fn mul(self, b: PixelVector) -> i32 {
self.x
.wrapping_mul(b.x)
.wrapping_add(self.y.wrapping_mul(b.y))
}
}
impl Mul<i32> for PixelVector {
type Output = PixelVector;
#[inline]
fn mul(self, scale: i32) -> PixelVector {
PixelVector::new(self.x.wrapping_mul(scale), self.y.wrapping_mul(scale))
}
}
impl Div<i32> for PixelVector {
type Output = PixelVector;
#[inline]
fn div(self, scale: i32) -> PixelVector {
PixelVector::new(self.x / scale, self.y / scale)
}
}
impl Neg for PixelVector {
type Output = PixelVector;
#[inline]
fn neg(self) -> PixelVector {
PixelVector::new(self.x.wrapping_neg(), self.y.wrapping_neg())
}
}
impl Add for PixelVector {
type Output = PixelVector;
#[inline]
fn add(self, b: PixelVector) -> PixelVector {
PixelVector::new(self.x.wrapping_add(b.x), self.y.wrapping_add(b.y))
}
}
impl Sub for PixelVector {
type Output = PixelVector;
#[inline]
fn sub(self, b: PixelVector) -> PixelVector {
PixelVector::new(self.x.wrapping_sub(b.x), self.y.wrapping_sub(b.y))
}
}
impl AddAssign for PixelVector {
#[inline]
fn add_assign(&mut self, b: PixelVector) {
*self = *self + b;
}
}
impl SubAssign for PixelVector {
#[inline]
fn sub_assign(&mut self, b: PixelVector) {
*self = *self - b;
}
}
impl MulAssign<i32> for PixelVector {
#[inline]
fn mul_assign(&mut self, scale: i32) {
*self = *self * scale;
}
}
impl DivAssign<i32> for PixelVector {
#[inline]
fn div_assign(&mut self, scale: i32) {
*self = *self / scale;
}
}
impl fmt::Display for PixelVector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}, {}", self.x, self.y)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arithmetic_and_display() {
let a = PixelVector::new(3, 4);
let b = PixelVector::new(1, 2);
assert_eq!(a * b, 11);
assert_eq!(a * 2, PixelVector::new(6, 8));
assert_eq!(a / 2, PixelVector::new(1, 2));
assert_eq!(-a, PixelVector::new(-3, -4));
assert_eq!(a + b, PixelVector::new(4, 6));
assert_eq!(a - b, PixelVector::new(2, 2));
assert_eq!(a.length(), 5.0);
assert_eq!(a.to_string(), "3, 4");
assert_eq!(PixelPoint::from(a), PixelPoint::new(3, 4));
assert!(a.nearly_equals(a));
assert!(!a.nearly_equals(b));
assert_eq!(a.with_x(9).with_y(8), PixelVector::new(9, 8));
}
#[test]
#[should_panic]
fn division_by_zero_panics() {
let _ = PixelVector::new(1, 1) / 0;
}
}