Coverage Report

Created: 2026-08-13 08:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.2/src/math.rs
Line
Count
Source
1
// calculations inspired by
2
// https://github.com/AcademySoftwareFoundation/openexr/blob/master/OpenEXR/IlmImf/ImfTiledMisc.cpp
3
4
//! Simple math utilities.
5
6
use std::{
7
    convert::TryFrom,
8
    fmt::Debug,
9
    ops::{Add, Div, Mul, Sub},
10
};
11
12
use crate::error::{i32_to_usize, Result};
13
14
/// Simple two-dimensional vector of any numerical type.
15
/// Supports only few mathematical operations
16
/// as this is used mainly as data struct.
17
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
18
pub struct Vec2<T>(pub T, pub T);
19
20
impl<T> Vec2<T> {
21
    /// Returns the vector with the maximum of either coordinates.
22
0
    pub fn max(self, other: Self) -> Self
23
0
    where
24
0
        T: Ord,
25
    {
26
0
        Self(self.0.max(other.0), self.1.max(other.1))
27
0
    }
28
29
    /// Returns the vector with the minimum of either coordinates.
30
0
    pub fn min(self, other: Self) -> Self
31
0
    where
32
0
        T: Ord,
33
    {
34
0
        Self(self.0.min(other.0), self.1.min(other.1))
35
0
    }
36
37
    /// Try to convert all components of this vector to a new type,
38
    /// yielding either a vector of that new type, or an error.
39
0
    pub fn try_from<S>(value: Vec2<S>) -> std::result::Result<Self, T::Error>
40
0
    where
41
0
        T: TryFrom<S>,
42
    {
43
0
        let x = T::try_from(value.0)?;
44
0
        let y = T::try_from(value.1)?;
45
0
        Ok(Self(x, y))
46
0
    }
47
48
    /// Seeing this vector as a dimension or size (width and height),
49
    /// this returns the area that this dimensions contains (`width * height`).
50
    #[inline]
51
0
    pub fn area(self) -> T
52
0
    where
53
0
        T: std::ops::Mul<T, Output = T>,
54
    {
55
0
        self.0 * self.1
56
0
    }
57
58
    /// The first component of this 2D vector.
59
    #[inline]
60
0
    pub fn x(self) -> T {
61
0
        self.0
62
0
    }
Unexecuted instantiation: <exr::math::Vec2<usize>>::x
Unexecuted instantiation: <exr::math::Vec2<i32>>::x
Unexecuted instantiation: <exr::math::Vec2<i64>>::x
Unexecuted instantiation: <exr::math::Vec2<f32>>::x
Unexecuted instantiation: <exr::math::Vec2<f32>>::x
63
64
    /// The second component of this 2D vector.
65
    #[inline]
66
0
    pub fn y(self) -> T {
67
0
        self.1
68
0
    }
Unexecuted instantiation: <exr::math::Vec2<usize>>::y
Unexecuted instantiation: <exr::math::Vec2<i32>>::y
Unexecuted instantiation: <exr::math::Vec2<i64>>::y
Unexecuted instantiation: <exr::math::Vec2<f32>>::y
Unexecuted instantiation: <exr::math::Vec2<f32>>::y
69
70
    /// The first component of this 2D vector.
71
    #[inline]
72
0
    pub fn width(self) -> T {
73
0
        self.0
74
0
    }
75
76
    /// The second component of this 2D vector.
77
    #[inline]
78
0
    pub fn height(self) -> T {
79
0
        self.1
80
0
    }
81
82
    // TODO use this!
83
    /// Convert this two-dimensional coordinate to an index suited for
84
    /// one-dimensional flattened image arrays. Works for images that store
85
    /// the pixels row by row, one after another, in a single array.
86
    /// In debug mode, panics for an index out of bounds.
87
    #[inline]
88
0
    pub fn flat_index_for_size(self, resolution: Self) -> T
89
0
    where
90
0
        T: Copy + Debug + Ord + Mul<Output = T> + Add<Output = T>,
91
    {
92
0
        debug_assert!(
93
0
            self.x() < resolution.width() && self.y() < resolution.height(),
94
0
            "Vec2 index {:?} is invalid for resolution {:?}",
95
            self,
96
            resolution
97
        );
98
99
0
        let Self(x, y) = self;
100
0
        y * resolution.width() + x
101
0
    }
102
}
103
104
impl Vec2<i32> {
105
    /// Try to convert to [`Vec2<usize>`], returning an error on negative
106
    /// numbers.
107
0
    pub fn to_usize(self, error_message: &'static str) -> Result<Vec2<usize>> {
108
0
        let x = i32_to_usize(self.0, error_message)?;
109
0
        let y = i32_to_usize(self.1, error_message)?;
110
0
        Ok(Vec2(x, y))
111
0
    }
112
}
113
114
impl Vec2<usize> {
115
    /// Panics for too large values
116
0
    pub fn to_i32(self) -> Vec2<i32> {
117
0
        let x = i32::try_from(self.0).expect("vector x coordinate too large");
118
0
        let y = i32::try_from(self.1).expect("vector y coordinate too large");
119
0
        Vec2(x, y)
120
0
    }
121
}
122
123
impl<T: std::ops::Add<T>> std::ops::Add<Self> for Vec2<T> {
124
    type Output = Vec2<T::Output>;
125
126
0
    fn add(self, other: Self) -> Self::Output {
127
0
        Vec2(self.0 + other.0, self.1 + other.1)
128
0
    }
Unexecuted instantiation: <exr::math::Vec2<usize> as core::ops::arith::Add>::add
Unexecuted instantiation: <exr::math::Vec2<i32> as core::ops::arith::Add>::add
129
}
130
131
impl<T: std::ops::Sub<T>> std::ops::Sub<Self> for Vec2<T> {
132
    type Output = Vec2<T::Output>;
133
134
0
    fn sub(self, other: Self) -> Self::Output {
135
0
        Vec2(self.0 - other.0, self.1 - other.1)
136
0
    }
137
}
138
139
impl<T: std::ops::Div<T>> std::ops::Div<Self> for Vec2<T> {
140
    type Output = Vec2<T::Output>;
141
142
0
    fn div(self, other: Self) -> Self::Output {
143
0
        Vec2(self.0 / other.0, self.1 / other.1)
144
0
    }
145
}
146
147
impl<T: std::ops::Mul<T>> std::ops::Mul<Self> for Vec2<T> {
148
    type Output = Vec2<T::Output>;
149
150
0
    fn mul(self, other: Self) -> Self::Output {
151
0
        Vec2(self.0 * other.0, self.1 * other.1)
152
0
    }
153
}
154
155
impl<T> std::ops::Neg for Vec2<T>
156
where
157
    T: std::ops::Neg<Output = T>,
