euclid/
point.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use super::UnknownUnit;
11use approxeq::ApproxEq;
12use length::Length;
13use scale::TypedScale;
14use size::{TypedSize2D, TypedSize3D};
15#[cfg(feature = "mint")]
16use mint;
17use num::*;
18use num_traits::{Float, NumCast};
19use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
20use core::fmt;
21use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
22use core::marker::PhantomData;
23
24/// A 2d Point tagged with a unit.
25#[derive(EuclidMatrix)]
26#[repr(C)]
27pub struct TypedPoint2D<T, U> {
28    pub x: T,
29    pub y: T,
30    #[doc(hidden)]
31    pub _unit: PhantomData<U>,
32}
33
34mint_vec!(TypedPoint2D[x, y] = Point2);
35
36/// Default 2d point type with no unit.
37///
38/// `Point2D` provides the same methods as `TypedPoint2D`.
39pub type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
40
41impl<T: Copy + Zero, U> TypedPoint2D<T, U> {
42    /// Constructor, setting all components to zero.
43    #[inline]
44    pub fn origin() -> Self {
45        point2(Zero::zero(), Zero::zero())
46    }
47
48    #[inline]
49    pub fn zero() -> Self {
50        Self::origin()
51    }
52
53    /// Convert into a 3d point.
54    #[inline]
55    pub fn to_3d(&self) -> TypedPoint3D<T, U> {
56        point3(self.x, self.y, Zero::zero())
57    }
58}
59
60impl<T: fmt::Debug, U> fmt::Debug for TypedPoint2D<T, U> {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        write!(f, "({:?},{:?})", self.x, self.y)
63    }
64}
65
66impl<T: fmt::Display, U> fmt::Display for TypedPoint2D<T, U> {
67    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
68        write!(formatter, "({},{})", self.x, self.y)
69    }
70}
71
72impl<T: Default, U> Default for TypedPoint2D<T, U> {
73    fn default() -> Self {
74        TypedPoint2D::new(Default::default(), Default::default())
75    }
76}
77
78impl<T, U> TypedPoint2D<T, U> {
79    /// Constructor taking scalar values directly.
80    #[inline]
81    pub fn new(x: T, y: T) -> Self {
82        TypedPoint2D {
83            x,
84            y,
85            _unit: PhantomData,
86        }
87    }
88}
89
90impl<T: Copy, U> TypedPoint2D<T, U> {
91    /// Constructor taking properly typed Lengths instead of scalar values.
92    #[inline]
93    pub fn from_lengths(x: Length<T, U>, y: Length<T, U>) -> Self {
94        point2(x.0, y.0)
95    }
96
97    /// Create a 3d point from this one, using the specified z value.
98    #[inline]
99    pub fn extend(&self, z: T) -> TypedPoint3D<T, U> {
100        point3(self.x, self.y, z)
101    }
102
103    /// Cast this point into a vector.
104    ///
105    /// Equivalent to subtracting the origin from this point.
106    #[inline]
107    pub fn to_vector(&self) -> TypedVector2D<T, U> {
108        vec2(self.x, self.y)
109    }
110
111    /// Swap x and y.
112    #[inline]
113    pub fn yx(&self) -> Self {
114        point2(self.y, self.x)
115    }
116
117    /// Returns self.x as a Length carrying the unit.
118    #[inline]
119    pub fn x_typed(&self) -> Length<T, U> {
120        Length::new(self.x)
121    }
122
123    /// Returns self.y as a Length carrying the unit.
124    #[inline]
125    pub fn y_typed(&self) -> Length<T, U> {
126        Length::new(self.y)
127    }
128
129    /// Drop the units, preserving only the numeric value.
130    #[inline]
131    pub fn to_untyped(&self) -> Point2D<T> {
132        point2(self.x, self.y)
133    }
134
135    /// Tag a unitless value with units.
136    #[inline]
137    pub fn from_untyped(p: &Point2D<T>) -> Self {
138        point2(p.x, p.y)
139    }
140
141    #[inline]
142    pub fn to_array(&self) -> [T; 2] {
143        [self.x, self.y]
144    }
145
146    #[inline]
147    pub fn to_tuple(&self) -> (T, T) {
148        (self.x, self.y)
149    }
150}
151
152impl<T: Copy + Add<T, Output = T>, U> TypedPoint2D<T, U> {
153    #[inline]
154    pub fn add_size(&self, other: &TypedSize2D<T, U>) -> Self {
155        point2(self.x + other.width, self.y + other.height)
156    }
157}
158
159impl<T: Copy + Add<T, Output = T>, U> Add<TypedSize2D<T, U>> for TypedPoint2D<T, U> {
160    type Output = Self;
161    #[inline]
162    fn add(self, other: TypedSize2D<T, U>) -> Self {
163        point2(self.x + other.width, self.y + other.height)
164    }
165}
166
167impl<T: Copy + Add<T, Output = T>, U> AddAssign<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
168    #[inline]
169    fn add_assign(&mut self, other: TypedVector2D<T, U>) {
170        *self = *self + other
171    }
172}
173
174impl<T: Copy + Sub<T, Output = T>, U> SubAssign<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
175    #[inline]
176    fn sub_assign(&mut self, other: TypedVector2D<T, U>) {
177        *self = *self - other
178    }
179}
180
181impl<T: Copy + Add<T, Output = T>, U> Add<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
182    type Output = Self;
183    #[inline]
184    fn add(self, other: TypedVector2D<T, U>) -> Self {
185        point2(self.x + other.x, self.y + other.y)
186    }
187}
188
189impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedPoint2D<T, U> {
190    type Output = TypedVector2D<T, U>;
191    #[inline]
192    fn sub(self, other: Self) -> TypedVector2D<T, U> {
193        vec2(self.x - other.x, self.y - other.y)
194    }
195}
196
197impl<T: Copy + Sub<T, Output = T>, U> Sub<TypedVector2D<T, U>> for TypedPoint2D<T, U> {
198    type Output = Self;
199    #[inline]
200    fn sub(self, other: TypedVector2D<T, U>) -> Self {
201        point2(self.x - other.x, self.y - other.y)
202    }
203}
204
205impl<T: Float, U> TypedPoint2D<T, U> {
206    #[inline]
207    pub fn min(self, other: Self) -> Self {
208        point2(self.x.min(other.x), self.y.min(other.y))
209    }
210
211    #[inline]
212    pub fn max(self, other: Self) -> Self {
213        point2(self.x.max(other.x), self.y.max(other.y))
214    }
215
216    #[inline]
217    pub fn clamp(&self, start: Self, end: Self) -> Self {
218        self.max(start).min(end)
219    }
220}
221
222impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedPoint2D<T, U> {
223    type Output = Self;
224    #[inline]
225    fn mul(self, scale: T) -> Self {
226        point2(self.x * scale, self.y * scale)
227    }
228}
229
230impl<T: Copy + Mul<T, Output = T>, U> MulAssign<T> for TypedPoint2D<T, U> {
231    #[inline]
232    fn mul_assign(&mut self, scale: T) {
233        *self = *self * scale
234    }
235}
236
237impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedPoint2D<T, U> {
238    type Output = Self;
239    #[inline]
240    fn div(self, scale: T) -> Self {
241        point2(self.x / scale, self.y / scale)
242    }
243}
244
245impl<T: Copy + Div<T, Output = T>, U> DivAssign<T> for TypedPoint2D<T, U> {
246    #[inline]
247    fn div_assign(&mut self, scale: T) {
248        *self = *self / scale
249    }
250}
251
252impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedPoint2D<T, U1> {
253    type Output = TypedPoint2D<T, U2>;
254    #[inline]
255    fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedPoint2D<T, U2> {
256        point2(self.x * scale.get(), self.y * scale.get())
257    }
258}
259
260impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedPoint2D<T, U2> {
261    type Output = TypedPoint2D<T, U1>;
262    #[inline]
263    fn div(self, scale: TypedScale<T, U1, U2>) -> TypedPoint2D<T, U1> {
264        point2(self.x / scale.get(), self.y / scale.get())
265    }
266}
267
268impl<T: Round, U> TypedPoint2D<T, U> {
269    /// Rounds each component to the nearest integer value.
270    ///
271    /// This behavior is preserved for negative values (unlike the basic cast).
272    /// For example `{ -0.1, -0.8 }.round() == { 0.0, -1.0 }`.
273    #[inline]
274    #[cfg_attr(feature = "unstable", must_use)]
275    pub fn round(&self) -> Self {
276        point2(self.x.round(), self.y.round())
277    }
278}
279
280impl<T: Ceil, U> TypedPoint2D<T, U> {
281    /// Rounds each component to the smallest integer equal or greater than the original value.
282    ///
283    /// This behavior is preserved for negative values (unlike the basic cast).
284    /// For example `{ -0.1, -0.8 }.ceil() == { 0.0, 0.0 }`.
285    #[inline]
286    #[cfg_attr(feature = "unstable", must_use)]
287    pub fn ceil(&self) -> Self {
288        point2(self.x.ceil(), self.y.ceil())
289    }
290}
291
292impl<T: Floor, U> TypedPoint2D<T, U> {
293    /// Rounds each component to the biggest integer equal or lower than the original value.
294    ///
295    /// This behavior is preserved for negative values (unlike the basic cast).
296    /// For example `{ -0.1, -0.8 }.floor() == { -1.0, -1.0 }`.
297    #[inline]
298    #[cfg_attr(feature = "unstable", must_use)]
299    pub fn floor(&self) -> Self {
300        point2(self.x.floor(), self.y.floor())
301    }
302}
303
304impl<T: NumCast + Copy, U> TypedPoint2D<T, U> {
305    /// Cast from one numeric representation to another, preserving the units.
306    ///
307    /// When casting from floating point to integer coordinates, the decimals are truncated
308    /// as one would expect from a simple cast, but this behavior does not always make sense
309    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
310    #[inline]
311    pub fn cast<NewT: NumCast + Copy>(&self) -> TypedPoint2D<NewT, U> {
312        self.try_cast().unwrap()
313    }
314
315    /// Fallible cast from one numeric representation to another, preserving the units.
316    ///
317    /// When casting from floating point to integer coordinates, the decimals are truncated
318    /// as one would expect from a simple cast, but this behavior does not always make sense
319    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
320    pub fn try_cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint2D<NewT, U>> {
321        match (NumCast::from(self.x), NumCast::from(self.y)) {
322            (Some(x), Some(y)) => Some(point2(x, y)),
323            _ => None,
324        }
325    }
326
327    // Convenience functions for common casts
328
329    /// Cast into an `f32` point.
330    #[inline]
331    pub fn to_f32(&self) -> TypedPoint2D<f32, U> {
332        self.cast()
333    }
334
335    /// Cast into an `f64` point.
336    #[inline]
337    pub fn to_f64(&self) -> TypedPoint2D<f64, U> {
338        self.cast()
339    }
340
341    /// Cast into an `usize` point, truncating decimals if any.
342    ///
343    /// When casting from floating point points, it is worth considering whether
344    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
345    /// the desired conversion behavior.
346    #[inline]
347    pub fn to_usize(&self) -> TypedPoint2D<usize, U> {
348        self.cast()
349    }
350
351    /// Cast into an `u32` point, truncating decimals if any.
352    ///
353    /// When casting from floating point points, it is worth considering whether
354    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
355    /// the desired conversion behavior.
356    #[inline]
357    pub fn to_u32(&self) -> TypedPoint2D<u32, U> {
358        self.cast()
359    }
360
361    /// Cast into an i32 point, truncating decimals if any.
362    ///
363    /// When casting from floating point points, it is worth considering whether
364    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
365    /// the desired conversion behavior.
366    #[inline]
367    pub fn to_i32(&self) -> TypedPoint2D<i32, U> {
368        self.cast()
369    }
370
371    /// Cast into an i64 point, truncating decimals if any.
372    ///
373    /// When casting from floating point points, it is worth considering whether
374    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
375    /// the desired conversion behavior.
376    #[inline]
377    pub fn to_i64(&self) -> TypedPoint2D<i64, U> {
378        self.cast()
379    }
380}
381
382impl<T, U> TypedPoint2D<T, U>
383where
384    T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
385{
386    /// Linearly interpolate between this point and another point.
387    ///
388    /// `t` is expected to be between zero and one.
389    #[inline]
390    pub fn lerp(&self, other: Self, t: T) -> Self {
391        let one_t = T::one() - t;
392        point2(one_t * self.x + t * other.x, one_t * self.y + t * other.y)
393    }
394}
395
396impl<T: Copy + ApproxEq<T>, U> ApproxEq<TypedPoint2D<T, U>> for TypedPoint2D<T, U> {
397    #[inline]
398    fn approx_epsilon() -> Self {
399        point2(T::approx_epsilon(), T::approx_epsilon())
400    }
401
402    #[inline]
403    fn approx_eq(&self, other: &Self) -> bool {
404        self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y)
405    }
406
407    #[inline]
408    fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
409        self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
410    }
411}
412
413impl<T: Copy, U> Into<[T; 2]> for TypedPoint2D<T, U> {
414    fn into(self) -> [T; 2] {
415        self.to_array()
416    }
417}
418
419impl<T: Copy, U> From<[T; 2]> for TypedPoint2D<T, U> {
420    fn from(array: [T; 2]) -> Self {
421        point2(array[0], array[1])
422    }
423}
424
425impl<T: Copy, U> Into<(T, T)> for TypedPoint2D<T, U> {
426    fn into(self) -> (T, T) {
427        self.to_tuple()
428    }
429}
430
431impl<T: Copy, U> From<(T, T)> for TypedPoint2D<T, U> {
432    fn from(tuple: (T, T)) -> Self {
433        point2(tuple.0, tuple.1)
434    }
435}
436
437/// A 3d Point tagged with a unit.
438#[derive(EuclidMatrix)]
439#[repr(C)]
440pub struct TypedPoint3D<T, U> {
441    pub x: T,
442    pub y: T,
443    pub z: T,
444    #[doc(hidden)]
445    pub _unit: PhantomData<U>,
446}
447
448mint_vec!(TypedPoint3D[x, y, z] = Point3);
449
450/// Default 3d point type with no unit.
451///
452/// `Point3D` provides the same methods as `TypedPoint3D`.
453pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
454
455impl<T: Copy + Zero, U> TypedPoint3D<T, U> {
456    /// Constructor, setting all components to zero.
457    #[inline]
458    pub fn origin() -> Self {
459        point3(Zero::zero(), Zero::zero(), Zero::zero())
460    }
461
462    #[inline]
463    pub fn zero() -> Self {
464        Self::origin()
465    }
466}
467
468impl<T: Copy + One, U> TypedPoint3D<T, U> {
469    #[inline]
470    pub fn to_array_4d(&self) -> [T; 4] {
471        [self.x, self.y, self.z, One::one()]
472    }
473
474    #[inline]
475    pub fn to_tuple_4d(&self) -> (T, T, T, T) {
476        (self.x, self.y, self.z, One::one())
477    }
478}
479
480impl<T, U> TypedPoint3D<T, U>
481where
482    T: Copy + One + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
483{
484    /// Linearly interpolate between this point and another point.
485    ///
486    /// `t` is expected to be between zero and one.
487    #[inline]
488    pub fn lerp(&self, other: Self, t: T) -> Self {
489        let one_t = T::one() - t;
490        point3(
491            one_t * self.x + t * other.x,
492            one_t * self.y + t * other.y,
493            one_t * self.z + t * other.z,
494        )
495    }
496}
497
498impl<T: fmt::Debug, U> fmt::Debug for TypedPoint3D<T, U> {
499    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
500        write!(f, "({:?},{:?},{:?})", self.x, self.y, self.z)
501    }
502}
503
504impl<T: fmt::Display, U> fmt::Display for TypedPoint3D<T, U> {
505    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
506        write!(f, "({},{},{})", self.x, self.y, self.z)
507    }
508}
509
510impl<T: Copy + Default, U> Default for TypedPoint3D<T, U> {
511    fn default() -> Self {
512        TypedPoint3D::new(Default::default(), Default::default(), Default::default())
513    }
514}
515
516impl<T: Copy, U> TypedPoint3D<T, U> {
517    /// Constructor taking scalar values directly.
518    #[inline]
519    pub fn new(x: T, y: T, z: T) -> Self {
520        TypedPoint3D {
521            x,
522            y,
523            z,
524            _unit: PhantomData,
525        }
526    }
527
528    /// Constructor taking properly typed Lengths instead of scalar values.
529    #[inline]
530    pub fn from_lengths(x: Length<T, U>, y: Length<T, U>, z: Length<T, U>) -> Self {
531        point3(x.0, y.0, z.0)
532    }
533
534    /// Cast this point into a vector.
535    ///
536    /// Equivalent to subtracting the origin to this point.
537    #[inline]
538    pub fn to_vector(&self) -> TypedVector3D<T, U> {
539        vec3(self.x, self.y, self.z)
540    }
541
542    /// Returns a 2d point using this point's x and y coordinates
543    #[inline]
544    pub fn xy(&self) -> TypedPoint2D<T, U> {
545        point2(self.x, self.y)
546    }
547
548    /// Returns a 2d point using this point's x and z coordinates
549    #[inline]
550    pub fn xz(&self) -> TypedPoint2D<T, U> {
551        point2(self.x, self.z)
552    }
553
554    /// Returns a 2d point using this point's x and z coordinates
555    #[inline]
556    pub fn yz(&self) -> TypedPoint2D<T, U> {
557        point2(self.y, self.z)
558    }
559
560    /// Returns self.x as a Length carrying the unit.
561    #[inline]
562    pub fn x_typed(&self) -> Length<T, U> {
563        Length::new(self.x)
564    }
565
566    /// Returns self.y as a Length carrying the unit.
567    #[inline]
568    pub fn y_typed(&self) -> Length<T, U> {
569        Length::new(self.y)
570    }
571
572    /// Returns self.z as a Length carrying the unit.
573    #[inline]
574    pub fn z_typed(&self) -> Length<T, U> {
575        Length::new(self.z)
576    }
577
578    #[inline]
579    pub fn to_array(&self) -> [T; 3] {
580        [self.x, self.y, self.z]
581    }
582
583    #[inline]
584    pub fn to_tuple(&self) -> (T, T, T) {
585        (self.x, self.y, self.z)
586    }
587
588    /// Drop the units, preserving only the numeric value.
589    #[inline]
590    pub fn to_untyped(&self) -> Point3D<T> {
591        point3(self.x, self.y, self.z)
592    }
593
594    /// Tag a unitless value with units.
595    #[inline]
596    pub fn from_untyped(p: &Point3D<T>) -> Self {
597        point3(p.x, p.y, p.z)
598    }
599
600    /// Convert into a 2d point.
601    #[inline]
602    pub fn to_2d(&self) -> TypedPoint2D<T, U> {
603        self.xy()
604    }
605}
606
607impl<T: Copy + Add<T, Output = T>, U> TypedPoint3D<T, U> {
608    #[inline]
609    pub fn add_size(&self, other: &TypedSize3D<T, U>) -> Self {
610        point3(self.x + other.width, self.y + other.height, self.z + other.depth)
611    }
612}
613
614impl<T: Copy + Add<T, Output = T>, U> AddAssign<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
615    #[inline]
616    fn add_assign(&mut self, other: TypedVector3D<T, U>) {
617        *self = *self + other
618    }
619}
620
621impl<T: Copy + Sub<T, Output = T>, U> SubAssign<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
622    #[inline]
623    fn sub_assign(&mut self, other: TypedVector3D<T, U>) {
624        *self = *self - other
625    }
626}
627
628impl<T: Copy + Add<T, Output = T>, U> Add<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
629    type Output = Self;
630    #[inline]
631    fn add(self, other: TypedVector3D<T, U>) -> Self {
632        point3(self.x + other.x, self.y + other.y, self.z + other.z)
633    }
634}
635
636impl<T: Copy + Sub<T, Output = T>, U> Sub for TypedPoint3D<T, U> {
637    type Output = TypedVector3D<T, U>;
638    #[inline]
639    fn sub(self, other: Self) -> TypedVector3D<T, U> {
640        vec3(self.x - other.x, self.y - other.y, self.z - other.z)
641    }
642}
643
644impl<T: Copy + Sub<T, Output = T>, U> Sub<TypedVector3D<T, U>> for TypedPoint3D<T, U> {
645    type Output = Self;
646    #[inline]
647    fn sub(self, other: TypedVector3D<T, U>) -> Self {
648        point3(self.x - other.x, self.y - other.y, self.z - other.z)
649    }
650}
651
652impl<T: Copy + Mul<T, Output = T>, U> Mul<T> for TypedPoint3D<T, U> {
653    type Output = Self;
654    #[inline]
655    fn mul(self, scale: T) -> Self {
656        point3(self.x * scale, self.y * scale, self.z * scale)
657    }
658}
659
660impl<T: Copy + Mul<T, Output = T>, U1, U2> Mul<TypedScale<T, U1, U2>> for TypedPoint3D<T, U1> {
661    type Output = TypedPoint3D<T, U2>;
662    #[inline]
663    fn mul(self, scale: TypedScale<T, U1, U2>) -> TypedPoint3D<T, U2> {
664        point3(self.x * scale.get(), self.y * scale.get(), self.z * scale.get())
665    }
666}
667
668impl<T: Copy + Div<T, Output = T>, U> Div<T> for TypedPoint3D<T, U> {
669    type Output = Self;
670    #[inline]
671    fn div(self, scale: T) -> Self {
672        point3(self.x / scale, self.y / scale, self.z / scale)
673    }
674}
675
676impl<T: Copy + Div<T, Output = T>, U1, U2> Div<TypedScale<T, U1, U2>> for TypedPoint3D<T, U2> {
677    type Output = TypedPoint3D<T, U1>;
678    #[inline]
679    fn div(self, scale: TypedScale<T, U1, U2>) -> TypedPoint3D<T, U1> {
680        point3(self.x / scale.get(), self.y / scale.get(), self.z / scale.get())
681    }
682}
683
684impl<T: Float, U> TypedPoint3D<T, U> {
685    #[inline]
686    pub fn min(self, other: Self) -> Self {
687        point3(
688            self.x.min(other.x),
689            self.y.min(other.y),
690            self.z.min(other.z),
691        )
692    }
693
694    #[inline]
695    pub fn max(self, other: Self) -> Self {
696        point3(
697            self.x.max(other.x),
698            self.y.max(other.y),
699            self.z.max(other.z),
700        )
701    }
702
703    #[inline]
704    pub fn clamp(&self, start: Self, end: Self) -> Self {
705        self.max(start).min(end)
706    }
707}
708
709impl<T: Round, U> TypedPoint3D<T, U> {
710    /// Rounds each component to the nearest integer value.
711    ///
712    /// This behavior is preserved for negative values (unlike the basic cast).
713    #[inline]
714    #[cfg_attr(feature = "unstable", must_use)]
715    pub fn round(&self) -> Self {
716        point3(self.x.round(), self.y.round(), self.z.round())
717    }
718}
719
720impl<T: Ceil, U> TypedPoint3D<T, U> {
721    /// Rounds each component to the smallest integer equal or greater than the original value.
722    ///
723    /// This behavior is preserved for negative values (unlike the basic cast).
724    #[inline]
725    #[cfg_attr(feature = "unstable", must_use)]
726    pub fn ceil(&self) -> Self {
727        point3(self.x.ceil(), self.y.ceil(), self.z.ceil())
728    }
729}
730
731impl<T: Floor, U> TypedPoint3D<T, U> {
732    /// Rounds each component to the biggest integer equal or lower than the original value.
733    ///
734    /// This behavior is preserved for negative values (unlike the basic cast).
735    #[inline]
736    #[cfg_attr(feature = "unstable", must_use)]
737    pub fn floor(&self) -> Self {
738        point3(self.x.floor(), self.y.floor(), self.z.floor())
739    }
740}
741
742impl<T: NumCast + Copy, U> TypedPoint3D<T, U> {
743    /// Cast from one numeric representation to another, preserving the units.
744    ///
745    /// When casting from floating point to integer coordinates, the decimals are truncated
746    /// as one would expect from a simple cast, but this behavior does not always make sense
747    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
748    #[inline]
749    pub fn cast<NewT: NumCast + Copy>(&self) -> TypedPoint3D<NewT, U> {
750        self.try_cast().unwrap()
751    }
752
753    /// Fallible cast from one numeric representation to another, preserving the units.
754    ///
755    /// When casting from floating point to integer coordinates, the decimals are truncated
756    /// as one would expect from a simple cast, but this behavior does not always make sense
757    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
758    #[inline]
759    pub fn try_cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint3D<NewT, U>> {
760        match (
761            NumCast::from(self.x),
762            NumCast::from(self.y),
763            NumCast::from(self.z),
764        ) {
765            (Some(x), Some(y), Some(z)) => Some(point3(x, y, z)),
766            _ => None,
767        }
768    }
769
770    // Convenience functions for common casts
771
772    /// Cast into an `f32` point.
773    #[inline]
774    pub fn to_f32(&self) -> TypedPoint3D<f32, U> {
775        self.cast()
776    }
777
778    /// Cast into an `f64` point.
779    #[inline]
780    pub fn to_f64(&self) -> TypedPoint3D<f64, U> {
781        self.cast()
782    }
783
784    /// Cast into an `usize` point, truncating decimals if any.
785    ///
786    /// When casting from floating point points, it is worth considering whether
787    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
788    /// the desired conversion behavior.
789    #[inline]
790    pub fn to_usize(&self) -> TypedPoint3D<usize, U> {
791        self.cast()
792    }
793
794    /// Cast into an `u32` point, truncating decimals if any.
795    ///
796    /// When casting from floating point points, it is worth considering whether
797    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
798    /// the desired conversion behavior.
799    #[inline]
800    pub fn to_u32(&self) -> TypedPoint3D<u32, U> {
801        self.cast()
802    }
803
804    /// Cast into an `i32` point, truncating decimals if any.
805    ///
806    /// When casting from floating point points, it is worth considering whether
807    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
808    /// the desired conversion behavior.
809    #[inline]
810    pub fn to_i32(&self) -> TypedPoint3D<i32, U> {
811        self.cast()
812    }
813
814    /// Cast into an `i64` point, truncating decimals if any.
815    ///
816    /// When casting from floating point points, it is worth considering whether
817    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
818    /// the desired conversion behavior.
819    #[inline]
820    pub fn to_i64(&self) -> TypedPoint3D<i64, U> {
821        self.cast()
822    }
823}
824
825impl<T: Copy + ApproxEq<T>, U> ApproxEq<TypedPoint3D<T, U>> for TypedPoint3D<T, U> {
826    #[inline]
827    fn approx_epsilon() -> Self {
828        point3(
829            T::approx_epsilon(),
830            T::approx_epsilon(),
831            T::approx_epsilon(),
832        )
833    }
834
835    #[inline]
836    fn approx_eq(&self, other: &Self) -> bool {
837        self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y) && self.z.approx_eq(&other.z)
838    }
839
840    #[inline]
841    fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
842        self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
843            && self.z.approx_eq_eps(&other.z, &eps.z)
844    }
845}
846
847impl<T: Copy, U> Into<[T; 3]> for TypedPoint3D<T, U> {
848    fn into(self) -> [T; 3] {
849        self.to_array()
850    }
851}
852
853impl<T: Copy, U> From<[T; 3]> for TypedPoint3D<T, U> {
854    fn from(array: [T; 3]) -> Self {
855        point3(array[0], array[1], array[2])
856    }
857}
858
859impl<T: Copy, U> Into<(T, T, T)> for TypedPoint3D<T, U> {
860    fn into(self) -> (T, T, T) {
861        self.to_tuple()
862    }
863}
864
865impl<T: Copy, U> From<(T, T, T)> for TypedPoint3D<T, U> {
866    fn from(tuple: (T, T, T)) -> Self {
867        point3(tuple.0, tuple.1, tuple.2)
868    }
869}
870
871pub fn point2<T: Copy, U>(x: T, y: T) -> TypedPoint2D<T, U> {
872    TypedPoint2D::new(x, y)
873}
874
875pub fn point3<T: Copy, U>(x: T, y: T, z: T) -> TypedPoint3D<T, U> {
876    TypedPoint3D::new(x, y, z)
877}
878
879
880#[cfg(test)]
881mod point2d {
882    use super::Point2D;
883    #[cfg(feature = "mint")]
884    use mint;
885
886    #[test]
887    pub fn test_scalar_mul() {
888        let p1: Point2D<f32> = Point2D::new(3.0, 5.0);
889
890        let result = p1 * 5.0;
891
892        assert_eq!(result, Point2D::new(15.0, 25.0));
893    }
894
895    #[test]
896    pub fn test_min() {
897        let p1 = Point2D::new(1.0, 3.0);
898        let p2 = Point2D::new(2.0, 2.0);
899
900        let result = p1.min(p2);
901
902        assert_eq!(result, Point2D::new(1.0, 2.0));
903    }
904
905    #[test]
906    pub fn test_max() {
907        let p1 = Point2D::new(1.0, 3.0);
908        let p2 = Point2D::new(2.0, 2.0);
909
910        let result = p1.max(p2);
911
912        assert_eq!(result, Point2D::new(2.0, 3.0));
913    }
914
915    #[cfg(feature = "mint")]
916    #[test]
917    pub fn test_mint() {
918        let p1 = Point2D::new(1.0, 3.0);
919        let pm: mint::Point2<_> = p1.into();
920        let p2 = Point2D::from(pm);
921
922        assert_eq!(p1, p2);
923    }
924}
925
926#[cfg(test)]
927mod typedpoint2d {
928    use super::{Point2D, TypedPoint2D, point2};
929    use scale::TypedScale;
930    use vector::vec2;
931
932    pub enum Mm {}
933    pub enum Cm {}
934
935    pub type Point2DMm<T> = TypedPoint2D<T, Mm>;
936    pub type Point2DCm<T> = TypedPoint2D<T, Cm>;
937
938    #[test]
939    pub fn test_add() {
940        let p1 = Point2DMm::new(1.0, 2.0);
941        let p2 = vec2(3.0, 4.0);
942
943        let result = p1 + p2;
944
945        assert_eq!(result, Point2DMm::new(4.0, 6.0));
946    }
947
948    #[test]
949    pub fn test_add_assign() {
950        let mut p1 = Point2DMm::new(1.0, 2.0);
951        p1 += vec2(3.0, 4.0);
952
953        assert_eq!(p1, Point2DMm::new(4.0, 6.0));
954    }
955
956    #[test]
957    pub fn test_scalar_mul() {
958        let p1 = Point2DMm::new(1.0, 2.0);
959        let cm_per_mm: TypedScale<f32, Mm, Cm> = TypedScale::new(0.1);
960
961        let result = p1 * cm_per_mm;
962
963        assert_eq!(result, Point2DCm::new(0.1, 0.2));
964    }
965
966    #[test]
967    pub fn test_conv_vector() {
968        use {Point2D, point2};
969
970        for i in 0..100 {
971            // We don't care about these values as long as they are not the same.
972            let x = i as f32 * 0.012345;
973            let y = i as f32 * 0.987654;
974            let p: Point2D<f32> = point2(x, y);
975            assert_eq!(p.to_vector().to_point(), p);
976        }
977    }
978
979    #[test]
980    pub fn test_swizzling() {
981        let p: Point2D<i32> = point2(1, 2);
982        assert_eq!(p.yx(), point2(2, 1));
983    }
984}
985
986#[cfg(test)]
987mod point3d {
988    use super::{Point3D, point2, point3};
989    #[cfg(feature = "mint")]
990    use mint;
991
992    #[test]
993    pub fn test_min() {
994        let p1 = Point3D::new(1.0, 3.0, 5.0);
995        let p2 = Point3D::new(2.0, 2.0, -1.0);
996
997        let result = p1.min(p2);
998
999        assert_eq!(result, Point3D::new(1.0, 2.0, -1.0));
1000    }
1001
1002    #[test]
1003    pub fn test_max() {
1004        let p1 = Point3D::new(1.0, 3.0, 5.0);
1005        let p2 = Point3D::new(2.0, 2.0, -1.0);
1006
1007        let result = p1.max(p2);
1008
1009        assert_eq!(result, Point3D::new(2.0, 3.0, 5.0));
1010    }
1011
1012    #[test]
1013    pub fn test_conv_vector() {
1014        use point3;
1015        for i in 0..100 {
1016            // We don't care about these values as long as they are not the same.
1017            let x = i as f32 * 0.012345;
1018            let y = i as f32 * 0.987654;
1019            let z = x * y;
1020            let p: Point3D<f32> = point3(x, y, z);
1021            assert_eq!(p.to_vector().to_point(), p);
1022        }
1023    }
1024
1025    #[test]
1026    pub fn test_swizzling() {
1027        let p: Point3D<i32> = point3(1, 2, 3);
1028        assert_eq!(p.xy(), point2(1, 2));
1029        assert_eq!(p.xz(), point2(1, 3));
1030        assert_eq!(p.yz(), point2(2, 3));
1031    }
1032
1033    #[cfg(feature = "mint")]
1034    #[test]
1035    pub fn test_mint() {
1036        let p1 = Point3D::new(1.0, 3.0, 5.0);
1037        let pm: mint::Point3<_> = p1.into();
1038        let p2 = Point3D::from(pm);
1039
1040        assert_eq!(p1, p2);
1041    }
1042
1043}