1use crate::{EuclideanDistance, Oklab, Rgb, TaxicabDistance, TransferFunction};
8use erydanos::{eatan2f, ehypotf, Cosine, Sine};
9use num_traits::Pow;
10use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
11
12#[repr(C)]
14#[derive(Copy, Clone, PartialOrd, PartialEq)]
15pub struct Oklch {
16 pub l: f32,
18 pub c: f32,
20 pub h: f32,
22}
23
24impl Oklch {
25 #[inline]
27 pub fn new(l: f32, c: f32, h: f32) -> Oklch {
28 Oklch { l, c, h }
29 }
30
31 #[inline]
36 pub fn from_rgb(rgb: Rgb<u8>, transfer_function: TransferFunction) -> Oklch {
37 let oklab = rgb.to_oklab(transfer_function);
38 Oklch::from_oklab(oklab)
39 }
40
41 #[inline]
46 pub fn from_linear_rgb(rgb: Rgb<f32>) -> Oklch {
47 let oklab = Oklab::from_linear_rgb(rgb);
48 Oklch::from_oklab(oklab)
49 }
50
51 #[inline]
56 pub fn to_rgb(&self, transfer_function: TransferFunction) -> Rgb<u8> {
57 let oklab = self.to_oklab();
58 oklab.to_rgb(transfer_function)
59 }
60
61 #[inline]
63 pub fn to_linear_rgb(&self) -> Rgb<f32> {
64 let oklab = self.to_oklab();
65 oklab.to_linear_rgb()
66 }
67
68 #[inline]
70 pub fn from_oklab(oklab: Oklab) -> Oklch {
71 let chroma = ehypotf(oklab.b, oklab.a);
72 let hue = eatan2f(oklab.b, oklab.a);
73 Oklch::new(oklab.l, chroma, hue)
74 }
75
76 #[inline]
78 pub fn to_oklab(&self) -> Oklab {
79 let l = self.l;
80 let a = self.c * self.h.ecos();
81 let b = self.c * self.h.esin();
82 Oklab::new(l, a, b)
83 }
84}
85
86impl EuclideanDistance for Oklch {
87 #[inline]
88 fn euclidean_distance(&self, other: Self) -> f32 {
89 let dl = self.l - other.l;
90 let dc = self.c - other.c;
91 let dh = self.h - other.h;
92 (dl * dl + dc * dc + dh * dh).sqrt()
93 }
94}
95
96impl TaxicabDistance for Oklch {
97 #[inline]
98 fn taxicab_distance(&self, other: Self) -> f32 {
99 let dl = self.l - other.l;
100 let dc = self.c - other.c;
101 let dh = self.h - other.h;
102 dl.abs() + dc.abs() + dh.abs()
103 }
104}
105
106impl Add<Oklch> for Oklch {
107 type Output = Oklch;
108
109 #[inline]
110 fn add(self, rhs: Self) -> Oklch {
111 Oklch::new(self.l + rhs.l, self.c + rhs.c, self.h + rhs.h)
112 }
113}
114
115impl Add<f32> for Oklch {
116 type Output = Oklch;
117
118 #[inline]
119 fn add(self, rhs: f32) -> Oklch {
120 Oklch::new(self.l + rhs, self.c + rhs, self.h + rhs)
121 }
122}
123
124impl AddAssign<Oklch> for Oklch {
125 #[inline]
126 fn add_assign(&mut self, rhs: Oklch) {
127 self.l += rhs.l;
128 self.c += rhs.c;
129 self.h += rhs.h;
130 }
131}
132
133impl AddAssign<f32> for Oklch {
134 #[inline]
135 fn add_assign(&mut self, rhs: f32) {
136 self.l += rhs;
137 self.c += rhs;
138 self.h += rhs;
139 }
140}
141
142impl Mul<f32> for Oklch {
143 type Output = Oklch;
144
145 #[inline]
146 fn mul(self, rhs: f32) -> Self::Output {
147 Oklch::new(self.l * rhs, self.c * rhs, self.h * rhs)
148 }
149}
150
151impl Mul<Oklch> for Oklch {
152 type Output = Oklch;
153
154 #[inline]
155 fn mul(self, rhs: Oklch) -> Self::Output {
156 Oklch::new(self.l * rhs.l, self.c * rhs.c, self.h * rhs.h)
157 }
158}
159
160impl MulAssign<f32> for Oklch {
161 #[inline]
162 fn mul_assign(&mut self, rhs: f32) {
163 self.l *= rhs;
164 self.c *= rhs;
165 self.h *= rhs;
166 }
167}
168
169impl MulAssign<Oklch> for Oklch {
170 #[inline]
171 fn mul_assign(&mut self, rhs: Oklch) {
172 self.l *= rhs.l;
173 self.c *= rhs.c;
174 self.h *= rhs.h;
175 }
176}
177
178impl Sub<f32> for Oklch {
179 type Output = Oklch;
180
181 #[inline]
182 fn sub(self, rhs: f32) -> Self::Output {
183 Oklch::new(self.l - rhs, self.c - rhs, self.h - rhs)
184 }
185}
186
187impl Sub<Oklch> for Oklch {
188 type Output = Oklch;
189
190 #[inline]
191 fn sub(self, rhs: Oklch) -> Self::Output {
192 Oklch::new(self.l - rhs.l, self.c - rhs.c, self.h - rhs.h)
193 }
194}
195
196impl SubAssign<f32> for Oklch {
197 #[inline]
198 fn sub_assign(&mut self, rhs: f32) {
199 self.l -= rhs;
200 self.c -= rhs;
201 self.h -= rhs;
202 }
203}
204
205impl SubAssign<Oklch> for Oklch {
206 #[inline]
207 fn sub_assign(&mut self, rhs: Oklch) {
208 self.l -= rhs.l;
209 self.c -= rhs.c;
210 self.h -= rhs.h;
211 }
212}
213
214impl Div<f32> for Oklch {
215 type Output = Oklch;
216
217 #[inline]
218 fn div(self, rhs: f32) -> Self::Output {
219 Oklch::new(self.l / rhs, self.c / rhs, self.h / rhs)
220 }
221}
222
223impl Div<Oklch> for Oklch {
224 type Output = Oklch;
225
226 #[inline]
227 fn div(self, rhs: Oklch) -> Self::Output {
228 Oklch::new(self.l / rhs.l, self.c / rhs.c, self.h / rhs.h)
229 }
230}
231
232impl DivAssign<f32> for Oklch {
233 #[inline]
234 fn div_assign(&mut self, rhs: f32) {
235 self.l /= rhs;
236 self.c /= rhs;
237 self.h /= rhs;
238 }
239}
240
241impl DivAssign<Oklch> for Oklch {
242 #[inline]
243 fn div_assign(&mut self, rhs: Oklch) {
244 self.l /= rhs.l;
245 self.c /= rhs.c;
246 self.h /= rhs.h;
247 }
248}
249
250impl Neg for Oklch {
251 type Output = Oklch;
252
253 fn neg(self) -> Self::Output {
254 Oklch::new(-self.l, -self.c, -self.h)
255 }
256}
257
258impl Pow<f32> for Oklch {
259 type Output = Oklch;
260
261 #[inline]
262 fn pow(self, rhs: f32) -> Self::Output {
263 Oklch::new(self.l.powf(rhs), self.c.powf(rhs), self.h.powf(rhs))
264 }
265}
266
267impl Pow<Oklch> for Oklch {
268 type Output = Oklch;
269
270 #[inline]
271 fn pow(self, rhs: Oklch) -> Self::Output {
272 Oklch::new(self.l.powf(rhs.l), self.c.powf(rhs.c), self.h.powf(rhs.h))
273 }
274}
275
276impl Oklch {
277 #[inline]
278 pub fn sqrt(&self) -> Oklch {
279 Oklch::new(
280 if self.l < 0. { 0. } else { self.l.sqrt() },
281 if self.c < 0. { 0. } else { self.c.sqrt() },
282 if self.h < 0. { 0. } else { self.h.sqrt() },
283 )
284 }
285
286 #[inline]
287 pub fn cbrt(&self) -> Oklch {
288 Oklch::new(self.l.cbrt(), self.c.cbrt(), self.h.cbrt())
289 }
290}