/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 |