1use crate::Xyz;
30use crate::jzazbz::Jzazbz;
31use num_traits::Pow;
32use pxfm::{f_atan2f, f_cbrtf, f_hypot3f, f_hypotf, f_powf, f_sincosf, f_sinf};
33use std::ops::{
34 Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
35};
36
37#[repr(C)]
39#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
40pub struct Jzczhz {
41 pub jz: f32,
43 pub cz: f32,
45 pub hz: f32,
47}
48
49impl Jzczhz {
50 #[inline]
52 pub fn new(jz: f32, cz: f32, hz: f32) -> Jzczhz {
53 Jzczhz { jz, cz, hz }
54 }
55
56 #[inline]
58 pub fn from_jzazbz(jzazbz: Jzazbz) -> Jzczhz {
59 let cz = f_hypotf(jzazbz.az, jzazbz.bz);
60 let hz = f_atan2f(jzazbz.bz, jzazbz.az);
61 Jzczhz::new(jzazbz.jz, cz, hz)
62 }
63
64 #[inline]
66 pub fn to_jzazbz(&self) -> Jzazbz {
67 let sincos = f_sincosf(self.hz);
68 let az = self.cz * sincos.1;
69 let bz = self.cz * sincos.0;
70 Jzazbz::new(self.jz, az, bz)
71 }
72
73 #[inline]
75 pub fn to_jzazbz_with_luminance(&self) -> Jzazbz {
76 let sincos = f_sincosf(self.hz);
77 let az = self.cz * sincos.1;
78 let bz = self.cz * sincos.0;
79 Jzazbz::new(self.jz, az, bz)
80 }
81
82 #[inline]
84 pub fn to_xyz(&self, display_luminance: f32) -> Xyz {
85 let jzazbz = self.to_jzazbz();
86 jzazbz.to_xyz(display_luminance)
87 }
88
89 #[inline]
91 pub fn from_xyz(xyz: Xyz) -> Jzczhz {
92 let jzazbz = Jzazbz::from_xyz(xyz);
93 Jzczhz::from_jzazbz(jzazbz)
94 }
95
96 #[inline]
98 pub fn from_xyz_with_display_luminance(xyz: Xyz, luminance: f32) -> Jzczhz {
99 let jzazbz = Jzazbz::from_xyz_with_display_luminance(xyz, luminance);
100 Jzczhz::from_jzazbz(jzazbz)
101 }
102
103 #[inline]
105 pub fn distance(&self, other: Jzczhz) -> f32 {
106 let djz = self.jz - other.jz;
107 let dcz = self.cz - other.cz;
108 let dhz = self.hz - other.hz;
109 let dh = 2. * (self.cz * other.cz).sqrt() * f_sinf(dhz * 0.5);
110 f_hypot3f(djz, dcz, dh)
111 }
112
113 #[inline]
114 pub fn euclidean_distance(&self, other: Self) -> f32 {
115 let djz = self.jz - other.jz;
116 let dhz = self.hz - other.hz;
117 let dcz = self.cz - other.cz;
118 (djz * djz + dhz * dhz + dcz * dcz).sqrt()
119 }
120
121 #[inline]
122 pub fn taxicab_distance(&self, other: Self) -> f32 {
123 let djz = self.jz - other.jz;
124 let dhz = self.hz - other.hz;
125 let dcz = self.cz - other.cz;
126 djz.abs() + dhz.abs() + dcz.abs()
127 }
128}
129
130impl Index<usize> for Jzczhz {
131 type Output = f32;
132
133 #[inline]
134 fn index(&self, index: usize) -> &f32 {
135 match index {
136 0 => &self.jz,
137 1 => &self.cz,
138 2 => &self.hz,
139 _ => panic!("Index out of bounds for Jzczhz"),
140 }
141 }
142}
143
144impl IndexMut<usize> for Jzczhz {
145 #[inline]
146 fn index_mut(&mut self, index: usize) -> &mut f32 {
147 match index {
148 0 => &mut self.jz,
149 1 => &mut self.cz,
150 2 => &mut self.hz,
151 _ => panic!("Index out of bounds for Jzczhz"),
152 }
153 }
154}
155
156impl Add<f32> for Jzczhz {
157 type Output = Jzczhz;
158
159 #[inline]
160 fn add(self, rhs: f32) -> Self::Output {
161 Jzczhz::new(self.jz + rhs, self.cz + rhs, self.hz + rhs)
162 }
163}
164
165impl Sub<f32> for Jzczhz {
166 type Output = Jzczhz;
167
168 #[inline]
169 fn sub(self, rhs: f32) -> Self::Output {
170 Jzczhz::new(self.jz - rhs, self.cz - rhs, self.hz - rhs)
171 }
172}
173
174impl Mul<f32> for Jzczhz {
175 type Output = Jzczhz;
176
177 #[inline]
178 fn mul(self, rhs: f32) -> Self::Output {
179 Jzczhz::new(self.jz * rhs, self.cz * rhs, self.hz * rhs)
180 }
181}
182
183impl Div<f32> for Jzczhz {
184 type Output = Jzczhz;
185
186 #[inline]
187 fn div(self, rhs: f32) -> Self::Output {
188 Jzczhz::new(self.jz / rhs, self.cz / rhs, self.hz / rhs)
189 }
190}
191
192impl Add<Jzczhz> for Jzczhz {
193 type Output = Jzczhz;
194
195 #[inline]
196 fn add(self, rhs: Jzczhz) -> Self::Output {
197 Jzczhz::new(self.jz + rhs.jz, self.cz + rhs.cz, self.hz + rhs.hz)
198 }
199}
200
201impl Sub<Jzczhz> for Jzczhz {
202 type Output = Jzczhz;
203
204 #[inline]
205 fn sub(self, rhs: Jzczhz) -> Self::Output {
206 Jzczhz::new(self.jz - rhs.jz, self.cz - rhs.cz, self.hz - rhs.hz)
207 }
208}
209
210impl Mul<Jzczhz> for Jzczhz {
211 type Output = Jzczhz;
212
213 #[inline]
214 fn mul(self, rhs: Jzczhz) -> Self::Output {
215 Jzczhz::new(self.jz * rhs.jz, self.cz * rhs.cz, self.hz * rhs.hz)
216 }
217}
218
219impl Div<Jzczhz> for Jzczhz {
220 type Output = Jzczhz;
221
222 #[inline]
223 fn div(self, rhs: Jzczhz) -> Self::Output {
224 Jzczhz::new(self.jz / rhs.jz, self.cz / rhs.cz, self.hz / rhs.hz)
225 }
226}
227
228impl AddAssign<Jzczhz> for Jzczhz {
229 #[inline]
230 fn add_assign(&mut self, rhs: Jzczhz) {
231 self.jz += rhs.jz;
232 self.cz += rhs.cz;
233 self.hz += rhs.hz;
234 }
235}
236
237impl SubAssign<Jzczhz> for Jzczhz {
238 #[inline]
239 fn sub_assign(&mut self, rhs: Jzczhz) {
240 self.jz -= rhs.jz;
241 self.cz -= rhs.cz;
242 self.hz -= rhs.hz;
243 }
244}
245
246impl MulAssign<Jzczhz> for Jzczhz {
247 #[inline]
248 fn mul_assign(&mut self, rhs: Jzczhz) {
249 self.jz *= rhs.jz;
250 self.cz *= rhs.cz;
251 self.hz *= rhs.hz;
252 }
253}
254
255impl DivAssign<Jzczhz> for Jzczhz {
256 #[inline]
257 fn div_assign(&mut self, rhs: Jzczhz) {
258 self.jz /= rhs.jz;
259 self.cz /= rhs.cz;
260 self.hz /= rhs.hz;
261 }
262}
263
264impl AddAssign<f32> for Jzczhz {
265 #[inline]
266 fn add_assign(&mut self, rhs: f32) {
267 self.jz += rhs;
268 self.cz += rhs;
269 self.hz += rhs;
270 }
271}
272
273impl SubAssign<f32> for Jzczhz {
274 #[inline]
275 fn sub_assign(&mut self, rhs: f32) {
276 self.jz -= rhs;
277 self.cz -= rhs;
278 self.hz -= rhs;
279 }
280}
281
282impl MulAssign<f32> for Jzczhz {
283 #[inline]
284 fn mul_assign(&mut self, rhs: f32) {
285 self.jz *= rhs;
286 self.cz *= rhs;
287 self.hz *= rhs;
288 }
289}
290
291impl DivAssign<f32> for Jzczhz {
292 #[inline]
293 fn div_assign(&mut self, rhs: f32) {
294 self.jz /= rhs;
295 self.cz /= rhs;
296 self.hz /= rhs;
297 }
298}
299
300impl Jzczhz {
301 #[inline]
302 pub fn sqrt(&self) -> Jzczhz {
303 Jzczhz::new(self.jz.sqrt(), self.cz.sqrt(), self.hz.sqrt())
304 }
305
306 #[inline]
307 pub fn cbrt(&self) -> Jzczhz {
308 Jzczhz::new(f_cbrtf(self.jz), f_cbrtf(self.cz), f_cbrtf(self.hz))
309 }
310}
311
312impl Pow<f32> for Jzczhz {
313 type Output = Jzczhz;
314
315 #[inline]
316 fn pow(self, rhs: f32) -> Self::Output {
317 Jzczhz::new(
318 f_powf(self.jz, rhs),
319 f_powf(self.cz, rhs),
320 f_powf(self.hz, rhs),
321 )
322 }
323}
324
325impl Pow<Jzczhz> for Jzczhz {
326 type Output = Jzczhz;
327
328 #[inline]
329 fn pow(self, rhs: Jzczhz) -> Self::Output {
330 Jzczhz::new(
331 f_powf(self.jz, rhs.jz),
332 f_powf(self.cz, self.cz),
333 f_powf(self.hz, self.hz),
334 )
335 }
336}
337
338impl Neg for Jzczhz {
339 type Output = Jzczhz;
340
341 #[inline]
342 fn neg(self) -> Self::Output {
343 Jzczhz::new(-self.jz, -self.cz, -self.hz)
344 }
345}
346
347#[cfg(test)]
348mod 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}