embedded_graphics_core/geometry/
point.rs

1use core::{
2    convert::{TryFrom, TryInto},
3    fmt,
4    ops::{Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Neg, Sub, SubAssign},
5};
6
7use crate::geometry::Size;
8
9/// 2D point.
10///
11/// A point can be used to define the position of a graphics object. For example, a [`Rectangle`]
12/// may be defined that has its top left corner at `(-1, -2)`. To specify the size of an object
13/// [`Size`] should be used instead.
14///
15/// [Nalgebra] support can be enabled with the `nalgebra_support` feature. This implements
16/// `From<Vector2<N>>` and `From<&Vector2<N>>` where `N` is `Scalar + Into<i32>`. This allows use
17/// of Nalgebra's [`Vector2`] with embedded-graphics where `i8`, `i16`, `i32`, `u16` or `u8` is used
18/// for value storage.
19///
20/// # Examples
21///
22/// ## Create a `Point` from two integers
23///
24/// ```rust
25/// use embedded_graphics::geometry::Point;
26///
27/// // Create a coord using the `new` constructor method
28/// let p = Point::new(10, 20);
29/// ```
30///
31/// ## Create a `Point` from a Nalgebra `Vector2`
32///
33/// _Be sure to enable the `nalgebra_support` feature to get [Nalgebra] integration._
34///
35/// ```rust
36/// # #[cfg(feature = "nalgebra_support")] {
37/// use embedded_graphics::geometry::Point;
38/// use nalgebra::Vector2;
39///
40/// let n_coord = Vector2::new(10i32, 20);
41///
42/// assert_eq!(Point::from(n_coord), Point::new(10, 20));
43/// # }
44/// ```
45///
46/// ## Convert a `Vector2<u8>` into a `Point`
47///
48/// _Be sure to enable the `nalgebra_support` feature to get [Nalgebra] integration._
49///
50/// Smaller unsigned types that can be converted to `i32` are also supported in conversions.
51///
52/// ```rust
53/// # #[cfg(feature = "nalgebra_support")] {
54/// use embedded_graphics::geometry::Point;
55/// use nalgebra::Vector2;
56///
57/// let n_coord = Vector2::new(10u8, 20);
58///
59/// assert_eq!(Point::from(n_coord), Point::new(10, 20));
60/// # }
61/// ```
62///
63/// [`Rectangle`]: crate::primitives::rectangle::Rectangle
64/// [`Vector2<N>`]: https://docs.rs/nalgebra/0.18.0/nalgebra/base/type.Vector2.html
65/// [`Vector2`]: https://docs.rs/nalgebra/0.18.0/nalgebra/base/type.Vector2.html
66/// [Nalgebra]: https://docs.rs/nalgebra
67#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
68#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
69pub struct Point {
70    /// The x coordinate.
71    pub x: i32,
72
73    /// The y coordinate.
74    pub y: i32,
75}
76
77impl Point {
78    /// Creates a point from  X and Y coordinates.
79    pub const fn new(x: i32, y: i32) -> Self {
80        Point { x, y }
81    }
82
83    /// Creates a point with X and Y values set to an equal value.
84    ///
85    /// # Examples
86    ///
87    /// ```rust
88    /// use embedded_graphics::geometry::Point;
89    ///
90    /// let point = Point::new_equal(11);
91    ///
92    /// assert_eq!(point, Point { x: 11, y: 11 });
93    /// ```
94    pub const fn new_equal(value: i32) -> Self {
95        Point { x: value, y: value }
96    }
97
98    /// Creates a point with X and Y equal to zero.
99    pub const fn zero() -> Self {
100        Point { x: 0, y: 0 }
101    }
102
103    /// Returns a point with equal `x` value and `y` set to `0`.
104    ///
105    /// # Examples
106    ///
107    /// ## Move a `Point` along the X axis.
108    ///
109    /// ```rust
110    /// use embedded_graphics::geometry::Point;
111    ///
112    /// let translate = Point::new(20, 30);
113    ///
114    /// let point = Point::new(10, 15);
115    ///
116    /// let moved_x = point + translate.x_axis();
117    ///
118    /// assert_eq!(moved_x, Point::new(30, 15));
119    /// ```
120    pub const fn x_axis(self) -> Self {
121        Self { x: self.x, y: 0 }
122    }
123
124    /// Returns a point with equal `y` value and `x` set to `0`.
125    ///
126    /// # Examples
127    ///
128    /// ## Move a `Point` along the Y axis.
129    ///
130    /// ```rust
131    /// use embedded_graphics::geometry::Point;
132    ///
133    /// let translate = Point::new(20, 30);
134    ///
135    /// let point = Point::new(10, 15);
136    ///
137    /// let moved_y = point + translate.y_axis();
138    ///
139    /// assert_eq!(moved_y, Point::new(10, 45));
140    /// ```
141    pub const fn y_axis(self) -> Self {
142        Self { x: 0, y: self.y }
143    }
144
145    /// Remove the sign from a coordinate
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// # use embedded_graphics::geometry::Point;
151    /// #
152    /// let point = Point::new(-5, -10);
153    ///
154    /// assert_eq!(point.abs(), Point::new(5, 10));
155    /// ```
156    pub const fn abs(self) -> Self {
157        Point::new(self.x.abs(), self.y.abs())
158    }
159
160    /// Offsets a point by subtracting a size.
161    ///
162    /// This method provides a workaround for the `Sub` trait not being usable in `const` contexts.
163    ///
164    /// # Panics
165    ///
166    /// This function will panic if `width` or `height` are too large to be represented as an `i32`
167    /// and debug assertions are enabled.
168    pub(crate) const fn sub_size(self, other: Size) -> Point {
169        let width = other.width as i32;
170        let height = other.height as i32;
171
172        debug_assert!(width >= 0, "width is too large");
173        debug_assert!(height >= 0, "height is too large");
174
175        Point::new(self.x - width, self.y - height)
176    }
177
178    /// Returns the componentwise minimum of two `Point`s
179    ///
180    /// # Examples
181    ///
182    /// ```rust
183    /// use embedded_graphics::geometry::Point;
184    ///
185    /// let min = Point::new(20, 30).component_min(Point::new(15, 50));
186    ///
187    /// assert_eq!(min, Point::new(15, 30));
188    /// ```
189    pub fn component_min(self, other: Self) -> Self {
190        Self::new(self.x.min(other.x), self.y.min(other.y))
191    }
192
193    /// Returns the componentwise maximum of two `Point`s
194    ///
195    /// # Examples
196    ///
197    /// ```rust
198    /// use embedded_graphics::geometry::Point;
199    ///
200    /// let min = Point::new(20, 30).component_max(Point::new(15, 50));
201    ///
202    /// assert_eq!(min, Point::new(20, 50));
203    /// ```
204    pub fn component_max(self, other: Self) -> Self {
205        Self::new(self.x.max(other.x), self.y.max(other.y))
206    }
207
208    /// Returns the componentwise multiplication of two `Point`s.
209    ///
210    /// ```rust
211    /// use embedded_graphics::geometry::Point;
212    ///
213    /// let result = Point::new(20, 30).component_mul(Point::new(-2, 3));
214    ///
215    /// assert_eq!(result, Point::new(-40, 90));
216    /// ```
217    pub const fn component_mul(self, other: Self) -> Self {
218        Self::new(self.x * other.x, self.y * other.y)
219    }
220
221    /// Returns the componentwise division of two `Points`s.
222    ///
223    /// # Panics
224    ///
225    /// Panics if one of the components of `other` equals zero.
226    ///
227    /// ```rust
228    /// use embedded_graphics::geometry::Point;
229    ///
230    /// let result = Point::new(20, 30).component_div(Point::new(10, -3));
231    ///
232    /// assert_eq!(result, Point::new(2, -10));
233    /// ```
234    pub const fn component_div(self, other: Self) -> Self {
235        Self::new(self.x / other.x, self.y / other.y)
236    }
237}
238
239impl Add for Point {
240    type Output = Point;
241
242    fn add(self, other: Point) -> Point {
243        Point::new(self.x + other.x, self.y + other.y)
244    }
245}
246
247impl Add<Size> for Point {
248    type Output = Point;
249
250    /// Offsets a point by adding a size.
251    ///
252    /// # Panics
253    ///
254    /// This function will panic if `width` or `height` are too large to be represented as an `i32`
255    /// and debug assertions are enabled.
256    fn add(self, other: Size) -> Point {
257        let width = other.width as i32;
258        let height = other.height as i32;
259
260        debug_assert!(width >= 0, "width is too large");
261        debug_assert!(height >= 0, "height is too large");
262
263        Point::new(self.x + width, self.y + height)
264    }
265}
266
267impl AddAssign for Point {
268    fn add_assign(&mut self, other: Point) {
269        self.x += other.x;
270        self.y += other.y;
271    }
272}
273
274impl AddAssign<Size> for Point {
275    /// Offsets a point by adding a size.
276    ///
277    /// # Panics
278    ///
279    /// This function will panic if `width` or `height` are too large to be represented as an `i32`
280    /// and debug assertions are enabled.
281    fn add_assign(&mut self, other: Size) {
282        let width = other.width as i32;
283        let height = other.height as i32;
284
285        debug_assert!(width >= 0, "width is too large");
286        debug_assert!(height >= 0, "height is too large");
287
288        self.x += width;
289        self.y += height;
290    }
291}
292
293impl Sub for Point {
294    type Output = Point;
295
296    fn sub(self, other: Point) -> Point {
297        Point::new(self.x - other.x, self.y - other.y)
298    }
299}
300
301impl Sub<Size> for Point {
302    type Output = Point;
303
304    /// Offsets a point by subtracting a size.
305    ///
306    /// # Panics
307    ///
308    /// This function will panic if `width` or `height` are too large to be represented as an `i32`
309    /// and debug assertions are enabled.
310    fn sub(self, other: Size) -> Point {
311        self.sub_size(other)
312    }
313}
314
315impl SubAssign for Point {
316    fn sub_assign(&mut self, other: Point) {
317        self.x -= other.x;
318        self.y -= other.y;
319    }
320}
321
322impl SubAssign<Size> for Point {
323    /// Offsets a point by subtracting a size.
324    ///
325    /// # Panics
326    ///
327    /// This function will panic if `width` or `height` are too large to be represented as an `i32`
328    /// and debug assertions are enabled.
329    fn sub_assign(&mut self, other: Size) {
330        let width = other.width as i32;
331        let height = other.height as i32;
332
333        debug_assert!(width >= 0, "width is too large");
334        debug_assert!(height >= 0, "height is too large");
335
336        self.x -= width;
337        self.y -= height;
338    }
339}
340
341impl Mul<i32> for Point {
342    type Output = Point;
343
344    fn mul(self, rhs: i32) -> Point {
345        Point::new(self.x * rhs, self.y * rhs)
346    }
347}
348
349impl MulAssign<i32> for Point {
350    fn mul_assign(&mut self, rhs: i32) {
351        self.x *= rhs;
352        self.y *= rhs;
353    }
354}
355
356impl Div<i32> for Point {
357    type Output = Point;
358
359    fn div(self, rhs: i32) -> Point {
360        Point::new(self.x / rhs, self.y / rhs)
361    }
362}
363
364impl DivAssign<i32> for Point {
365    fn div_assign(&mut self, rhs: i32) {
366        self.x /= rhs;
367        self.y /= rhs;
368    }
369}
370
371impl Index<usize> for Point {
372    type Output = i32;
373
374    fn index(&self, idx: usize) -> &i32 {
375        match idx {
376            0 => &self.x,
377            1 => &self.y,
378            _ => panic!("index out of bounds: the len is 2 but the index is {}", idx),
379        }
380    }
381}
382
383impl Neg for Point {
384    type Output = Point;
385
386    fn neg(self) -> Self::Output {
387        Point::new(-self.x, -self.y)
388    }
389}
390
391impl From<(i32, i32)> for Point {
392    fn from(other: (i32, i32)) -> Self {
393        Point::new(other.0, other.1)
394    }
395}
396
397impl From<[i32; 2]> for Point {
398    fn from(other: [i32; 2]) -> Self {
399        Point::new(other[0], other[1])
400    }
401}
402
403impl From<&[i32; 2]> for Point {
404    fn from(other: &[i32; 2]) -> Self {
405        Point::new(other[0], other[1])
406    }
407}
408
409impl From<Point> for (i32, i32) {
410    fn from(other: Point) -> (i32, i32) {
411        (other.x, other.y)
412    }
413}
414
415impl From<Point> for [i32; 2] {
416    fn from(other: Point) -> [i32; 2] {
417        [other.x, other.y]
418    }
419}
420
421impl From<&Point> for (i32, i32) {
422    fn from(other: &Point) -> (i32, i32) {
423        (other.x, other.y)
424    }
425}
426
427impl TryFrom<Point> for (u32, u32) {
428    type Error = core::num::TryFromIntError;
429
430    fn try_from(point: Point) -> Result<Self, Self::Error> {
431        Ok((point.x.try_into()?, point.y.try_into()?))
432    }
433}
434
435impl TryFrom<(u32, u32)> for Point {
436    type Error = core::num::TryFromIntError;
437
438    fn try_from(point: (u32, u32)) -> Result<Self, Self::Error> {
439        let x = point.0.try_into()?;
440        let y = point.1.try_into()?;
441
442        Ok(Point::new(x, y))
443    }
444}
445
446impl TryFrom<Point> for [u32; 2] {
447    type Error = core::num::TryFromIntError;
448
449    fn try_from(point: Point) -> Result<Self, Self::Error> {
450        Ok([point.x.try_into()?, point.y.try_into()?])
451    }
452}
453
454impl TryFrom<[u32; 2]> for Point {
455    type Error = core::num::TryFromIntError;
456
457    fn try_from(point: [u32; 2]) -> Result<Self, Self::Error> {
458        let x = point[0].try_into()?;
459        let y = point[1].try_into()?;
460
461        Ok(Point::new(x, y))
462    }
463}
464
465impl TryFrom<&[u32; 2]> for Point {
466    type Error = core::num::TryFromIntError;
467
468    fn try_from(point: &[u32; 2]) -> Result<Self, Self::Error> {
469        let x = point[0].try_into()?;
470        let y = point[1].try_into()?;
471
472        Ok(Point::new(x, y))
473    }
474}
475
476impl fmt::Display for Point {
477    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478        write!(f, "{}, {}", self.x, self.y)
479    }
480}
481
482#[cfg(feature = "nalgebra_support")]
483use nalgebra::{base::Scalar, Vector2};
484
485#[cfg(feature = "nalgebra_support")]
486impl<N> From<Vector2<N>> for Point
487where
488    N: Into<i32> + Scalar + Copy,
489{
490    fn from(other: Vector2<N>) -> Self {
491        Self::new(other[0].into(), other[1].into())
492    }
493}
494
495#[cfg(feature = "nalgebra_support")]
496impl<N> From<&Vector2<N>> for Point
497where
498    N: Into<i32> + Scalar + Copy,
499{
500    fn from(other: &Vector2<N>) -> Self {
501        Self::new(other[0].into(), other[1].into())
502    }
503}
504
505#[cfg(test)]
506mod tests {
507    use super::*;
508
509    use core::fmt::Write;
510
511    #[test]
512    fn convert_positive_to_u32_tuple() {
513        let p = Point::new(10, 20);
514
515        let tuple: (u32, u32) = p.try_into().unwrap();
516        let array: [u32; 2] = p.try_into().unwrap();
517
518        assert_eq!(tuple, (10, 20));
519        assert_eq!(array, [10, 20]);
520    }
521
522    #[test]
523    fn convert_i32_max_to_u32_tuple() {
524        let p = Point::new(i32::max_value(), i32::max_value());
525
526        let tuple: (u32, u32) = p.try_into().unwrap();
527        let array: [u32; 2] = p.try_into().unwrap();
528
529        // Literal value taken from https://doc.rust-lang.org/std/primitive.i32.html#method.max_value
530        assert_eq!(tuple, (2147483647, 2147483647));
531        assert_eq!(array, [2147483647, 2147483647]);
532    }
533
534    #[test]
535    fn convert_negative_to_u32_tuple() {
536        let p = Point::new(-50, -10);
537
538        let tuple: Result<(u32, u32), _> = p.try_into();
539        let array: Result<[u32; 2], _> = p.try_into();
540
541        assert!(tuple.is_err());
542        assert!(array.is_err());
543    }
544
545    #[test]
546    fn convert_i32_min_to_u32_tuple() {
547        let p = Point::new(i32::min_value(), i32::min_value());
548
549        let tuple: Result<(u32, u32), _> = p.try_into();
550        let array: Result<[u32; 2], _> = p.try_into();
551
552        assert!(tuple.is_err());
553        assert!(array.is_err());
554    }
555
556    #[test]
557    fn points_can_be_added() {
558        let mut left = Point::new(10, 20);
559        let right = Point::new(30, 40);
560
561        assert_eq!(left + right, Point::new(40, 60));
562
563        left += right;
564        assert_eq!(left, Point::new(40, 60));
565    }
566
567    #[test]
568    fn point_and_size_can_be_added() {
569        let mut left = Point::new(11, 21);
570        let right = Size::new(30, 40);
571
572        assert_eq!(left + right, Point::new(41, 61));
573
574        left += right;
575        assert_eq!(left, Point::new(41, 61));
576    }
577
578    #[test]
579    fn points_can_be_subtracted() {
580        let mut left = Point::new(30, 50);
581        let right = Point::new(10, 20);
582
583        assert_eq!(left - right, Point::new(20, 30));
584
585        left -= right;
586        assert_eq!(left, Point::new(20, 30));
587    }
588
589    #[test]
590    fn point_and_size_can_be_subtracted() {
591        let mut left = Point::new(30, 40);
592        let right = Size::new(11, 22);
593
594        assert_eq!(left - right, Point::new(19, 18));
595
596        left -= right;
597        assert_eq!(left, Point::new(19, 18));
598    }
599
600    #[test]
601    fn points_can_be_negative_after_subtraction() {
602        let left = Point::new(10, 20);
603        let right = Point::new(30, 50);
604
605        assert_eq!(left - right, Point::new(-20, -30));
606
607        let left = Point::new(10, 20);
608        let right = Size::new(31, 42);
609
610        assert_eq!(left - right, Point::new(-21, -22));
611    }
612
613    #[test]
614    fn points_can_be_multiplied_by_scalar() {
615        let p = Point::new(1, 2);
616        assert_eq!(p * 3, Point::new(3, 6));
617
618        let mut p = Point::new(3, 4);
619        p *= -5;
620        assert_eq!(p, Point::new(-15, -20));
621    }
622
623    #[test]
624    fn points_can_be_divided_by_scalar() {
625        let p = Point::new(10, 20);
626        assert_eq!(p / 2, Point::new(5, 10));
627
628        let mut p = Point::new(-10, 10);
629        p /= -5;
630        assert_eq!(p, Point::new(2, -2));
631    }
632
633    #[test]
634    #[should_panic(expected = "width is too large")]
635    #[cfg(debug_assertions)]
636    fn too_large_width_can_not_be_added() {
637        let p = Point::zero();
638        let _ = p + Size::new(u32::max_value(), 0);
639    }
640
641    #[test]
642    #[should_panic(expected = "width is too large")]
643    #[cfg(debug_assertions)]
644    fn too_large_width_can_not_be_add_assigned() {
645        let mut p = Point::zero();
646        p += Size::new(u32::max_value(), 0);
647    }
648
649    #[test]
650    #[should_panic(expected = "height is too large")]
651    #[cfg(debug_assertions)]
652    fn too_large_height_can_not_be_added() {
653        let p = Point::zero();
654        let _ = p + Size::new(0, 0x80000000);
655    }
656
657    #[test]
658    #[should_panic(expected = "height is too large")]
659    #[cfg(debug_assertions)]
660    fn too_large_height_can_not_be_add_assigned() {
661        let mut p = Point::zero();
662        p += Size::new(0, 0x80000000);
663    }
664
665    #[test]
666    #[should_panic(expected = "width is too large")]
667    #[cfg(debug_assertions)]
668    fn too_large_width_can_not_be_subtracted() {
669        let p = Point::zero();
670        let _ = p - Size::new(u32::max_value(), 0);
671    }
672
673    #[test]
674    #[should_panic(expected = "width is too large")]
675    #[cfg(debug_assertions)]
676    fn too_large_width_can_not_be_sub_assigned() {
677        let mut p = Point::zero();
678        p -= Size::new(u32::max_value(), 0);
679    }
680
681    #[test]
682    #[should_panic(expected = "height is too large")]
683    #[cfg(debug_assertions)]
684    fn too_large_height_can_not_be_subtracted() {
685        let p = Point::zero();
686        let _ = p - Size::new(0, 0x80000000);
687    }
688
689    #[test]
690    #[should_panic(expected = "height is too large")]
691    #[cfg(debug_assertions)]
692    fn too_large_height_can_not_be_sub_assigned() {
693        let mut p = Point::zero();
694        p -= Size::new(0, 0x80000000);
695    }
696
697    #[test]
698    fn from_tuple() {
699        assert_eq!(Point::from((20i32, 30i32)), Point::new(20, 30));
700        assert_eq!(Point::from((20i32, 30i32)), Point::new(20, 30));
701    }
702
703    #[test]
704    fn from_array() {
705        assert_eq!(Point::from([20i32, 30i32]), Point::new(20, 30));
706        assert_eq!(Point::from([20i32, 30i32]), Point::new(20, 30));
707    }
708
709    #[test]
710    fn from_array_ref() {
711        assert_eq!(Point::from(&[20i32, 30i32]), Point::new(20, 30));
712        assert_eq!(Point::from(&[20i32, 30i32]), Point::new(20, 30));
713    }
714
715    #[test]
716    fn neg() {
717        assert_eq!(-Point::new(10, 20), Point::new(-10, -20));
718        assert_eq!(-Point::new(-40, -50), Point::new(40, 50));
719    }
720
721    #[test]
722    fn index() {
723        let point = Point::new(12, -34);
724
725        assert_eq!(point.x, point[0]);
726        assert_eq!(point.y, point[1]);
727    }
728
729    #[test]
730    #[should_panic]
731    fn index_out_of_bounds() {
732        let point = Point::new(1, 2);
733        let _ = point[2];
734    }
735
736    #[test]
737    #[cfg(feature = "nalgebra_support")]
738    fn nalgebra_support() {
739        let left = nalgebra::Vector2::new(30, 40);
740        let right = nalgebra::Vector2::new(10, 20);
741
742        assert_eq!(Point::from(left - right), Point::new(20, 20));
743    }
744
745    #[test]
746    #[cfg(feature = "nalgebra_support")]
747    fn convert_ref() {
748        let left = nalgebra::Vector2::new(30, 40);
749        let right = nalgebra::Vector2::new(10, 20);
750
751        let c = left - right;
752
753        assert_eq!(Point::from(&c), Point::new(20, 20));
754    }
755
756    #[test]
757    fn component_min_max() {
758        let a = Point::new(20, 30);
759        let b = Point::new(15, 50);
760
761        assert_eq!(a.component_min(b), Point::new(15, 30));
762        assert_eq!(a.component_max(b), Point::new(20, 50));
763    }
764
765    #[test]
766    fn display() {
767        let mut buffer = arrayvec::ArrayString::<32>::new();
768        write!(buffer, "{}", Point::new(123, -456)).unwrap();
769
770        assert_eq!(&buffer, "123, -456");
771    }
772}