/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.8.1/src/luv.rs
Line | Count | Source |
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 | | */ |
7 | | |
8 | | //! # Luv |
9 | | /// Struct representing a color in CIE LUV, a.k.a. L\*u\*v\*, color space |
10 | | #[repr(C)] |
11 | | #[derive(Debug, Copy, Clone, Default, PartialOrd)] |
12 | | pub struct Luv { |
13 | | /// The L\* value (achromatic luminance) of the colour in 0–100 range. |
14 | | pub l: f32, |
15 | | /// The u\* value of the colour. |
16 | | /// |
17 | | /// Together with v\* value, it defines chromaticity of the colour. The u\* |
18 | | /// coordinate represents colour’s position on red-green axis with negative |
19 | | /// values indicating more red and positive more green colour. Typical |
20 | | /// values are in -134–220 range (but exact range for ‘valid’ colours |
21 | | /// depends on luminance and v\* value). |
22 | | pub u: f32, |
23 | | /// The u\* value of the colour. |
24 | | /// |
25 | | /// Together with u\* value, it defines chromaticity of the colour. The v\* |
26 | | /// coordinate represents colour’s position on blue-yellow axis with |
27 | | /// negative values indicating more blue and positive more yellow colour. |
28 | | /// Typical values are in -140–122 range (but exact range for ‘valid’ |
29 | | /// colours depends on luminance and u\* value). |
30 | | pub v: f32, |
31 | | } |
32 | | |
33 | | /// Representing a color in cylindrical CIE LCh(uv) color space |
34 | | #[repr(C)] |
35 | | #[derive(Debug, Copy, Clone, Default, PartialOrd)] |
36 | | pub struct LCh { |
37 | | /// The L\* value (achromatic luminance) of the colour in 0–100 range. |
38 | | /// |
39 | | /// This is the same value as in the [`Luv`] object. |
40 | | pub l: f32, |
41 | | /// The C\*_uv value (chroma) of the colour. |
42 | | /// |
43 | | /// Together with h_uv, it defines chromaticity of the colour. The typical |
44 | | /// values of the coordinate go from zero up to around 150 (but exact range |
45 | | /// for ‘valid’ colours depends on luminance and hue). Zero represents |
46 | | /// shade of grey. |
47 | | pub c: f32, |
48 | | /// The h_uv value (hue) of the colour measured in radians. |
49 | | /// |
50 | | /// Together with C\*_uv, it defines chromaticity of the colour. The value |
51 | | /// represents an angle thus it wraps around τ. Typically, the value will |
52 | | /// be in the -π–π range. The value is undefined if C\*_uv is zero. |
53 | | pub h: f32, |
54 | | } |
55 | | |
56 | | use crate::mlaf::mlaf; |
57 | | use crate::{Chromaticity, Lab, Xyz}; |
58 | | use num_traits::Pow; |
59 | | use pxfm::{f_atan2f, f_cbrtf, f_hypotf, f_powf, f_sincosf}; |
60 | | use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; |
61 | | |
62 | | pub(crate) const LUV_WHITE_U_PRIME: f32 = 4.0f32 * Chromaticity::D50.to_xyz().y |
63 | | / (Chromaticity::D50.to_xyz().x |
64 | | + 15.0 * Chromaticity::D50.to_xyz().y |
65 | | + 3.0 * Chromaticity::D50.to_xyz().z); |
66 | | pub(crate) const LUV_WHITE_V_PRIME: f32 = 9.0f32 * Chromaticity::D50.to_xyz().y |
67 | | / (Chromaticity::D50.to_xyz().x |
68 | | + 15.0 * Chromaticity::D50.to_xyz().y |
69 | | + 3.0 * Chromaticity::D50.to_xyz().z); |
70 | | |
71 | | pub(crate) const LUV_CUTOFF_FORWARD_Y: f32 = (6f32 / 29f32) * (6f32 / 29f32) * (6f32 / 29f32); |
72 | | pub(crate) const LUV_MULTIPLIER_FORWARD_Y: f32 = (29f32 / 3f32) * (29f32 / 3f32) * (29f32 / 3f32); |
73 | | pub(crate) const LUV_MULTIPLIER_INVERSE_Y: f32 = (3f32 / 29f32) * (3f32 / 29f32) * (3f32 / 29f32); |
74 | | impl Luv { |
75 | | /// Converts CIE XYZ to CIE Luv using D50 white point |
76 | | #[inline] |
77 | | #[allow(clippy::manual_clamp)] |
78 | 0 | pub fn from_xyz(xyz: Xyz) -> Self { |
79 | 0 | let [x, y, z] = [xyz.x, xyz.y, xyz.z]; |
80 | 0 | let den = mlaf(mlaf(x, 15.0, y), 3.0, z); |
81 | | |
82 | 0 | let l = (if y < LUV_CUTOFF_FORWARD_Y { |
83 | 0 | LUV_MULTIPLIER_FORWARD_Y * y |
84 | | } else { |
85 | 0 | 116. * f_cbrtf(y) - 16. |
86 | | }) |
87 | 0 | .min(100.) |
88 | 0 | .max(0.); |
89 | | let (u, v); |
90 | 0 | if den != 0f32 { |
91 | 0 | let u_prime = 4. * x / den; |
92 | 0 | let v_prime = 9. * y / den; |
93 | 0 | u = 13. * l * (u_prime - LUV_WHITE_U_PRIME); |
94 | 0 | v = 13. * l * (v_prime - LUV_WHITE_V_PRIME); |
95 | 0 | } else { |
96 | 0 | u = 0.; |
97 | 0 | v = 0.; |
98 | 0 | } |
99 | | |
100 | 0 | Luv { l, u, v } |
101 | 0 | } |
102 | | |
103 | | /// To [Xyz] using D50 colorimetry |
104 | | #[inline] |
105 | 0 | pub fn to_xyz(&self) -> Xyz { |
106 | 0 | if self.l <= 0. { |
107 | 0 | return Xyz::new(0., 0., 0.); |
108 | 0 | } |
109 | 0 | let l13 = 1. / (13. * self.l); |
110 | 0 | let u = mlaf(LUV_WHITE_U_PRIME, self.u, l13); |
111 | 0 | let v = mlaf(LUV_WHITE_V_PRIME, self.v, l13); |
112 | 0 | let y = if self.l > 8. { |
113 | 0 | let jx = (self.l + 16.) / 116.; |
114 | 0 | jx * jx * jx |
115 | | } else { |
116 | 0 | self.l * LUV_MULTIPLIER_INVERSE_Y |
117 | | }; |
118 | | let (x, z); |
119 | 0 | if v != 0. { |
120 | 0 | let den = 1. / (4. * v); |
121 | 0 | x = y * 9. * u * den; |
122 | 0 | z = y * mlaf(mlaf(12.0, -3.0, u), -20., v) * den; |
123 | 0 | } else { |
124 | 0 | x = 0.; |
125 | 0 | z = 0.; |
126 | 0 | } |
127 | | |
128 | 0 | Xyz::new(x, y, z) |
129 | 0 | } |
130 | | |
131 | | #[inline] |
132 | 0 | pub const fn new(l: f32, u: f32, v: f32) -> Luv { |
133 | 0 | Luv { l, u, v } |
134 | 0 | } |
135 | | } |
136 | | |
137 | | impl LCh { |
138 | | #[inline] |
139 | 0 | pub const fn new(l: f32, c: f32, h: f32) -> Self { |
140 | 0 | LCh { l, c, h } |
141 | 0 | } |
142 | | |
143 | | /// Converts Lab to LCh(uv) |
144 | | #[inline] |
145 | 0 | pub fn from_luv(luv: Luv) -> Self { |
146 | 0 | LCh { |
147 | 0 | l: luv.l, |
148 | 0 | c: f_hypotf(luv.u, luv.v), |
149 | 0 | h: f_atan2f(luv.v, luv.u), |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | /// Converts Lab to LCh(ab) |
154 | | #[inline] |
155 | 0 | pub fn from_lab(lab: Lab) -> Self { |
156 | 0 | LCh { |
157 | 0 | l: lab.l, |
158 | 0 | c: f_hypotf(lab.a, lab.b), |
159 | 0 | h: f_atan2f(lab.b, lab.a), |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | /// Computes LCh(uv) |
164 | | #[inline] |
165 | 0 | pub fn from_xyz(xyz: Xyz) -> Self { |
166 | 0 | Self::from_luv(Luv::from_xyz(xyz)) |
167 | 0 | } |
168 | | |
169 | | /// Computes LCh(ab) |
170 | | #[inline] |
171 | 0 | pub fn from_xyz_lab(xyz: Xyz) -> Self { |
172 | 0 | Self::from_lab(Lab::from_xyz(xyz)) |
173 | 0 | } |
174 | | |
175 | | /// Converts LCh(uv) to Luv |
176 | | #[inline] |
177 | 0 | pub fn to_xyz(&self) -> Xyz { |
178 | 0 | self.to_luv().to_xyz() |
179 | 0 | } |
180 | | |
181 | | /// Converts LCh(ab) to Lab |
182 | | #[inline] |
183 | 0 | pub fn to_xyz_lab(&self) -> Xyz { |
184 | 0 | self.to_lab().to_xyz() |
185 | 0 | } |
186 | | |
187 | | #[inline] |
188 | 0 | pub fn to_luv(&self) -> Luv { |
189 | 0 | let sincos = f_sincosf(self.h); |
190 | 0 | Luv { |
191 | 0 | l: self.l, |
192 | 0 | u: self.c * sincos.1, |
193 | 0 | v: self.c * sincos.0, |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | #[inline] |
198 | 0 | pub fn to_lab(&self) -> Lab { |
199 | 0 | let sincos = f_sincosf(self.h); |
200 | 0 | Lab { |
201 | 0 | l: self.l, |
202 | 0 | a: self.c * sincos.1, |
203 | 0 | b: self.c * sincos.0, |
204 | 0 | } |
205 | 0 | } |
206 | | } |
207 | | |
208 | | impl PartialEq<Luv> for Luv { |
209 | | /// Compares two colours ignoring chromaticity if L\* is zero. |
210 | | #[inline] |
211 | 0 | fn eq(&self, other: &Self) -> bool { |
212 | 0 | if self.l != other.l { |
213 | 0 | false |
214 | 0 | } else if self.l == 0.0 { |
215 | 0 | true |
216 | | } else { |
217 | 0 | self.u == other.u && self.v == other.v |
218 | | } |
219 | 0 | } |
220 | | } |
221 | | |
222 | | impl PartialEq<LCh> for LCh { |
223 | | /// Compares two colors ignoring chromaticity if L\* is zero and hue if C\* |
224 | | /// is zero. Hues which are τ apart are compared equal. |
225 | | #[inline] |
226 | 0 | fn eq(&self, other: &Self) -> bool { |
227 | 0 | if self.l != other.l { |
228 | 0 | false |
229 | 0 | } else if self.l == 0.0 { |
230 | 0 | true |
231 | 0 | } else if self.c != other.c { |
232 | 0 | false |
233 | 0 | } else if self.c == 0.0 { |
234 | 0 | true |
235 | | } else { |
236 | | use std::f32::consts::TAU; |
237 | 0 | self.h.rem_euclid(TAU) == other.h.rem_euclid(TAU) |
238 | | } |
239 | 0 | } |
240 | | } |
241 | | |
242 | | impl Luv { |
243 | | #[inline] |
244 | 0 | pub fn euclidean_distance(&self, other: Luv) -> f32 { |
245 | 0 | let dl = self.l - other.l; |
246 | 0 | let du = self.u - other.u; |
247 | 0 | let dv = self.v - other.v; |
248 | 0 | (dl * dl + du * du + dv * dv).sqrt() |
249 | 0 | } |
250 | | } |
251 | | |
252 | | impl LCh { |
253 | | #[inline] |
254 | 0 | pub fn euclidean_distance(&self, other: LCh) -> f32 { |
255 | 0 | let dl = self.l - other.l; |
256 | 0 | let dc = self.c - other.c; |
257 | 0 | let dh = self.h - other.h; |
258 | 0 | (dl * dl + dc * dc + dh * dh).sqrt() |
259 | 0 | } |
260 | | } |
261 | | |
262 | | impl Luv { |
263 | | #[inline] |
264 | 0 | pub const fn taxicab_distance(&self, other: Self) -> f32 { |
265 | 0 | let dl = self.l - other.l; |
266 | 0 | let du = self.u - other.u; |
267 | 0 | let dv = self.v - other.v; |
268 | 0 | dl.abs() + du.abs() + dv.abs() |
269 | 0 | } |
270 | | } |
271 | | |
272 | | impl LCh { |
273 | | #[inline] |
274 | 0 | pub const fn taxicab_distance(&self, other: Self) -> f32 { |
275 | 0 | let dl = self.l - other.l; |
276 | 0 | let dc = self.c - other.c; |
277 | 0 | let dh = self.h - other.h; |
278 | 0 | dl.abs() + dc.abs() + dh.abs() |
279 | 0 | } |
280 | | } |
281 | | |
282 | | impl Add<Luv> for Luv { |
283 | | type Output = Luv; |
284 | | |
285 | | #[inline] |
286 | 0 | fn add(self, rhs: Luv) -> Luv { |
287 | 0 | Luv::new(self.l + rhs.l, self.u + rhs.u, self.v + rhs.v) |
288 | 0 | } |
289 | | } |
290 | | |
291 | | impl Add<LCh> for LCh { |
292 | | type Output = LCh; |
293 | | |
294 | | #[inline] |
295 | 0 | fn add(self, rhs: LCh) -> LCh { |
296 | 0 | LCh::new(self.l + rhs.l, self.c + rhs.c, self.h + rhs.h) |
297 | 0 | } |
298 | | } |
299 | | |
300 | | impl Sub<Luv> for Luv { |
301 | | type Output = Luv; |
302 | | |
303 | | #[inline] |
304 | 0 | fn sub(self, rhs: Luv) -> Luv { |
305 | 0 | Luv::new(self.l - rhs.l, self.u - rhs.u, self.v - rhs.v) |
306 | 0 | } |
307 | | } |
308 | | |
309 | | impl Sub<LCh> for LCh { |
310 | | type Output = LCh; |
311 | | |
312 | | #[inline] |
313 | 0 | fn sub(self, rhs: LCh) -> LCh { |
314 | 0 | LCh::new(self.l - rhs.l, self.c - rhs.c, self.h - rhs.h) |
315 | 0 | } |
316 | | } |
317 | | |
318 | | impl Mul<Luv> for Luv { |
319 | | type Output = Luv; |
320 | | |
321 | | #[inline] |
322 | 0 | fn mul(self, rhs: Luv) -> Luv { |
323 | 0 | Luv::new(self.l * rhs.l, self.u * rhs.u, self.v * rhs.v) |
324 | 0 | } |
325 | | } |
326 | | |
327 | | impl Mul<LCh> for LCh { |
328 | | type Output = LCh; |
329 | | |
330 | | #[inline] |
331 | 0 | fn mul(self, rhs: LCh) -> LCh { |
332 | 0 | LCh::new(self.l * rhs.l, self.c * rhs.c, self.h * rhs.h) |
333 | 0 | } |
334 | | } |
335 | | |
336 | | impl Div<Luv> for Luv { |
337 | | type Output = Luv; |
338 | | |
339 | | #[inline] |
340 | 0 | fn div(self, rhs: Luv) -> Luv { |
341 | 0 | Luv::new(self.l / rhs.l, self.u / rhs.u, self.v / rhs.v) |
342 | 0 | } |
343 | | } |
344 | | |
345 | | impl Div<LCh> for LCh { |
346 | | type Output = LCh; |
347 | | |
348 | | #[inline] |
349 | 0 | fn div(self, rhs: LCh) -> LCh { |
350 | 0 | LCh::new(self.l / rhs.l, self.c / rhs.c, self.h / rhs.h) |
351 | 0 | } |
352 | | } |
353 | | |
354 | | impl Add<f32> for Luv { |
355 | | type Output = Luv; |
356 | | |
357 | | #[inline] |
358 | 0 | fn add(self, rhs: f32) -> Self::Output { |
359 | 0 | Luv::new(self.l + rhs, self.u + rhs, self.v + rhs) |
360 | 0 | } |
361 | | } |
362 | | |
363 | | impl Add<f32> for LCh { |
364 | | type Output = LCh; |
365 | | |
366 | | #[inline] |
367 | 0 | fn add(self, rhs: f32) -> Self::Output { |
368 | 0 | LCh::new(self.l + rhs, self.c + rhs, self.h + rhs) |
369 | 0 | } |
370 | | } |
371 | | |
372 | | impl Sub<f32> for Luv { |
373 | | type Output = Luv; |
374 | | |
375 | | #[inline] |
376 | 0 | fn sub(self, rhs: f32) -> Self::Output { |
377 | 0 | Luv::new(self.l - rhs, self.u - rhs, self.v - rhs) |
378 | 0 | } |
379 | | } |
380 | | |
381 | | impl Sub<f32> for LCh { |
382 | | type Output = LCh; |
383 | | |
384 | | #[inline] |
385 | 0 | fn sub(self, rhs: f32) -> Self::Output { |
386 | 0 | LCh::new(self.l - rhs, self.c - rhs, self.h - rhs) |
387 | 0 | } |
388 | | } |
389 | | |
390 | | impl Mul<f32> for Luv { |
391 | | type Output = Luv; |
392 | | |
393 | | #[inline] |
394 | 0 | fn mul(self, rhs: f32) -> Self::Output { |
395 | 0 | Luv::new(self.l * rhs, self.u * rhs, self.v * rhs) |
396 | 0 | } |
397 | | } |
398 | | |
399 | | impl Mul<f32> for LCh { |
400 | | type Output = LCh; |
401 | | |
402 | | #[inline] |
403 | 0 | fn mul(self, rhs: f32) -> Self::Output { |
404 | 0 | LCh::new(self.l * rhs, self.c * rhs, self.h * rhs) |
405 | 0 | } |
406 | | } |
407 | | |
408 | | impl Div<f32> for Luv { |
409 | | type Output = Luv; |
410 | | |
411 | | #[inline] |
412 | 0 | fn div(self, rhs: f32) -> Self::Output { |
413 | 0 | Luv::new(self.l / rhs, self.u / rhs, self.v / rhs) |
414 | 0 | } |
415 | | } |
416 | | |
417 | | impl Div<f32> for LCh { |
418 | | type Output = LCh; |
419 | | |
420 | | #[inline] |
421 | 0 | fn div(self, rhs: f32) -> Self::Output { |
422 | 0 | LCh::new(self.l / rhs, self.c / rhs, self.h / rhs) |
423 | 0 | } |
424 | | } |
425 | | |
426 | | impl AddAssign<Luv> for Luv { |
427 | | #[inline] |
428 | 0 | fn add_assign(&mut self, rhs: Luv) { |
429 | 0 | self.l += rhs.l; |
430 | 0 | self.u += rhs.u; |
431 | 0 | self.v += rhs.v; |
432 | 0 | } |
433 | | } |
434 | | |
435 | | impl AddAssign<LCh> for LCh { |
436 | | #[inline] |
437 | 0 | fn add_assign(&mut self, rhs: LCh) { |
438 | 0 | self.l += rhs.l; |
439 | 0 | self.c += rhs.c; |
440 | 0 | self.h += rhs.h; |
441 | 0 | } |
442 | | } |
443 | | |
444 | | impl SubAssign<Luv> for Luv { |
445 | | #[inline] |
446 | 0 | fn sub_assign(&mut self, rhs: Luv) { |
447 | 0 | self.l -= rhs.l; |
448 | 0 | self.u -= rhs.u; |
449 | 0 | self.v -= rhs.v; |
450 | 0 | } |
451 | | } |
452 | | |
453 | | impl SubAssign<LCh> for LCh { |
454 | | #[inline] |
455 | 0 | fn sub_assign(&mut self, rhs: LCh) { |
456 | 0 | self.l -= rhs.l; |
457 | 0 | self.c -= rhs.c; |
458 | 0 | self.h -= rhs.h; |
459 | 0 | } |
460 | | } |
461 | | |
462 | | impl MulAssign<Luv> for Luv { |
463 | | #[inline] |
464 | 0 | fn mul_assign(&mut self, rhs: Luv) { |
465 | 0 | self.l *= rhs.l; |
466 | 0 | self.u *= rhs.u; |
467 | 0 | self.v *= rhs.v; |
468 | 0 | } |
469 | | } |
470 | | |
471 | | impl MulAssign<LCh> for LCh { |
472 | | #[inline] |
473 | 0 | fn mul_assign(&mut self, rhs: LCh) { |
474 | 0 | self.l *= rhs.l; |
475 | 0 | self.c *= rhs.c; |
476 | 0 | self.h *= rhs.h; |
477 | 0 | } |
478 | | } |
479 | | |
480 | | impl DivAssign<Luv> for Luv { |
481 | | #[inline] |
482 | 0 | fn div_assign(&mut self, rhs: Luv) { |
483 | 0 | self.l /= rhs.l; |
484 | 0 | self.u /= rhs.u; |
485 | 0 | self.v /= rhs.v; |
486 | 0 | } |
487 | | } |
488 | | |
489 | | impl DivAssign<LCh> for LCh { |
490 | | #[inline] |
491 | 0 | fn div_assign(&mut self, rhs: LCh) { |
492 | 0 | self.l /= rhs.l; |
493 | 0 | self.c /= rhs.c; |
494 | 0 | self.h /= rhs.h; |
495 | 0 | } |
496 | | } |
497 | | |
498 | | impl AddAssign<f32> for Luv { |
499 | | #[inline] |
500 | 0 | fn add_assign(&mut self, rhs: f32) { |
501 | 0 | self.l += rhs; |
502 | 0 | self.u += rhs; |
503 | 0 | self.v += rhs; |
504 | 0 | } |
505 | | } |
506 | | |
507 | | impl AddAssign<f32> for LCh { |
508 | | #[inline] |
509 | 0 | fn add_assign(&mut self, rhs: f32) { |
510 | 0 | self.l += rhs; |
511 | 0 | self.c += rhs; |
512 | 0 | self.h += rhs; |
513 | 0 | } |
514 | | } |
515 | | |
516 | | impl SubAssign<f32> for Luv { |
517 | | #[inline] |
518 | 0 | fn sub_assign(&mut self, rhs: f32) { |
519 | 0 | self.l -= rhs; |
520 | 0 | self.u -= rhs; |
521 | 0 | self.v -= rhs; |
522 | 0 | } |
523 | | } |
524 | | |
525 | | impl SubAssign<f32> for LCh { |
526 | | #[inline] |
527 | 0 | fn sub_assign(&mut self, rhs: f32) { |
528 | 0 | self.l -= rhs; |
529 | 0 | self.c -= rhs; |
530 | 0 | self.h -= rhs; |
531 | 0 | } |
532 | | } |
533 | | |
534 | | impl MulAssign<f32> for Luv { |
535 | | #[inline] |
536 | 0 | fn mul_assign(&mut self, rhs: f32) { |
537 | 0 | self.l *= rhs; |
538 | 0 | self.u *= rhs; |
539 | 0 | self.v *= rhs; |
540 | 0 | } |
541 | | } |
542 | | |
543 | | impl MulAssign<f32> for LCh { |
544 | | #[inline] |
545 | 0 | fn mul_assign(&mut self, rhs: f32) { |
546 | 0 | self.l *= rhs; |
547 | 0 | self.c *= rhs; |
548 | 0 | self.h *= rhs; |
549 | 0 | } |
550 | | } |
551 | | |
552 | | impl DivAssign<f32> for Luv { |
553 | | #[inline] |
554 | 0 | fn div_assign(&mut self, rhs: f32) { |
555 | 0 | self.l /= rhs; |
556 | 0 | self.u /= rhs; |
557 | 0 | self.v /= rhs; |
558 | 0 | } |
559 | | } |
560 | | |
561 | | impl DivAssign<f32> for LCh { |
562 | | #[inline] |
563 | 0 | fn div_assign(&mut self, rhs: f32) { |
564 | 0 | self.l /= rhs; |
565 | 0 | self.c /= rhs; |
566 | 0 | self.h /= rhs; |
567 | 0 | } |
568 | | } |
569 | | |
570 | | impl Neg for LCh { |
571 | | type Output = LCh; |
572 | | |
573 | | #[inline] |
574 | 0 | fn neg(self) -> Self::Output { |
575 | 0 | LCh::new(-self.l, -self.c, -self.h) |
576 | 0 | } |
577 | | } |
578 | | |
579 | | impl Neg for Luv { |
580 | | type Output = Luv; |
581 | | |
582 | | #[inline] |
583 | 0 | fn neg(self) -> Self::Output { |
584 | 0 | Luv::new(-self.l, -self.u, -self.v) |
585 | 0 | } |
586 | | } |
587 | | |
588 | | impl Pow<f32> for Luv { |
589 | | type Output = Luv; |
590 | | |
591 | | #[inline] |
592 | 0 | fn pow(self, rhs: f32) -> Self::Output { |
593 | 0 | Luv::new( |
594 | 0 | f_powf(self.l, rhs), |
595 | 0 | f_powf(self.u, rhs), |
596 | 0 | f_powf(self.v, rhs), |
597 | | ) |
598 | 0 | } |
599 | | } |
600 | | |
601 | | impl Pow<f32> for LCh { |
602 | | type Output = LCh; |
603 | | |
604 | | #[inline] |
605 | 0 | fn pow(self, rhs: f32) -> Self::Output { |
606 | 0 | LCh::new( |
607 | 0 | f_powf(self.l, rhs), |
608 | 0 | f_powf(self.c, rhs), |
609 | 0 | f_powf(self.h, rhs), |
610 | | ) |
611 | 0 | } |
612 | | } |
613 | | |
614 | | impl Pow<Luv> for Luv { |
615 | | type Output = Luv; |
616 | | |
617 | | #[inline] |
618 | 0 | fn pow(self, rhs: Luv) -> Self::Output { |
619 | 0 | Luv::new( |
620 | 0 | f_powf(self.l, rhs.l), |
621 | 0 | f_powf(self.u, rhs.u), |
622 | 0 | f_powf(self.v, rhs.v), |
623 | | ) |
624 | 0 | } |
625 | | } |
626 | | |
627 | | impl Pow<LCh> for LCh { |
628 | | type Output = LCh; |
629 | | |
630 | | #[inline] |
631 | 0 | fn pow(self, rhs: LCh) -> Self::Output { |
632 | 0 | LCh::new( |
633 | 0 | f_powf(self.l, rhs.l), |
634 | 0 | f_powf(self.c, rhs.c), |
635 | 0 | f_powf(self.h, rhs.h), |
636 | | ) |
637 | 0 | } |
638 | | } |
639 | | |
640 | | impl Luv { |
641 | | #[inline] |
642 | 0 | pub fn sqrt(&self) -> Luv { |
643 | 0 | Luv::new(self.l.sqrt(), self.u.sqrt(), self.v.sqrt()) |
644 | 0 | } |
645 | | |
646 | | #[inline] |
647 | 0 | pub fn cbrt(&self) -> Luv { |
648 | 0 | Luv::new(f_cbrtf(self.l), f_cbrtf(self.u), f_cbrtf(self.v)) |
649 | 0 | } |
650 | | } |
651 | | |
652 | | impl LCh { |
653 | | #[inline] |
654 | 0 | pub fn sqrt(&self) -> LCh { |
655 | 0 | LCh::new( |
656 | 0 | if self.l < 0. { 0. } else { self.l.sqrt() }, |
657 | 0 | if self.c < 0. { 0. } else { self.c.sqrt() }, |
658 | 0 | if self.h < 0. { 0. } else { self.h.sqrt() }, |
659 | | ) |
660 | 0 | } |
661 | | |
662 | | #[inline] |
663 | 0 | pub fn cbrt(&self) -> LCh { |
664 | 0 | LCh::new(f_cbrtf(self.l), f_cbrtf(self.c), f_cbrtf(self.h)) |
665 | 0 | } |
666 | | } |
667 | | |
668 | | #[cfg(test)] |
669 | | mod tests { |
670 | | use super::*; |
671 | | |
672 | | #[test] |
673 | | fn round_trip_luv() { |
674 | | let xyz = Xyz::new(0.1, 0.2, 0.3); |
675 | | let lab = Luv::from_xyz(xyz); |
676 | | let rolled_back = lab.to_xyz(); |
677 | | let dx = (xyz.x - rolled_back.x).abs(); |
678 | | let dy = (xyz.y - rolled_back.y).abs(); |
679 | | let dz = (xyz.z - rolled_back.z).abs(); |
680 | | assert!(dx < 1e-5); |
681 | | assert!(dy < 1e-5); |
682 | | assert!(dz < 1e-5); |
683 | | } |
684 | | |
685 | | #[test] |
686 | | fn round_trip_lch() { |
687 | | let xyz = Xyz::new(0.1, 0.2, 0.3); |
688 | | let luv = Luv::from_xyz(xyz); |
689 | | let lab = LCh::from_luv(luv); |
690 | | let rolled_back = lab.to_luv(); |
691 | | let dx = (luv.l - rolled_back.l).abs(); |
692 | | let dy = (luv.u - rolled_back.u).abs(); |
693 | | let dz = (luv.v - rolled_back.v).abs(); |
694 | | assert!(dx < 1e-4); |
695 | | assert!(dy < 1e-4); |
696 | | assert!(dz < 1e-4); |
697 | | } |
698 | | } |