1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use crate::Result;
use std::fmt;

/// A scale and an offset that transforms xyz coordinates.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform {
    /// The scale.
    pub scale: f64,
    /// The offset.
    pub offset: f64,
}

impl Transform {
    /// Applies this transform to an i32, returning a float.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Transform;
    /// let transform = Transform { scale: 2., offset: 1. };
    /// assert_eq!(3., transform.direct(1));
    /// ```
    pub fn direct(&self, n: i32) -> f64 {
        self.scale * f64::from(n) + self.offset
    }

    /// Applies the inverse transform, and rounds the result.
    ///
    /// Returns an error if the resultant value can't be represented as an i32.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Transform;
    /// let transform = Transform { scale: 2., offset: 1. };
    /// assert_eq!(1, transform.inverse(2.9).unwrap());
    /// ```
    pub fn inverse(&self, n: f64) -> Result<i32> {
        use crate::Error;
        use std::i32;

        let n = ((n - self.offset) / self.scale).round();
        if n > f64::from(i32::MAX) || n < f64::from(i32::MIN) {
            Err(Error::InverseTransform {
                n,
                transform: *self,
            })
        } else {
            Ok(n as i32)
        }
    }
}

impl Default for Transform {
    fn default() -> Transform {
        Transform {
            scale: 0.001,
            offset: 0.,
        }
    }
}

impl fmt::Display for Transform {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "`{} * x + {}`", self.scale, self.offset)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::i32;

    #[test]
    fn too_large() {
        let transform = Transform::default();
        let n = i32::MAX as f64 * transform.scale + 1.;
        assert!(transform.inverse(n).is_err());
    }

    #[test]
    fn too_small() {
        let transform = Transform::default();
        let n = i32::MIN as f64 * transform.scale - 1.;
        assert!(transform.inverse(n).is_err());
    }
}