158
{
159
    type Output = Self;
160
161
0
    fn neg(self) -> Self::Output {
162
0
        Self(-self.0, -self.1)
163
0
    }
164
}
165
166
impl<T> From<(T, T)> for Vec2<T> {
167
0
    fn from((x, y): (T, T)) -> Self {
168
0
        Self(x, y)
169
0
    }
Unexecuted instantiation: <exr::math::Vec2<_> as core::convert::From<(_, _)>>::from
Unexecuted instantiation: <exr::math::Vec2<usize> as core::convert::From<(usize, usize)>>::from
Unexecuted instantiation: <exr::math::Vec2<usize> as core::convert::From<(usize, usize)>>::from
170
}
171
172
impl<T> From<Vec2<T>> for (T, T) {
173
0
    fn from(vec2: Vec2<T>) -> Self {
174
0
        (vec2.0, vec2.1)
175
0
    }
176
}
177
178
/// Computes `floor(log(x)/log(2))`. Returns 0 where argument is 0.
179
// TODO does rust std not provide this?
180
0
pub(crate) const fn floor_log_2(mut number: u32) -> u32 {
181
0
    let mut log = 0;
182
183
    // TODO check if this unrolls properly?
184
0
    while number > 1 {
185
0
        log += 1;
186
0
        number >>= 1;
187
0
    }
188
189
0
    log
190
0
}
191
192
/// Computes `ceil(log(x)/log(2))`. Returns 0 where argument is 0.
193
// taken from https://github.com/openexr/openexr/blob/master/OpenEXR/IlmImf/ImfTiledMisc.cpp
194
// TODO does rust std not provide this?
195
0
pub(crate) const fn ceil_log_2(mut number: u32) -> u32 {
196
0
    let mut log = 0;
197
0
    let mut round_up = 0;
198
199
    // TODO check if this unrolls properly
200
0
    while number > 1 {
201
0
        if number & 1 != 0 {
202
0
            round_up = 1;
203
0
        }
204
205
0
        log += 1;
206
0
        number >>= 1;
207
    }
208
209
0
    log + round_up
210
0
}
211
212
/// Round up or down in specific calculations.
213
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
214
pub enum RoundingMode {
215
    /// Round down.
216
    Down,
217
218
    /// Round up.
219
    Up,
220
}
221
222
impl RoundingMode {
223
0
    pub(crate) const fn log2(self, number: u32) -> u32 {
224
0
        match self {
225
0
            Self::Down => self::floor_log_2(number),
226
0
            Self::Up => self::ceil_log_2(number),
227
        }
228
0
    }
229
230
    /// Only works for positive numbers.
231
0
    pub(crate) fn divide<T>(self, dividend: T, divisor: T) -> T
232
0
    where
233
0
        T: Copy
234
0
            + Add<Output = T>
235
0
            + Sub<Output = T>
236
0
            + Div<Output = T>
237
0
            + From<u8>
238
0
            + std::cmp::PartialOrd,
239
    {
240
0
        assert!(
241
0
            dividend >= T::from(0) && divisor >= T::from(1),
242
            "division with rounding up only works for positive numbers"
243
        );
244
245
0
        match self {
246
0
            Self::Up => (dividend + divisor - T::from(1_u8)) / divisor, // only works for
247
            // positive numbers
248
0
            Self::Down => dividend / divisor,
249
        }
250
0
    }
251
}
252
253
// TODO log2 tests