colorutils_rs/
jzczhz.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7use crate::{EuclideanDistance, Jzazbz, Rgb, TaxicabDistance, TransferFunction, Xyz};
8use erydanos::{eatan2f, ehypot3f, ehypotf, Cosine, Sine};
9use num_traits::Pow;
10use std::ops::{
11    Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
12};
13
14/// Represents Jzazbz in polar coordinates as Jzczhz
15#[repr(C)]
16#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
17pub struct Jzczhz {
18    /// Jz(lightness) generally expects to be between [0;1]
19    pub jz: f32,
20    /// Cz generally expects to be between [-1;1]
21    pub cz: f32,
22    /// Hz generally expects to be between [-1;1]
23    pub hz: f32,
24}
25
26impl Jzczhz {
27    /// Creates new instance of Jzczhz
28    #[inline]
29    pub fn new(jz: f32, cz: f32, hz: f32) -> Jzczhz {
30        Jzczhz { jz, cz, hz }
31    }
32
33    /// Converts Rgb to polar coordinates Jzczhz
34    ///
35    /// # Arguments
36    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
37    #[inline]
38    pub fn from_rgb(rgb: Rgb<u8>, transfer_function: TransferFunction) -> Jzczhz {
39        let jzazbz = rgb.to_jzazbz(transfer_function);
40        Jzczhz::from_jzazbz(jzazbz)
41    }
42
43    /// Converts Rgb to polar coordinates Jzczhz
44    ///
45    /// # Arguments
46    /// `display_luminance` - display luminance
47    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
48    #[inline]
49    pub fn from_rgb_with_luminance(
50        rgb: Rgb<u8>,
51        display_luminance: f32,
52        transfer_function: TransferFunction,
53    ) -> Jzczhz {
54        let jzazbz = rgb.to_jzazbz_with_luminance(display_luminance, transfer_function);
55        Jzczhz::from_jzazbz(jzazbz)
56    }
57
58    /// Converts Jzazbz to polar coordinates Jzczhz
59    #[inline]
60    pub fn from_jzazbz(jzazbz: Jzazbz) -> Jzczhz {
61        let cz = ehypotf(jzazbz.az, jzazbz.bz);
62        let hz = eatan2f(jzazbz.bz, jzazbz.az);
63        Jzczhz::new(jzazbz.jz, cz, hz)
64    }
65
66    /// Converts Jzczhz into Jzazbz
67    #[inline]
68    pub fn to_jzazbz(&self) -> Jzazbz {
69        let az = self.cz * self.hz.ecos();
70        let bz = self.cz * self.hz.esin();
71        Jzazbz::new(self.jz, az, bz)
72    }
73
74    /// Converts Jzczhz into Jzazbz
75    #[inline]
76    pub fn to_jzazbz_with_luminance(&self, display_luminance: f32) -> Jzazbz {
77        let az = self.cz * self.hz.ecos();
78        let bz = self.cz * self.hz.esin();
79        Jzazbz::new_with_luminance(self.jz, az, bz, display_luminance)
80    }
81
82    /// Converts Jzczhz to Rgb
83    ///
84    /// # Arguments
85    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
86    #[inline]
87    pub fn to_rgb(&self, transfer_function: TransferFunction) -> Rgb<u8> {
88        let jzazbz = self.to_jzazbz();
89        jzazbz.to_rgb(transfer_function)
90    }
91
92    /// Converts Jzczhz to Rgb
93    ///
94    /// # Arguments
95    /// `display_luminance` - display luminance
96    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
97    #[inline]
98    pub fn to_rgb_with_luminance(
99        &self,
100        display_luminance: f32,
101        transfer_function: TransferFunction,
102    ) -> Rgb<u8> {
103        let jzazbz = self.to_jzazbz_with_luminance(display_luminance);
104        jzazbz.to_rgb(transfer_function)
105    }
106
107    /// Converts [Jzczhz] to linear [Rgb]
108    ///
109    /// # Arguments
110    /// `display_luminance` - display luminance
111    /// `transfer_function` - Transfer function to convert into linear colorspace and backwards
112    #[inline]
113    pub fn to_linear_rgb_with_luminance(&self, display_luminance: f32) -> Rgb<f32> {
114        let jzazbz = self.to_jzazbz_with_luminance(display_luminance);
115        jzazbz.to_linear_rgb()
116    }
117
118    /// Converts Jzczhz to *Xyz*
119    #[inline]
120    pub fn to_xyz(&self) -> Xyz {
121        let jzazbz = self.to_jzazbz();
122        jzazbz.to_xyz()
123    }
124
125    /// Converts [Xyz] to [Jzczhz]
126    #[inline]
127    pub fn from_xyz(xyz: Xyz) -> Jzczhz {
128        let jzazbz = Jzazbz::from_xyz(xyz);
129        Jzczhz::from_jzazbz(jzazbz)
130    }
131
132    /// Converts [Xyz] to [Jzczhz]
133    #[inline]
134    pub fn from_xyz_with_display_luminance(xyz: Xyz, luminance: f32) -> Jzczhz {
135        let jzazbz = Jzazbz::from_xyz_with_display_luminance(xyz, luminance);
136        Jzczhz::from_jzazbz(jzazbz)
137    }
138
139    /// Computes distance for *Jzczhz*
140    #[inline]
141    pub fn distance(&self, other: Jzczhz) -> f32 {
142        let djz = self.jz - other.jz;
143        let dcz = self.cz - other.cz;
144        let dhz = self.hz - other.hz;
145        let dh = 2f32 * (self.cz * other.cz).sqrt() * (dhz * 0.5f32).esin();
146        ehypot3f(djz, dcz, dh)
147    }
148}
149
150impl EuclideanDistance for Jzczhz {
151    #[inline]
152    fn euclidean_distance(&self, other: Self) -> f32 {
153        let djz = self.jz - other.jz;
154        let dhz = self.hz - other.hz;
155        let dcz = self.cz - other.cz;
156        (djz * djz + dhz * dhz + dcz * dcz).sqrt()
157    }
158}
159
160impl TaxicabDistance for Jzczhz {
161    #[inline]
162    fn taxicab_distance(&self, other: Self) -> f32 {
163        let djz = self.jz - other.jz;
164        let dhz = self.hz - other.hz;
165        let dcz = self.cz - other.cz;
166        djz.abs() + dhz.abs() + dcz.abs()
167    }
168}
169
170impl Index<usize> for Jzczhz {
171    type Output = f32;
172
173    #[inline]
174    fn index(&self, index: usize) -> &f32 {
175        match index {
176            0 => &self.jz,
177            1 => &self.cz,
178            2 => &self.hz,
179            _ => panic!("Index out of bounds for Jzczhz"),
180        }
181    }
182}
183
184impl IndexMut<usize> for Jzczhz {
185    #[inline]
186    fn index_mut(&mut self, index: usize) -> &mut f32 {
187        match index {
188            0 => &mut self.jz,
189            1 => &mut self.cz,
190            2 => &mut self.hz,
191            _ => panic!("Index out of bounds for Jzczhz"),
192        }
193    }
194}
195
196impl Add<f32> for Jzczhz {
197    type Output = Jzczhz;
198
199    #[inline]
200    fn add(self, rhs: f32) -> Self::Output {
201        Jzczhz::new(self.jz + rhs, self.cz + rhs, self.hz + rhs)
202    }
203}
204
205impl Sub<f32> for Jzczhz {
206    type Output = Jzczhz;
207
208    #[inline]
209    fn sub(self, rhs: f32) -> Self::Output {
210        Jzczhz::new(self.jz - rhs, self.cz - rhs, self.hz - rhs)
211    }
212}
213
214impl Mul<f32> for Jzczhz {
215    type Output = Jzczhz;
216
217    #[inline]
218    fn mul(self, rhs: f32) -> Self::Output {
219        Jzczhz::new(self.jz * rhs, self.cz * rhs, self.hz * rhs)
220    }
221}
222
223impl Div<f32> for Jzczhz {
224    type Output = Jzczhz;
225
226    #[inline]
227    fn div(self, rhs: f32) -> Self::Output {
228        Jzczhz::new(self.jz / rhs, self.cz / rhs, self.hz / rhs)
229    }
230}
231
232impl Add<Jzczhz> for Jzczhz {
233    type Output = Jzczhz;
234
235    #[inline]
236    fn add(self, rhs: Jzczhz) -> Self::Output {
237        Jzczhz::new(self.jz + rhs.jz, self.cz + rhs.cz, self.hz + rhs.hz)
238    }
239}
240
241impl Sub<Jzczhz> for Jzczhz {
242    type Output = Jzczhz;
243
244    #[inline]
245    fn sub(self, rhs: Jzczhz) -> Self::Output {
246        Jzczhz::new(self.jz - rhs.jz, self.cz - rhs.cz, self.hz - rhs.hz)
247    }
248}
249
250impl Mul<Jzczhz> for Jzczhz {
251    type Output = Jzczhz;
252
253    #[inline]
254    fn mul(self, rhs: Jzczhz) -> Self::Output {
255        Jzczhz::new(self.jz * rhs.jz, self.cz * rhs.cz, self.hz * rhs.hz)
256    }
257}
258
259impl Div<Jzczhz> for Jzczhz {
260    type Output = Jzczhz;
261
262    #[inline]
263    fn div(self, rhs: Jzczhz) -> Self::Output {
264        Jzczhz::new(self.jz / rhs.jz, self.cz / rhs.cz, self.hz / rhs.hz)
265    }
266}
267
268impl AddAssign<Jzczhz> for Jzczhz {
269    #[inline]
270    fn add_assign(&mut self, rhs: Jzczhz) {
271        self.jz += rhs.jz;
272        self.cz += rhs.cz;
273        self.hz += rhs.hz;
274    }
275}
276
277impl SubAssign<Jzczhz> for Jzczhz {
278    #[inline]
279    fn sub_assign(&mut self, rhs: Jzczhz) {
280        self.jz -= rhs.jz;
281        self.cz -= rhs.cz;
282        self.hz -= rhs.hz;
283    }
284}
285
286impl MulAssign<Jzczhz> for Jzczhz {
287    #[inline]
288    fn mul_assign(&mut self, rhs: Jzczhz) {
289        self.jz *= rhs.jz;
290        self.cz *= rhs.cz;
291        self.hz *= rhs.hz;
292    }
293}
294
295impl DivAssign<Jzczhz> for Jzczhz {
296    #[inline]
297    fn div_assign(&mut self, rhs: Jzczhz) {
298        self.jz /= rhs.jz;
299        self.cz /= rhs.cz;
300        self.hz /= rhs.hz;
301    }
302}
303
304impl AddAssign<f32> for Jzczhz {
305    #[inline]
306    fn add_assign(&mut self, rhs: f32) {
307        self.jz += rhs;
308        self.cz += rhs;
309        self.hz += rhs;
310    }
311}
312
313impl SubAssign<f32> for Jzczhz {
314    #[inline]
315    fn sub_assign(&mut self, rhs: f32) {
316        self.jz -= rhs;
317        self.cz -= rhs;
318        self.hz -= rhs;
319    }
320}
321
322impl MulAssign<f32> for Jzczhz {
323    #[inline]
324    fn mul_assign(&mut self, rhs: f32) {
325        self.jz *= rhs;
326        self.cz *= rhs;
327        self.hz *= rhs;
328    }
329}
330
331impl DivAssign<f32> for Jzczhz {
332    #[inline]
333    fn div_assign(&mut self, rhs: f32) {
334        self.jz /= rhs;
335        self.cz /= rhs;
336        self.hz /= rhs;
337    }
338}
339
340impl Jzczhz {
341    #[inline]
342    pub fn sqrt(&self) -> Jzczhz {
343        Jzczhz::new(
344            if self.jz < 0. { 0. } else { self.jz.sqrt() },
345            if self.cz < 0. { 0. } else { self.cz.sqrt() },
346            if self.hz < 0. { 0. } else { self.hz.sqrt() },
347        )
348    }
349
350    #[inline]
351    pub fn cbrt(&self) -> Jzczhz {
352        Jzczhz::new(self.jz.cbrt(), self.cz.cbrt(), self.hz.cbrt())
353    }
354}
355
356impl Pow<f32> for Jzczhz {
357    type Output = Jzczhz;
358
359    #[inline]
360    fn pow(self, rhs: f32) -> Self::Output {
361        Jzczhz::new(self.jz.powf(rhs), self.cz.powf(rhs), self.hz.powf(rhs))
362    }
363}
364
365impl Pow<Jzczhz> for Jzczhz {
366    type Output = Jzczhz;
367
368    #[inline]
369    fn pow(self, rhs: Jzczhz) -> Self::Output {
370        Jzczhz::new(
371            self.jz.powf(rhs.jz),
372            self.cz.powf(self.cz),
373            self.hz.powf(self.hz),
374        )
375    }
376}
377
378impl Neg for Jzczhz {
379    type Output = Jzczhz;
380
381    #[inline]
382    fn neg(self) -> Self::Output {
383        Jzczhz::new(-self.jz, -self.cz, -self.hz)
384    }
385}