Skip to main content

las/
transform.rs

1use crate::Result;
2use std::fmt;
3
4/// A scale and an offset that transforms xyz coordinates.
5#[derive(Clone, Copy, Debug, PartialEq)]
6pub struct Transform {
7    /// The scale.
8    pub scale: f64,
9
10    /// The offset.
11    pub offset: f64,
12}
13
14impl Transform {
15    /// Applies this transform to an i32, returning a float.
16    ///
17    /// # Examples
18    ///
19    /// ```
20    /// use las::Transform;
21    /// let transform = Transform { scale: 2., offset: 1. };
22    /// assert_eq!(3., transform.direct(1));
23    /// ```
24    pub fn direct(&self, n: i32) -> f64 {
25        self.scale * f64::from(n) + self.offset
26    }
27
28    /// Applies the inverse transform, and rounds the result.
29    ///
30    /// Returns an error if the resultant value can't be represented as an i32.
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use las::Transform;
36    /// let transform = Transform { scale: 2., offset: 1. };
37    /// assert_eq!(1, transform.inverse(2.9).unwrap());
38    /// ```
39    pub fn inverse(&self, n: f64) -> Result<i32> {
40        self.inverse_with_rounding_mode(n, RoundingMode::Round)
41    }
42
43    pub(crate) fn inverse_with_rounding_mode(&self, n: f64, r: RoundingMode) -> Result<i32> {
44        use crate::Error;
45
46        fn round(n: f64, r: RoundingMode) -> f64 {
47            match r {
48                RoundingMode::Round => n.round(),
49                RoundingMode::Ceil => n.ceil(),
50                RoundingMode::Floor => n.floor(),
51            }
52        }
53
54        let n = round((n - self.offset) / self.scale, r);
55
56        if n > f64::from(i32::MAX) || n < f64::from(i32::MIN) {
57            Err(Error::InvalidInverseTransform {
58                n,
59                transform: *self,
60            })
61        } else {
62            Ok(n as i32)
63        }
64    }
65}
66
67impl Default for Transform {
68    fn default() -> Transform {
69        Transform {
70            scale: 0.001,
71            offset: 0.,
72        }
73    }
74}
75
76impl fmt::Display for Transform {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        write!(f, "`{} * x + {}`", self.scale, self.offset)
79    }
80}
81
82pub(crate) enum RoundingMode {
83    Round,
84    Ceil,
85    Floor,
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn too_large() {
94        let transform = Transform::default();
95        let n = i32::MAX as f64 * transform.scale + 1.;
96        assert!(transform.inverse(n).is_err());
97    }
98
99    #[test]
100    fn too_small() {
101        let transform = Transform::default();
102        let n = i32::MIN as f64 * transform.scale - 1.;
103        assert!(transform.inverse(n).is_err());
104    }
105}