1use std::fmt;
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5use std::str::FromStr;
6
7use crate::utilities::{FormatError, SpanStringTokenizer};
8use crate::{PixelVector, Point, Vector};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
15pub struct PixelPoint {
16 pub x: i32,
18 pub y: i32,
20}
21
22impl PixelPoint {
23 pub const ORIGIN: PixelPoint = PixelPoint::new(0, 0);
25
26 #[inline]
28 pub const fn new(x: i32, y: i32) -> Self {
29 Self { x, y }
30 }
31
32 pub fn parse(s: &str) -> Result<Self, FormatError> {
34 SpanStringTokenizer::with_message(s, "Invalid PixelPoint.")
35 .scope(|t| Ok(PixelPoint::new(t.read_int32()?, t.read_int32()?)))
36 }
37
38 #[inline]
40 pub const fn equals(&self, other: PixelPoint) -> bool {
41 self.x == other.x && self.y == other.y
42 }
43
44 #[inline]
46 pub const fn with_x(&self, x: i32) -> PixelPoint {
47 PixelPoint::new(x, self.y)
48 }
49
50 #[inline]
52 pub const fn with_y(&self, y: i32) -> PixelPoint {
53 PixelPoint::new(self.x, y)
54 }
55
56 #[inline]
59 pub fn to_point(&self, scale: f64) -> Point {
60 Point::new(self.x as f64 / scale, self.y as f64 / scale)
61 }
62
63 #[inline]
66 pub fn to_point_vector(&self, scale: Vector) -> Point {
67 Point::new(self.x as f64 / scale.x, self.y as f64 / scale.y)
68 }
69
70 #[inline]
73 pub fn to_point_with_dpi(&self, dpi: f64) -> Point {
74 self.to_point(dpi / 96.0)
75 }
76
77 #[inline]
80 pub fn to_point_with_dpi_vector(&self, dpi: Vector) -> Point {
81 self.to_point_vector(Vector::new(dpi.x / 96.0, dpi.y / 96.0))
82 }
83
84 #[inline]
86 pub fn from_point(point: Point, scale: f64) -> PixelPoint {
87 PixelPoint::new((point.x * scale) as i32, (point.y * scale) as i32)
88 }
89
90 #[inline]
92 pub fn from_point_vector(point: Point, scale: Vector) -> PixelPoint {
93 PixelPoint::new((point.x * scale.x) as i32, (point.y * scale.y) as i32)
94 }
95
96 #[inline]
98 pub fn from_point_with_dpi(point: Point, dpi: f64) -> PixelPoint {
99 PixelPoint::from_point(point, dpi / 96.0)
100 }
101
102 #[inline]
104 pub fn from_point_with_dpi_vector(point: Point, dpi: Vector) -> PixelPoint {
105 PixelPoint::from_point_vector(point, Vector::new(dpi.x / 96.0, dpi.y / 96.0))
106 }
107}
108
109impl From<PixelPoint> for PixelVector {
111 #[inline]
112 fn from(p: PixelPoint) -> PixelVector {
113 PixelVector::new(p.x, p.y)
114 }
115}
116
117impl Add for PixelPoint {
118 type Output = PixelPoint;
119 #[inline]
120 fn add(self, b: PixelPoint) -> PixelPoint {
121 PixelPoint::new(self.x.wrapping_add(b.x), self.y.wrapping_add(b.y))
122 }
123}
124
125impl Add<PixelVector> for PixelPoint {
126 type Output = PixelPoint;
127 #[inline]
128 fn add(self, b: PixelVector) -> PixelPoint {
129 PixelPoint::new(self.x.wrapping_add(b.x), self.y.wrapping_add(b.y))
130 }
131}
132
133impl Sub for PixelPoint {
134 type Output = PixelPoint;
135 #[inline]
136 fn sub(self, b: PixelPoint) -> PixelPoint {
137 PixelPoint::new(self.x.wrapping_sub(b.x), self.y.wrapping_sub(b.y))
138 }
139}
140
141impl Sub<PixelVector> for PixelPoint {
142 type Output = PixelPoint;
143 #[inline]
144 fn sub(self, b: PixelVector) -> PixelPoint {
145 PixelPoint::new(self.x.wrapping_sub(b.x), self.y.wrapping_sub(b.y))
146 }
147}
148
149impl AddAssign for PixelPoint {
150 #[inline]
151 fn add_assign(&mut self, b: PixelPoint) {
152 *self = *self + b;
153 }
154}
155
156impl AddAssign<PixelVector> for PixelPoint {
157 #[inline]
158 fn add_assign(&mut self, b: PixelVector) {
159 *self = *self + b;
160 }
161}
162
163impl SubAssign for PixelPoint {
164 #[inline]
165 fn sub_assign(&mut self, b: PixelPoint) {
166 *self = *self - b;
167 }
168}
169
170impl SubAssign<PixelVector> for PixelPoint {
171 #[inline]
172 fn sub_assign(&mut self, b: PixelVector) {
173 *self = *self - b;
174 }
175}
176
177impl FromStr for PixelPoint {
178 type Err = FormatError;
179 #[inline]
180 fn from_str(s: &str) -> Result<Self, FormatError> {
181 PixelPoint::parse(s)
182 }
183}
184
185impl fmt::Display for PixelPoint {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "{}, {}", self.x, self.y)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
195
196 #[test]
197 fn parse_display_and_operators() {
198 assert_eq!(
199 PixelPoint::parse("10, -20").unwrap(),
200 PixelPoint::new(10, -20)
201 );
202 assert_eq!(
203 PixelPoint::parse("1.5, 2").unwrap_err().message(),
204 "Invalid PixelPoint."
205 );
206 assert_eq!(PixelPoint::new(10, -20).to_string(), "10, -20");
207 let p = PixelPoint::new(1, 2);
208 assert_eq!(p + PixelPoint::new(1, 1), PixelPoint::new(2, 3));
209 assert_eq!(p + PixelVector::new(1, 1), PixelPoint::new(2, 3));
210 assert_eq!(p - PixelPoint::new(1, 1), PixelPoint::new(0, 1));
211 assert_eq!(p - PixelVector::new(1, 1), PixelPoint::new(0, 1));
212 assert_eq!(PixelVector::from(p), PixelVector::new(1, 2));
213 assert_eq!(
214 PixelPoint::new(i32::MAX, 0) + PixelPoint::new(1, 0),
215 PixelPoint::new(i32::MIN, 0)
216 );
217 }
218
219 #[test]
220 fn conversions_truncate_toward_zero() {
221 assert_eq!(
222 PixelPoint::from_point(Point::new(1.9, -1.9), 1.0),
223 PixelPoint::new(1, -1)
224 );
225 assert_eq!(
226 PixelPoint::from_point_with_dpi(Point::new(10.0, 10.0), 144.0),
227 PixelPoint::new(15, 15)
228 );
229 assert_eq!(
230 PixelPoint::from_point_vector(Point::new(10.0, 10.0), Vector::new(2.0, 3.0)),
231 PixelPoint::new(20, 30)
232 );
233 assert_eq!(
234 PixelPoint::new(15, 30).to_point(1.5),
235 Point::new(10.0, 20.0)
236 );
237 assert_eq!(
238 PixelPoint::new(15, 30).to_point_with_dpi_vector(Vector::new(144.0, 192.0)),
239 Point::new(10.0, 15.0)
240 );
241 }
242}