use std::{fmt, iter::Sum}; use std::hash::Hash; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::ResultPoint; /** *

Encapsulates a point of interest in an image containing a barcode. Typically, this * would be the location of a finder pattern or the corner of the barcode, for example.

* * @author Sean Owen */ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, Default)] pub struct PointT { pub x: T, pub y: T, } // #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] // #[derive(Debug, Clone, Copy, Default)] // pub struct Point { // pub(crate) x: f32, // pub(crate) y: f32, // } pub type PointF = PointT; pub type PointI = PointT; pub type PointU = PointT; pub type Point = PointF; impl From for PointI { fn from(val: Point) -> Self { PointI { x: val.x.floor() as i32, y: val.y.floor() as i32, } } } impl From for Point { fn from(val: PointI) -> Self { Point { x: val.x as f32, y: val.y as f32, } } } impl From for PointU { fn from(val: Point) -> Self { PointU { x: val.x.floor() as u32, y: val.y.floor() as u32, } } } impl From for Point { fn from(val: PointU) -> Self { Point { x: val.x as f32, y: val.y as f32, } } } /** An alias for `Point::new`. */ pub const fn point_f(x: f32, y: f32) -> Point { Point::new(x, y) } pub const fn point(x: T, y: T) -> PointT where T: Copy, { PointT::new(x, y) } pub fn point_i>(x: T, y: T) -> Point { Point::new(x.into() as f32, y.into() as f32) } impl Hash for Point { fn hash(&self, state: &mut H) { self.x.to_string().hash(state); self.y.to_string().hash(state); } } impl PartialEq for Point { fn eq(&self, other: &Self) -> bool { self.x == other.x && self.y == other.y } } impl Eq for Point {} impl PointT where T: Copy, { pub const fn new(x: T, y: T) -> PointT { PointT { x, y } } pub const fn with_single(x: T) -> Self { Self { x, y: x } } } impl std::ops::AddAssign for PointT where T: std::ops::Add + Copy, { fn add_assign(&mut self, rhs: Self) { self.x = self.x + rhs.x; self.y = self.y + rhs.y; } } impl std::ops::SubAssign for PointT where T: std::ops::Sub + Copy, { fn sub_assign(&mut self, rhs: Self) { self.x = self.x - rhs.x; self.y = self.y - rhs.y; } } impl<'a, T> Sum<&'a PointT> for PointT where T: std::ops::Add + 'a + Default, PointT: std::ops::Add> + Copy, { fn sum>(iter: I) -> Self { iter.fold(Self::default(), |acc, &p| acc + p) } } /** This impl is temporary and is there to ease refactoring. */ impl ResultPoint for PointT where T: Into + Copy, { fn getX(&self) -> f32 { self.x.into() } fn getY(&self) -> f32 { self.y.into() } fn to_rxing_result_point(&self) -> PointT { PointT { x: self.x.into(), y: self.y.into(), } } } impl fmt::Display for PointT where T: std::fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({},{})", self.x, self.y) } } impl std::ops::Sub for PointT where T: std::ops::Sub + Copy, { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { Self::new(self.x - rhs.x, self.y - rhs.y) } } impl std::ops::Neg for PointT where T: std::ops::Neg + Copy, { type Output = Self; fn neg(self) -> Self::Output { Self::new(-self.x, -self.y) } } impl std::ops::Add for PointT where T: std::ops::Add + Copy, { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self::new(self.x + rhs.x, self.y + rhs.y) } } impl std::ops::Add for PointT where T: Into + std::ops::Add + Copy, { type Output = Self; fn add(self, rhs: f32) -> Self::Output { Self::new(self.x + rhs, self.y + rhs) } } impl std::ops::Add> for f32 where T: std::ops::Add, { type Output = Point; fn add(self, rhs: PointT) -> Self::Output { Point::new(rhs.x + self, rhs.y + self) } } impl std::ops::Mul for PointT where T: std::ops::Mul + Copy, { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { Self::new(self.x * rhs.x, self.y * rhs.y) } } impl std::ops::Mul for Point { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { Self::new(self.x * rhs, self.y * rhs) } } impl std::ops::Mul for Point { type Output = Self; fn mul(self, rhs: i32) -> Self::Output { Self::new(self.x * rhs as f32, self.y * rhs as f32) } } impl std::ops::Mul for Point { type Output = Self; fn mul(self, rhs: u32) -> Self::Output { Self::new(self.x * rhs as f32, self.y * rhs as f32) } } impl std::ops::Mul for i32 { type Output = Point; fn mul(self, rhs: Point) -> Self::Output { Self::Output::new(rhs.x * self as f32, rhs.y * self as f32) } } impl std::ops::Mul for f32 { type Output = Point; fn mul(self, rhs: Point) -> Self::Output { Self::Output::new(rhs.x * self, rhs.y * self) } } impl std::ops::Div for Point { type Output = Point; fn div(self, rhs: f32) -> Self::Output { Self::Output::new(self.x / rhs, self.y / rhs) } } impl std::ops::Mul for u32 { type Output = Point; fn mul(self, rhs: Point) -> Self::Output { Self::Output::new(rhs.x * self as f32, rhs.y * self as f32) } } impl PointT where T: std::ops::Mul + std::ops::Sub + num::traits::real::Real, PointT: std::ops::Div>, { pub fn dot(self, p: Self) -> T { self.x * p.x + self.y * p.y } pub fn cross(self, p: Self) -> T { self.x * p.y - p.x * self.y } /// L1 norm pub fn sumAbsComponent(self) -> T { self.x.abs() + self.y.abs() } /// L2 norm pub fn length(self) -> T { self.x.hypot(self.y) } /// L-inf norm pub fn maxAbsComponent(self) -> T { self.x.abs().max(self.y.abs()) // f32::max(self.x.abs(), self.y.abs()) } pub fn squaredDistance(self, p: Self) -> T { let diff = self - p; diff.x * diff.x + diff.y * diff.y } pub fn distance(self, p: Self) -> T { (self - p).length() } pub fn abs(self) -> Self { Self::new(self.x.abs(), self.y.abs()) } pub fn fold U>(self, f: F) -> U { f(self.x, self.y) } pub fn middle(self, p: Self) -> Self where T: From, { (self + p) / 2.into() } pub fn normalized(self) -> Self { self / Self::length(self) } pub fn bresenhamDirection(self) -> Self { self / Self::maxAbsComponent(self) } pub fn mainDirection(self) -> Self where T: From, { if self.x.abs() > self.y.abs() { Self::new(self.x, 0.into()) } else { Self::new(0.into(), self.y) } } pub fn round(self) -> Self { Self { x: self.x.round(), y: self.y.round(), } } /// Calculate a floating point pixel coordinate representing the 'center' of the pixel. /// This is sort of the inverse operation of the PointI(PointF) conversion constructor. /// See also the documentation of the GridSampler API. #[inline(always)] pub fn centered(self) -> PointT where T: Into, { PointT::new(self.x.floor().into() + 0.5, self.y.floor().into() + 0.5) } pub fn floor(self) -> Self { Self { x: self.x.floor(), y: self.y.floor(), } } /** * Returns the z component of the cross product between vectors BC and BA. */ pub fn crossProductZ(a: PointT, b: PointT, c: PointT) -> T { ((c.x - b.x) * (a.y - b.y)) - ((c.y - b.y) * (a.x - b.x)) } } impl From<(i32, i32)> for Point { fn from((x, y): (i32, i32)) -> Self { Self::new(x as f32, y as f32) } } impl From<(u32, u32)> for Point { fn from((x, y): (u32, u32)) -> Self { Self::new(x as f32, y as f32) } } impl From<(f32, f32)> for PointI { fn from((x, y): (f32, f32)) -> Self { PointI { x: x.floor() as i32, y: y.floor() as i32, } } } impl From<(T, T)> for PointT { fn from((x, y): (T, T)) -> PointT { PointT { x, y } } } impl From<&(T, T)> for PointT where T: Copy, { fn from(&(x, y): &(T, T)) -> PointT { PointT { x, y } } } impl From for PointT where T: Copy, { fn from(value: T) -> Self { Self::with_single(value) } } #[cfg(test)] mod tests { use super::Point; #[test] fn testDistance() { assert_eq!( (8.0f32).sqrt(), Point::new(1.0, 2.0).distance(Point::new(3.0, 4.0)) ); assert_eq!(0.0, Point::new(1.0, 2.0).distance(Point::new(1.0, 2.0))); assert_eq!( (8.0f32).sqrt(), Point::new(1.0, 2.0).distance(Point::new(3.0, 4.0)) ); assert_eq!(0.0, Point::new(1.0, 2.0).distance(Point::new(1.0, 2.0))); } }