1use 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#[repr(C)]
16#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
17pub struct Jzczhz {
18 pub jz: f32,
20 pub cz: f32,
22 pub hz: f32,
24}
25
26impl Jzczhz {
27 #[inline]
29 pub fn new(jz: f32, cz: f32, hz: f32) -> Jzczhz {
30 Jzczhz { jz, cz, hz }
31 }
32
33 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
120 pub fn to_xyz(&self) -> Xyz {
121 let jzazbz = self.to_jzazbz();
122 jzazbz.to_xyz()
123 }
124
125 #[inline]
127 pub fn from_xyz(xyz: Xyz) -> Jzczhz {
128 let jzazbz = Jzazbz::from_xyz(xyz);
129 Jzczhz::from_jzazbz(jzazbz)
130 }
131
132 #[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 #[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}