Coverage Report

Created: 2025-11-24 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.7.9/src/jzczhz.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 3/2025. All rights reserved.
3
 * //
4
 * // Redistribution and use in source and binary forms, with or without modification,
5
 * // are permitted provided that the following conditions are met:
6
 * //
7
 * // 1.  Redistributions of source code must retain the above copyright notice, this
8
 * // list of conditions and the following disclaimer.
9
 * //
10
 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * // this list of conditions and the following disclaimer in the documentation
12
 * // and/or other materials provided with the distribution.
13
 * //
14
 * // 3.  Neither the name of the copyright holder nor the names of its
15
 * // contributors may be used to endorse or promote products derived from
16
 * // this software without specific prior written permission.
17
 * //
18
 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
use crate::Xyz;
30
use crate::jzazbz::Jzazbz;
31
use num_traits::Pow;
32
use pxfm::{f_atan2f, f_cbrtf, f_hypot3f, f_hypotf, f_powf, f_sincosf, f_sinf};
33
use std::ops::{
34
    Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
35
};
36
37
/// Represents Jzazbz in polar coordinates as Jzczhz
38
#[repr(C)]
39
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
40
pub struct Jzczhz {
41
    /// Jz(lightness) generally expects to be between `0.0..1.0`.
42
    pub jz: f32,
43
    /// Cz generally expects to be between `-1.0..1.0`.
44
    pub cz: f32,
45
    /// Hz generally expects to be between `-1.0..1.0`.
46
    pub hz: f32,
47
}
48
49
impl Jzczhz {
50
    /// Creates new instance of Jzczhz
51
    #[inline]
52
0
    pub fn new(jz: f32, cz: f32, hz: f32) -> Jzczhz {
53
0
        Jzczhz { jz, cz, hz }
54
0
    }
55
56
    /// Converts Jzazbz to polar coordinates Jzczhz
57
    #[inline]
58
0
    pub fn from_jzazbz(jzazbz: Jzazbz) -> Jzczhz {
59
0
        let cz = f_hypotf(jzazbz.az, jzazbz.bz);
60
0
        let hz = f_atan2f(jzazbz.bz, jzazbz.az);
61
0
        Jzczhz::new(jzazbz.jz, cz, hz)
62
0
    }
63
64
    /// Converts Jzczhz into Jzazbz
65
    #[inline]
66
0
    pub fn to_jzazbz(&self) -> Jzazbz {
67
0
        let sincos = f_sincosf(self.hz);
68
0
        let az = self.cz * sincos.1;
69
0
        let bz = self.cz * sincos.0;
70
0
        Jzazbz::new(self.jz, az, bz)
71
0
    }
72
73
    /// Converts Jzczhz into Jzazbz
74
    #[inline]
75
0
    pub fn to_jzazbz_with_luminance(&self) -> Jzazbz {
76
0
        let sincos = f_sincosf(self.hz);
77
0
        let az = self.cz * sincos.1;
78
0
        let bz = self.cz * sincos.0;
79
0
        Jzazbz::new(self.jz, az, bz)
80
0
    }
81
82
    /// Converts Jzczhz to *Xyz*
83
    #[inline]
84
0
    pub fn to_xyz(&self, display_luminance: f32) -> Xyz {
85
0
        let jzazbz = self.to_jzazbz();
86
0
        jzazbz.to_xyz(display_luminance)
87
0
    }
88
89
    /// Converts [Xyz] to [Jzczhz]
90
    #[inline]
91
0
    pub fn from_xyz(xyz: Xyz) -> Jzczhz {
92
0
        let jzazbz = Jzazbz::from_xyz(xyz);
93
0
        Jzczhz::from_jzazbz(jzazbz)
94
0
    }
95
96
    /// Converts [Xyz] to [Jzczhz]
97
    #[inline]
98
0
    pub fn from_xyz_with_display_luminance(xyz: Xyz, luminance: f32) -> Jzczhz {
99
0
        let jzazbz = Jzazbz::from_xyz_with_display_luminance(xyz, luminance);
100
0
        Jzczhz::from_jzazbz(jzazbz)
101
0
    }
102
103
    /// Computes distance for *Jzczhz*
104
    #[inline]
105
0
    pub fn distance(&self, other: Jzczhz) -> f32 {
106
0
        let djz = self.jz - other.jz;
107
0
        let dcz = self.cz - other.cz;
108
0
        let dhz = self.hz - other.hz;
109
0
        let dh = 2. * (self.cz * other.cz).sqrt() * f_sinf(dhz * 0.5);
110
0
        f_hypot3f(djz, dcz, dh)
111
0
    }
112
113
    #[inline]
114
0
    pub fn euclidean_distance(&self, other: Self) -> f32 {
115
0
        let djz = self.jz - other.jz;
116
0
        let dhz = self.hz - other.hz;
117
0
        let dcz = self.cz - other.cz;
118
0
        (djz * djz + dhz * dhz + dcz * dcz).sqrt()
119
0
    }
120
121
    #[inline]
122
0
    pub fn taxicab_distance(&self, other: Self) -> f32 {
123
0
        let djz = self.jz - other.jz;
124
0
        let dhz = self.hz - other.hz;
125
0
        let dcz = self.cz - other.cz;
126
0
        djz.abs() + dhz.abs() + dcz.abs()
127
0
    }
128
}
129
130
impl Index<usize> for Jzczhz {
131
    type Output = f32;
132
133
    #[inline]
134
0
    fn index(&self, index: usize) -> &f32 {
135
0
        match index {
136
0
            0 => &self.jz,
137
0
            1 => &self.cz,
138
0
            2 => &self.hz,
139
0
            _ => panic!("Index out of bounds for Jzczhz"),
140
        }
141
0
    }
142
}
143
144
impl IndexMut<usize> for Jzczhz {
145
    #[inline]
146
0
    fn index_mut(&mut self, index: usize) -> &mut f32 {
147
0
        match index {
148
0
            0 => &mut self.jz,
149
0
            1 => &mut self.cz,
150
0
            2 => &mut self.hz,
151
0
            _ => panic!("Index out of bounds for Jzczhz"),
152
        }
153
0
    }
154
}
155
156
impl Add<f32> for Jzczhz {
157
    type Output = Jzczhz;
158
159
    #[inline]
160
0
    fn add(self, rhs: f32) -> Self::Output {
161
0
        Jzczhz::new(self.jz + rhs, self.cz + rhs, self.hz + rhs)
162
0
    }
163
}
164
165
impl Sub<f32> for Jzczhz {
166
    type Output = Jzczhz;
167
168
    #[inline]
169
0
    fn sub(self, rhs: f32) -> Self::Output {
170
0
        Jzczhz::new(self.jz - rhs, self.cz - rhs, self.hz - rhs)
171
0
    }
172
}
173
174
impl Mul<f32> for Jzczhz {
175
    type Output = Jzczhz;
176
177
    #[inline]
178
0
    fn mul(self, rhs: f32) -> Self::Output {
179
0
        Jzczhz::new(self.jz * rhs, self.cz * rhs, self.hz * rhs)
180
0
    }
181
}
182
183
impl Div<f32> for Jzczhz {
184
    type Output = Jzczhz;
185
186
    #[inline]
187
0
    fn div(self, rhs: f32) -> Self::Output {
188
0
        Jzczhz::new(self.jz / rhs, self.cz / rhs, self.hz / rhs)
189
0
    }
190
}
191
192
impl Add<Jzczhz> for Jzczhz {
193
    type Output = Jzczhz;
194
195
    #[inline]
196
0
    fn add(self, rhs: Jzczhz) -> Self::Output {
197
0
        Jzczhz::new(self.jz + rhs.jz, self.cz + rhs.cz, self.hz + rhs.hz)
198
0
    }
199
}
200
201
impl Sub<Jzczhz> for Jzczhz {
202
    type Output = Jzczhz;
203
204
    #[inline]
205
0
    fn sub(self, rhs: Jzczhz) -> Self::Output {
206
0
        Jzczhz::new(self.jz - rhs.jz, self.cz - rhs.cz, self.hz - rhs.hz)
207
0
    }
208
}
209
210
impl Mul<Jzczhz> for Jzczhz {
211
    type Output = Jzczhz;
212
213
    #[inline]
214
0
    fn mul(self, rhs: Jzczhz) -> Self::Output {
215
0
        Jzczhz::new(self.jz * rhs.jz, self.cz * rhs.cz, self.hz * rhs.hz)
216
0
    }
217
}
218
219
impl Div<Jzczhz> for Jzczhz {
220
    type Output = Jzczhz;
221
222
    #[inline]
223
0
    fn div(self, rhs: Jzczhz) -> Self::Output {
224
0
        Jzczhz::new(self.jz / rhs.jz, self.cz / rhs.cz, self.hz / rhs.hz)
225
0
    }
226
}
227
228
impl AddAssign<Jzczhz> for Jzczhz {
229
    #[inline]
230
0
    fn add_assign(&mut self, rhs: Jzczhz) {
231
0
        self.jz += rhs.jz;
232
0
        self.cz += rhs.cz;
233
0
        self.hz += rhs.hz;
234
0
    }
235
}
236
237
impl SubAssign<Jzczhz> for Jzczhz {
238
    #[inline]
239
0
    fn sub_assign(&mut self, rhs: Jzczhz) {
240
0
        self.jz -= rhs.jz;
241
0
        self.cz -= rhs.cz;
242
0
        self.hz -= rhs.hz;
243
0
    }
244
}
245
246
impl MulAssign<Jzczhz> for Jzczhz {
247
    #[inline]
248
0
    fn mul_assign(&mut self, rhs: Jzczhz) {
249
0
        self.jz *= rhs.jz;
250
0
        self.cz *= rhs.cz;
251
0
        self.hz *= rhs.hz;
252
0
    }
253
}
254
255
impl DivAssign<Jzczhz> for Jzczhz {
256
    #[inline]
257
0
    fn div_assign(&mut self, rhs: Jzczhz) {
258
0
        self.jz /= rhs.jz;
259
0
        self.cz /= rhs.cz;
260
0
        self.hz /= rhs.hz;
261
0
    }
262
}
263
264
impl AddAssign<f32> for Jzczhz {
265
    #[inline]
266
0
    fn add_assign(&mut self, rhs: f32) {
267
0
        self.jz += rhs;
268
0
        self.cz += rhs;
269
0
        self.hz += rhs;
270
0
    }
271
}
272
273
impl SubAssign<f32> for Jzczhz {
274
    #[inline]
275
0
    fn sub_assign(&mut self, rhs: f32) {
276
0
        self.jz -= rhs;
277
0
        self.cz -= rhs;
278
0
        self.hz -= rhs;
279
0
    }
280
}
281
282
impl MulAssign<f32> for Jzczhz {
283
    #[inline]
284
0
    fn mul_assign(&mut self, rhs: f32) {
285
0
        self.jz *= rhs;
286
0
        self.cz *= rhs;
287
0
        self.hz *= rhs;
288
0
    }
289
}
290
291
impl DivAssign<f32> for Jzczhz {
292
    #[inline]
293
0
    fn div_assign(&mut self, rhs: f32) {
294
0
        self.jz /= rhs;
295
0
        self.cz /= rhs;
296
0
        self.hz /= rhs;
297
0
    }
298
}
299
300
impl Jzczhz {
301
    #[inline]
302
0
    pub fn sqrt(&self) -> Jzczhz {
303
0
        Jzczhz::new(self.jz.sqrt(), self.cz.sqrt(), self.hz.sqrt())
304
0
    }
305
306
    #[inline]
307
0
    pub fn cbrt(&self) -> Jzczhz {
308
0
        Jzczhz::new(f_cbrtf(self.jz), f_cbrtf(self.cz), f_cbrtf(self.hz))
309
0
    }
310
}
311
312
impl Pow<f32> for Jzczhz {
313
    type Output = Jzczhz;
314
315
    #[inline]
316
0
    fn pow(self, rhs: f32) -> Self::Output {
317
0
        Jzczhz::new(
318
0
            f_powf(self.jz, rhs),
319
0
            f_powf(self.cz, rhs),
320
0
            f_powf(self.hz, rhs),
321
        )
322
0
    }
323
}
324
325
impl Pow<Jzczhz> for Jzczhz {
326
    type Output = Jzczhz;
327
328
    #[inline]
329
0
    fn pow(self, rhs: Jzczhz) -> Self::Output {
330
0
        Jzczhz::new(
331
0
            f_powf(self.jz, rhs.jz),
332
0
            f_powf(self.cz, self.cz),
333
0
            f_powf(self.hz, self.hz),
334
        )
335
0
    }
336
}
337
338
impl Neg for Jzczhz {
339
    type Output = Jzczhz;
340
341
    #[inline]
342
0
    fn neg(self) -> Self::Output {
343
0
        Jzczhz::new(-self.jz, -self.cz, -self.hz)
344
0
    }
345
}
346
347
#[cfg(test)]
348
mod tests {
349
    use super::*;
350
351
    #[test]
352
    fn jzczhz_round() {
353
        let xyz = Xyz::new(0.5, 0.4, 0.3);
354
        let jzczhz = Jzczhz::from_xyz_with_display_luminance(xyz, 253.);
355
        let old_xyz = jzczhz.to_xyz(253f32);
356
        assert!(
357
            (xyz.x - old_xyz.x).abs() <= 1e-3,
358
            "{:?} != {:?}",
359
            xyz,
360
            old_xyz
361
        );
362
        assert!(
363
            (xyz.y - old_xyz.y).abs() <= 1e-3,
364
            "{:?} != {:?}",
365
            xyz,
366
            old_xyz
367
        );
368
        assert!(
369
            (xyz.z - old_xyz.z).abs() <= 1e-3,
370
            "{:?} != {:?}",
371
            xyz,
372
            old_xyz
373
        );
374
    }
375
}