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