Browse Source

colour is a source file

Inderjit Gill 1 year ago
parent
commit
4928164fb2
6 changed files with 709 additions and 20 deletions
  1. 0
    8
      Cargo.lock
  2. 0
    2
      Cargo.toml
  3. 675
    0
      src/colour.rs
  4. 12
    0
      src/error.rs
  5. 1
    1
      src/game.rs
  6. 21
    9
      src/lib.rs

+ 0
- 8
Cargo.lock View File

@@ -98,13 +98,6 @@ version = "0.1.9"
98 98
 source = "registry+https://github.com/rust-lang/crates.io-index"
99 99
 
100 100
 [[package]]
101
-name = "sen-colour"
102
-version = "0.2.0"
103
-dependencies = [
104
- "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
105
-]
106
-
107
-[[package]]
108 101
 name = "serde"
109 102
 version = "1.0.70"
110 103
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -223,7 +216,6 @@ name = "wasm_tetris"
223 216
 version = "0.1.0"
224 217
 dependencies = [
225 218
  "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
226
- "sen-colour 0.2.0",
227 219
  "wasm-bindgen 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
228 220
 ]
229 221
 

+ 0
- 2
Cargo.toml View File

@@ -9,8 +9,6 @@ crate-type = ["cdylib"]
9 9
 [dependencies]
10 10
 wasm-bindgen = "0.2.13"
11 11
 failure = "0.1.1"
12
-sen-colour = { path = "../sen-colour" }
13
-
14 12
 
15 13
 [profile.release]
16 14
 # Include function names in the `.wasm` for better debugging and

+ 675
- 0
src/colour.rs View File

@@ -0,0 +1,675 @@
1
+
2
+// |--------+-----------+-------------+-------------|
3
+// | format | element 0 | element 1   | element 2   |
4
+// |--------+-----------+-------------+-------------|
5
+// | RGB    | R 0..1    | G 0..1      | B 0..1      |
6
+// | HSL    | H 0..360  | S 0..1      | L 0..1      |
7
+// | HSLuv  | H 0..360  | S 0..100    | L 0..100    |
8
+// | LAB    | L 0..100  | A -128..128 | B -128..128 |
9
+// |--------+-----------+-------------+-------------|
10
+
11
+use error;
12
+use std;
13
+
14
+use sin;
15
+use cos;
16
+use pow;
17
+use sqrt;
18
+use atan2;
19
+
20
+const REF_U: f64 = 0.197_830_006_642_836_807_64;
21
+const REF_V: f64 = 0.468_319_994_938_791_003_70;
22
+
23
+//  http://www.brucelindbloom.com/index.html?Equations.html
24
+//  http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
25
+
26
+// we're using an sRGB working space with a D65 reference white
27
+
28
+// https://uk.mathworks.com/help/images/ref/whitepoint.html
29
+// the D65 whitepoint
30
+const WHITEPOINT_0: f64 = 0.9504;
31
+const WHITEPOINT_1: f64 = 1.0;
32
+const WHITEPOINT_2: f64 = 1.0888;
33
+
34
+const CIE_EPSILON: f64 = 0.008_856;
35
+const CIE_KAPPA: f64 = 903.3;
36
+
37
+// intent from the CIE
38
+//
39
+// #define CIE_EPSILON (216.0f / 24389.0f)
40
+// #define CIE_KAPPA (24389.0f / 27.0f)
41
+
42
+// RGB to XYZ (M)
43
+// 0.4124564  0.3575761  0.1804375
44
+// 0.2126729  0.7151522  0.0721750
45
+// 0.0193339  0.1191920  0.9503041
46
+
47
+// XYZ to RBG (M)^-1
48
+//  3.2404542 -1.5371385 -0.4985314
49
+// -0.9692660  1.8760108  0.0415560
50
+//  0.0556434 -0.2040259  1.0572252
51
+
52
+#[derive(Debug, PartialEq, Clone, Copy)]
53
+pub enum Format {
54
+    RGB,
55
+    HSLuv,
56
+    HSL,
57
+    LAB,
58
+    HSV,
59
+}
60
+
61
+#[derive(Debug, Clone, Copy)]
62
+pub enum Colour {
63
+    RGB(f64, f64, f64, f64),
64
+    HSLuv(f64, f64, f64, f64),
65
+    HSL(f64, f64, f64, f64),
66
+    LAB(f64, f64, f64, f64),
67
+    HSV(f64, f64, f64, f64),
68
+    XYZ(f64, f64, f64, f64),
69
+    LUV(f64, f64, f64, f64),
70
+    LCH(f64, f64, f64, f64),
71
+}
72
+
73
+impl Colour {
74
+    pub fn is_format(&self, format: Format) -> bool {
75
+        match format {
76
+            Format::RGB => {
77
+                match *self {
78
+                    Colour::RGB(_, _, _, _) => true,
79
+                    _ => false,
80
+                }
81
+            },
82
+            Format::HSLuv => {
83
+                match *self {
84
+                    Colour::HSLuv(_, _, _, _) => true,
85
+                    _ => false,
86
+                }
87
+            },
88
+            Format::HSL => {
89
+                match *self {
90
+                    Colour::HSL(_, _, _, _) => true,
91
+                    _ => false,
92
+                }
93
+            },
94
+            Format::LAB => {
95
+                match *self {
96
+                    Colour::LAB(_, _, _, _) => true,
97
+                    _ => false,
98
+                }
99
+            },
100
+            Format::HSV => {
101
+                match *self {
102
+                    Colour::HSV(_, _, _, _) => true,
103
+                    _ => false,
104
+                }
105
+            },
106
+        }
107
+    }
108
+
109
+    pub fn clone_as(&self, format: Format) -> error::Result<Colour> {
110
+        match *self {
111
+            Colour::HSL(h, s, l, alpha) => {
112
+                match format {
113
+                    Format::HSL => Ok(Colour::HSL(h, s, l, alpha)),
114
+                    Format::HSLuv => hsluv_from_xyz(xyz_from_rgb(rgb_from_hsl(*self)?)?),
115
+                    Format::HSV => hsv_from_rgb(rgb_from_hsl(*self)?),
116
+                    Format::LAB => lab_from_xyz(xyz_from_rgb(rgb_from_hsl(*self)?)?),
117
+                    Format::RGB => rgb_from_hsl(*self),
118
+                }
119
+            },
120
+            Colour::HSLuv(h, s, l, alpha) => {
121
+                match format {
122
+                    Format::HSL => hsl_from_rgb(rgb_from_xyz(xyz_from_hsluv(*self)?)?),
123
+                    Format::HSLuv => Ok(Colour::HSLuv(h, s, l, alpha)),
124
+                    Format::HSV => hsv_from_rgb(rgb_from_xyz(xyz_from_hsluv(*self)?)?),
125
+                    Format::LAB => lab_from_xyz(xyz_from_hsluv(*self)?),
126
+                    Format::RGB => rgb_from_xyz(xyz_from_hsluv(*self)?),
127
+                }
128
+            },
129
+            Colour::HSV(h, s, v, alpha) => {
130
+                match format {
131
+                    Format::HSL => hsl_from_rgb(rgb_from_hsv(*self)?),
132
+                    Format::HSLuv => hsluv_from_xyz(xyz_from_rgb(rgb_from_hsv(*self)?)?),
133
+                    Format::HSV => Ok(Colour::HSV(h, s, v, alpha)),
134
+                    Format::LAB => lab_from_xyz(xyz_from_rgb(rgb_from_hsv(*self)?)?),
135
+                    Format::RGB => rgb_from_hsv(*self),
136
+                }
137
+            },
138
+            Colour::LAB(l, a, b, alpha) => {
139
+                match format {
140
+                    Format::HSL => hsl_from_rgb(rgb_from_xyz(xyz_from_lab(*self)?)?),
141
+                    Format::HSLuv => hsluv_from_xyz(xyz_from_lab(*self)?),
142
+                    Format::HSV => hsv_from_rgb(rgb_from_xyz(xyz_from_lab(*self)?)?),
143
+                    Format::LAB => Ok(Colour::LAB(l, a, b, alpha)),
144
+                    Format::RGB => rgb_from_xyz(xyz_from_lab(*self)?),
145
+                }
146
+            },
147
+            Colour::RGB(r, g, b, alpha) => {
148
+                match format {
149
+                    Format::HSL => hsl_from_rgb(*self),
150
+                    Format::HSLuv => hsluv_from_xyz(xyz_from_rgb(*self)?),
151
+                    Format::HSV => hsv_from_rgb(*self),
152
+                    Format::LAB => lab_from_xyz(xyz_from_rgb(*self)?),
153
+                    Format::RGB => Ok(Colour::RGB(r, g, b, alpha)),
154
+                }
155
+            },
156
+            _ => Err(error::TetrisError::IncorrectFormat)
157
+        }
158
+    }
159
+}
160
+
161
+fn colour_to_axis(component: f64) -> f64{
162
+    if component > 0.04045 {
163
+        pow((component + 0.055) / 1.055, 2.4)
164
+    } else {
165
+        (component / 12.92)
166
+    }
167
+}
168
+
169
+fn axis_to_colour(a: f64) -> f64 {
170
+    if a > 0.003_130_8 {
171
+        (1.055 * pow(a, 1.0 / 2.4)) - 0.055
172
+    } else {
173
+        a * 12.92
174
+    }
175
+}
176
+
177
+fn xyz_from_rgb(rgb: Colour) -> error::Result<Colour> {
178
+    match rgb {
179
+        Colour::RGB(r, g, b, alpha) => {
180
+            let rr = colour_to_axis(r);
181
+            let gg = colour_to_axis(g);
182
+            let bb = colour_to_axis(b);
183
+
184
+            // multiply by matrix
185
+            // see http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
186
+            // sRGB colour space with D65 reference white
187
+            //
188
+
189
+            let x = (rr * 0.412_390_799_265_959_5) +
190
+                (gg * 0.357_584_339_383_877_96) + (bb * 0.180_480_788_401_834_3);
191
+            let y = (rr * 0.212_639_005_871_510_36) +
192
+                (gg * 0.715_168_678_767_755_927_46) + (bb * 0.072_192_315_360_733_715_00);
193
+            let z = (rr * 0.019_330_818_715_591_850_69) +
194
+                (gg * 0.119_194_779_794_625_987_91) + (bb * 0.950_532_152_249_660_580_86);
195
+
196
+            Ok(Colour::XYZ(x, y, z, alpha))
197
+        },
198
+        _ => Err(error::TetrisError::IncorrectFormat)
199
+    }
200
+}
201
+
202
+fn rgb_from_xyz(xyz: Colour) -> error::Result<Colour> {
203
+    match xyz {
204
+        Colour::XYZ(x, y, z, alpha) => {
205
+            let r = (x * 3.240_969_941_904_521_343_77) + (y * -1.537_383_177_570_093_457_94) +
206
+                (z * -0.498_610_760_293_003_283_66);
207
+            let g = (x * -0.969_243_636_280_879_826_13) + (y * 1.875_967_501_507_720_667_72) +
208
+                (z * 0.041_555_057_407_175_612_47);
209
+            let b = (x * 0.055_630_079_696_993_608_46) + (y * -0.203_976_958_888_976_564_35) +
210
+                (z * 1.056_971_514_242_878_560_72);
211
+
212
+            let rr = axis_to_colour(r);
213
+            let gg = axis_to_colour(g);
214
+            let bb = axis_to_colour(b);
215
+
216
+            Ok(Colour::RGB(rr, gg, bb, alpha))
217
+        },
218
+        _ => Err(error::TetrisError::IncorrectFormat)
219
+    }
220
+}
221
+
222
+fn axis_to_lab_component(a: f64) -> f64 {
223
+    if a > CIE_EPSILON {
224
+        a.cbrt()
225
+    } else {
226
+        ((CIE_KAPPA * a) + 16.0) / 116.0
227
+    }
228
+}
229
+
230
+fn lab_from_xyz(xyz: Colour) -> error::Result<Colour> {
231
+    match xyz {
232
+        Colour::XYZ(x, y, z, alpha) => {
233
+            let xr = x / WHITEPOINT_0;
234
+            let yr = y / WHITEPOINT_1;
235
+            let zr = z / WHITEPOINT_2;
236
+
237
+            let fx = axis_to_lab_component(xr);
238
+            let fy = axis_to_lab_component(yr);
239
+            let fz = axis_to_lab_component(zr);
240
+
241
+            let l = (116.0 * fy) - 16.0;
242
+            let a = 500.0 * (fx - fy);
243
+            let b = 200.0 * (fy - fz);
244
+
245
+            Ok(Colour::LAB(l, a, b, alpha))
246
+        },
247
+        _ => Err(error::TetrisError::IncorrectFormat)
248
+    }
249
+}
250
+
251
+fn max_channel(r: f64, g: f64, b: f64) -> i32 {
252
+    let hi = if r > g { 0 } else { 1 };
253
+    let hival = if r > g { r } else { g };
254
+
255
+    if b > hival { 2 } else { hi }
256
+}
257
+
258
+// TODO: implement a better fmod, this one is not exact
259
+fn fmod(a: f64, b: f64) -> f64 {
260
+    a - b * (a / b).floor()
261
+}
262
+
263
+// http://www.rapidtables.com/convert/color/rgb-to-hsl.htm
264
+fn hue(colour: Colour, max_chan: i32, chroma: f64) -> error::Result<f64> {
265
+    if chroma == 0.0 {
266
+        // return Err(error::TetrisError::InvalidHue)
267
+        return Ok(0.0)
268
+    }
269
+
270
+    let mut angle: f64;
271
+
272
+    match colour {
273
+        Colour::RGB(r, g, b, _) => angle = match max_chan {
274
+            0 => fmod((g - b) / chroma, 6.0),
275
+            1 => ((b - r) / chroma) + 2.0,
276
+            2 => ((r - g) / chroma) + 4.0,
277
+            _ => return Err(error::TetrisError::InvalidChannel)
278
+        },
279
+        _ => return Err(error::TetrisError::IncorrectFormat)
280
+    }
281
+
282
+    angle *= 60.0;
283
+
284
+    while angle < 0.0 {
285
+        angle += 360.0;
286
+    }
287
+
288
+    Ok(angle)
289
+}
290
+
291
+// http://www.rapidtables.com/convert/color/rgb-to-hsl.htm
292
+fn hsl_from_rgb(colour: Colour) -> error::Result<Colour> {
293
+    match colour {
294
+        Colour::RGB(r, g, b, alpha) => {
295
+
296
+            let min_val = r.min(g).min(b);
297
+            let max_val = r.max(g).max(b);
298
+            let max_ch = max_channel(r, g, b);
299
+
300
+            let delta = max_val - min_val;
301
+
302
+            let h = hue(colour, max_ch, delta)?;
303
+            let lightness = 0.5 * (min_val + max_val);
304
+            let saturation: f64 = if delta == 0.0 {
305
+                0.0
306
+            } else {
307
+                delta / (1.0 - ((2.0 * lightness) - 1.0).abs())
308
+            };
309
+
310
+            Ok(Colour::HSL(h, saturation, lightness, alpha))
311
+        },
312
+        _ => Err(error::TetrisError::IncorrectFormat)
313
+    }
314
+
315
+}
316
+
317
+fn hsv_from_rgb(colour: Colour) -> error::Result<Colour> {
318
+    match colour {
319
+        Colour::RGB(r, g, b, alpha) => {
320
+
321
+            let min_val = r.min(g).min(b);
322
+            let max_val = r.max(g).max(b);
323
+            let max_ch = max_channel(r, g, b);
324
+
325
+            let chroma = max_val - min_val;
326
+            let h = hue(colour, max_ch, chroma)?;
327
+
328
+            // valid_hue: bool = chroma != 0.0;
329
+
330
+            let saturation: f64 = if chroma == 0.0 {
331
+                0.0
332
+            } else {
333
+                chroma / max_val
334
+            };
335
+
336
+            // TODO: set valid_hue
337
+            // return col.set('valid_hue', valid_hue);
338
+
339
+            Ok(Colour::HSV(h, saturation, max_val, alpha))
340
+        },
341
+        _ => Err(error::TetrisError::IncorrectFormat)
342
+    }
343
+}
344
+
345
+fn rgb_from_chm(chroma: f64, h: f64, m: f64, alpha: f64) -> Colour {
346
+    // todo: validhue test
347
+    //
348
+    // if (c.get('validHue') === undefined) {
349
+    // return construct(Format.RGB, [m, m, m, element(c, ALPHA)]);
350
+    //}
351
+
352
+    let hprime = h / 60.0;
353
+    let x = chroma * (1.0 - (fmod(hprime, 2.0) - 1.0).abs());
354
+    let mut r = 0.0;
355
+    let mut g = 0.0;
356
+    let mut b = 0.0;
357
+
358
+    if hprime < 1.0 {
359
+        r = chroma;
360
+        g = x;
361
+        b = 0.0;
362
+    } else if hprime < 2.0 {
363
+        r = x;
364
+        g = chroma;
365
+        b = 0.0;
366
+    } else if hprime < 3.0 {
367
+        r = 0.0;
368
+        g = chroma;
369
+        b = x;
370
+    } else if hprime < 4.0 {
371
+        r = 0.0;
372
+        g = x;
373
+        b = chroma;
374
+    } else if hprime < 5.0 {
375
+        r = x;
376
+        g = 0.0;
377
+        b = chroma;
378
+    } else if hprime < 6.0 {
379
+        r = chroma;
380
+        g = 0.0;
381
+        b = x;
382
+    }
383
+
384
+    Colour::RGB(r + m, g + m, b + m, alpha)
385
+}
386
+
387
+fn rgb_from_hsl(hsl: Colour) -> error::Result<Colour> {
388
+    match hsl {
389
+        Colour::HSL(h, s, l, alpha) => {
390
+            let chroma = (1.0 - ((2.0 * l) - 1.0).abs()) * s;
391
+            let m = l - (0.5 * chroma);
392
+
393
+            // todo: set validhue
394
+            // f64 col = c.set('validHue', true);
395
+
396
+            Ok(rgb_from_chm(chroma, h, m, alpha))
397
+        },
398
+        _ => Err(error::TetrisError::IncorrectFormat)
399
+    }
400
+}
401
+
402
+fn lab_component_to_axis(l: f64) -> f64 {
403
+    if pow(l, 3.0) > CIE_EPSILON {
404
+        pow(l, 3.0)
405
+    } else {
406
+        ((116.0 * l) - 16.0) / CIE_KAPPA
407
+    }
408
+}
409
+
410
+fn xyz_from_lab(lab: Colour) -> error::Result<Colour> {
411
+    match lab {
412
+        Colour::LAB(l, a, b, alpha) => {
413
+
414
+            let fy = (l + 16.0) / 116.0;
415
+            let fz = fy - (b / 200.0);
416
+            let fx = (a / 500.0) + fy;
417
+
418
+            let xr = lab_component_to_axis(fx);
419
+            let mut yr;
420
+            if l > (CIE_EPSILON * CIE_KAPPA) {
421
+                yr = (l + 16.0) / 116.0;
422
+                yr = yr * yr * yr;
423
+            } else {
424
+                yr = l / CIE_KAPPA;
425
+            }
426
+            let zr = lab_component_to_axis(fz);
427
+
428
+            Ok(Colour::XYZ(WHITEPOINT_0 * xr, WHITEPOINT_1 * yr, WHITEPOINT_2 * zr, alpha))
429
+        },
430
+        _ => Err(error::TetrisError::IncorrectFormat)
431
+    }
432
+}
433
+
434
+fn rgb_from_hsv(hsv: Colour) -> error::Result<Colour> {
435
+    match hsv {
436
+        Colour::HSV(h, s, v, alpha) => {
437
+            let chroma = v * s;
438
+            let m = v - chroma;
439
+
440
+            Ok(rgb_from_chm(chroma, h, m, alpha))
441
+        },
442
+        _ => Err(error::TetrisError::IncorrectFormat)
443
+    }
444
+}
445
+
446
+// the luv and hsluv code is based on https://github.com/hsluv/hsluv-c
447
+// which uses the MIT License:
448
+
449
+// # The MIT License (MIT)
450
+
451
+// Copyright © 2015 Alexei Boronine (original idea, JavaScript implementation)
452
+// Copyright © 2015 Roger Tallada (Obj-C implementation)
453
+// Copyright © 2017 Martin Mitáš (C implementation, based on Obj-C
454
+// implementation)
455
+
456
+// Permission is hereby granted, free of charge, to any person obtaining a
457
+// copy of this software and associated documentation files (the “Software”),
458
+// to deal in the Software without restriction, including without limitation
459
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
460
+// and/or sell copies of the Software, and to permit persons to whom the
461
+// Software is furnished to do so, subject to the following conditions:
462
+
463
+// The above copyright notice and this permission notice shall be included
464
+// in all copies or substantial portions of the Software.
465
+
466
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
467
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
468
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
469
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
470
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
471
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
472
+// IN THE SOFTWARE.
473
+
474
+#[derive(Debug, Clone, Copy)]
475
+struct Bounds {
476
+    a: f64,
477
+    b: f64,
478
+}
479
+
480
+fn get_bounds(l: f64, bounds: &mut [Bounds]) {
481
+    let tl = l + 16.0;
482
+    let sub1 = (tl * tl * tl) / 1_560_896.0;
483
+    let sub2 = if sub1 > CIE_EPSILON { sub1 } else { l / CIE_KAPPA };
484
+
485
+    let mut m = [[0f64; 3]; 3];
486
+    m[0][0] =  3.240_969_941_904_521_343_77;
487
+    m[0][1] = -1.537_383_177_570_093_457_94;
488
+    m[0][2] = -0.498_610_760_293_003_283_66;
489
+    m[1][0] = -0.969_243_636_280_879_826_13;
490
+    m[1][1] =  1.875_967_501_507_720_667_72;
491
+    m[1][2] =  0.041_555_057_407_175_612_47;
492
+    m[2][0] =  0.055_630_079_696_993_608_46;
493
+    m[2][1] = -0.203_976_958_888_976_564_35;
494
+    m[2][2] =  1.056_971_514_242_878_560_72;
495
+
496
+    for channel in 0..3 {
497
+        let m1 = m[channel][0];
498
+        let m2 = m[channel][1];
499
+        let m3 = m[channel][2];
500
+
501
+        for t in 0..2 {
502
+            let top1 = (284_517.0 * m1 - 94_839.0 * m3) * sub2;
503
+            let top2 = (838_422.0 * m3 + 769_860.0 * m2 + 731_718.0 * m1) * l * sub2 -
504
+                769_860.0 * (t as f64) * l;
505
+            let bottom = (632_260.0 * m3 - 126_452.0 * m2) * sub2 + 126_452.0 * (t as f64);
506
+
507
+            bounds[channel * 2 + t].a = top1 / bottom;
508
+            bounds[channel * 2 + t].b = top2 / bottom;
509
+        }
510
+    }
511
+}
512
+
513
+fn ray_length_until_intersect(theta: f64, line: &Bounds) -> f64 {
514
+    line.b / (sin(theta) - line.a * cos(theta))
515
+}
516
+
517
+fn max_chroma_for_lh(l: f64, h: f64) -> f64 {
518
+    let mut min_len = std::f64::MAX;
519
+    let hrad = h * 0.017_453_292_519_943_295_77; /* (2 * pi / 260) */
520
+    let mut bounds = [Bounds { a: 0.0, b: 0.0 }; 6];
521
+
522
+    get_bounds(l, &mut bounds);
523
+
524
+    for b in &bounds {
525
+        let l2 = ray_length_until_intersect(hrad, &b);
526
+
527
+        if l2 >= 0.0 && l2 < min_len {
528
+            min_len = l2;
529
+        }
530
+    }
531
+
532
+    min_len
533
+}
534
+
535
+/* http://en.wikipedia.org/wiki/CIELUV
536
+ * In these formulas, Yn refers to the reference white point. We are using
537
+ * illuminant D65, so Yn (see refY in Maxima file) equals 1. The formula is
538
+ * simplified accordingly.
539
+ */
540
+fn y2l(y: f64) -> f64 {
541
+    if y <= CIE_EPSILON {
542
+        y * CIE_KAPPA
543
+    } else {
544
+        116.0 * y.cbrt() - 16.0
545
+    }
546
+}
547
+
548
+fn l2y(l: f64) -> f64 {
549
+    if l <= 8.0 {
550
+        l / CIE_KAPPA
551
+    } else {
552
+        let x = (l + 16.0) / 116.0;
553
+        x * x * x
554
+    }
555
+}
556
+
557
+fn luv_from_xyz(xyz: Colour) -> error::Result<Colour> {
558
+    match xyz {
559
+        Colour::XYZ(x, y, z, alpha) => {
560
+            let var_u = (4.0 * x) / (x + (15.0 * y) + (3.0 * z));
561
+            let var_v = (9.0 * y) / (x + (15.0 * y) + (3.0 * z));
562
+            let l = y2l(y);
563
+            let u = 13.0 * l * (var_u - REF_U);
564
+            let v = 13.0 * l * (var_v - REF_V);
565
+
566
+            if l < 0.000_000_01 {
567
+                Ok(Colour::LUV(l, 0.0, 0.0, alpha))
568
+            } else {
569
+                Ok(Colour::LUV(l, u, v, alpha))
570
+            }
571
+        },
572
+        _ => Err(error::TetrisError::IncorrectFormat)
573
+    }
574
+}
575
+
576
+fn xyz_from_luv(luv: Colour) -> error::Result<Colour> {
577
+    match luv {
578
+        Colour::LUV(l, u, v, alpha) =>  {
579
+            if l <= 0.000_000_01 {
580
+                return Ok(Colour::XYZ(0.0, 0.0, 0.0, alpha))
581
+            }
582
+
583
+            let var_u = u / (13.0 * l) + REF_U;
584
+            let var_v = v / (13.0 * l) + REF_V;
585
+            let y = l2y(l);
586
+            let x = -(9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v);
587
+            let z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v);
588
+
589
+            Ok(Colour::XYZ(x, y, z, alpha))
590
+        },
591
+        _ => Err(error::TetrisError::IncorrectFormat)
592
+    }
593
+}
594
+
595
+fn lch_from_luv(luv: Colour) -> error::Result<Colour> {
596
+
597
+    match luv {
598
+        Colour::LUV(l, u, v, alpha) => {
599
+            let mut h: f64;
600
+            let c = sqrt(u * u + v * v);
601
+
602
+            if c < 0.000_000_01 {
603
+                h = 0.0;
604
+            } else {
605
+                h = atan2(v, u) * 57.295_779_513_082_320_876_80; /* (180 / pi) */
606
+                if h < 0.0 {
607
+                    h += 360.0;
608
+                }
609
+            }
610
+
611
+            Ok(Colour::LCH(l, c, h, alpha))
612
+        },
613
+        _ => Err(error::TetrisError::IncorrectFormat)
614
+    }
615
+
616
+}
617
+
618
+fn luv_from_lch(lch: Colour) -> error::Result<Colour> {
619
+    match lch {
620
+        Colour::LCH(l, c, h, alpha) => {
621
+            let hrad = h * 0.017_453_292_519_943_295_77; /* (pi / 180.0) */
622
+            let u = cos(hrad) * c;
623
+            let v = sin(hrad) * c;
624
+
625
+            Ok(Colour::LUV(l, u, v, alpha))
626
+        },
627
+        _ => Err(error::TetrisError::IncorrectFormat)
628
+    }
629
+}
630
+
631
+fn lch_from_hsluv(hsluv: Colour) -> error::Result<Colour> {
632
+    match hsluv {
633
+        Colour::HSLuv(h, s, l, alpha) => {
634
+            let c = if l > 99.999_999_9 || l < 0.000_000_01 {
635
+                0.0
636
+            } else {
637
+                max_chroma_for_lh(l, h) / 100.0 * s
638
+            };
639
+
640
+            if s < 0.000_000_01 {
641
+                Ok(Colour::LCH(l, c, 0.0, alpha))
642
+            } else {
643
+                Ok(Colour::LCH(l, c, h, alpha))
644
+            }
645
+        },
646
+        _ => Err(error::TetrisError::IncorrectFormat)
647
+    }
648
+}
649
+
650
+fn hsluv_from_lch(lch: Colour) -> error::Result<Colour> {
651
+    match lch {
652
+        Colour::LCH(l, c, h, alpha) => {
653
+            let s = if l > 99.999_999_9 || l < 0.000_000_01 {
654
+                0.0
655
+            } else {
656
+                c / max_chroma_for_lh(l, h) * 100.0
657
+            };
658
+
659
+            if c < 0.000_000_01 {
660
+                Ok(Colour::HSLuv(0.0, s, l, alpha))
661
+            } else {
662
+                Ok(Colour::HSLuv(h, s, l, alpha))
663
+            }
664
+        },
665
+        _ => Err(error::TetrisError::IncorrectFormat)
666
+    }
667
+}
668
+
669
+fn xyz_from_hsluv(hsluv: Colour) -> error::Result<Colour> {
670
+  xyz_from_luv(luv_from_lch(lch_from_hsluv(hsluv)?)?)
671
+}
672
+
673
+fn hsluv_from_xyz(xyz: Colour) -> error::Result<Colour> {
674
+  hsluv_from_lch(lch_from_luv(luv_from_xyz(xyz)?)?)
675
+}

+ 12
- 0
src/error.rs View File

@@ -14,4 +14,16 @@ pub enum TetrisError {
14 14
 
15 15
     #[fail(display = "Invalid Colour Format")]
16 16
     InvalidColourFormat,
17
+
18
+    #[fail(display = "General")]
19
+    GeneralError,
20
+
21
+    #[fail(display = "IncorrectFormat")]
22
+    IncorrectFormat,
23
+
24
+    #[fail(display = "InvalidHue")]
25
+    InvalidHue,
26
+
27
+    #[fail(display = "InvalidChannel")]
28
+    InvalidChannel,
17 29
 }

+ 1
- 1
src/game.rs View File

@@ -3,7 +3,7 @@ use error;
3 3
 use geometry::Geometry;
4 4
 use log;
5 5
 use audio_play;
6
-use sen_colour::{Colour, Format};
6
+use colour::{Colour, Format};
7 7
 use sprite::Sprite;
8 8
 use std::collections::HashMap;
9 9
 use units::*;

+ 21
- 9
src/lib.rs View File

@@ -6,25 +6,37 @@
6 6
 
7 7
 #[macro_use]
8 8
 extern crate failure;
9
-extern crate sen_colour;
10 9
 extern crate wasm_bindgen;
11 10
 
12 11
 use wasm_bindgen::prelude::*;
13 12
 
14
-pub mod controller;
15
-pub mod error;
16
-pub mod game;
17
-pub mod geometry;
18
-pub mod sprite;
19
-pub mod text;
20
-pub mod units;
13
+mod controller;
14
+mod error;
15
+mod game;
16
+mod colour;
17
+mod geometry;
18
+mod sprite;
19
+mod text;
20
+mod units;
21 21
 
22 22
 use controller::{Controller, ControllerAction, ControllerButton};
23 23
 use game::Game;
24 24
 use geometry::Geometry;
25 25
 
26
-#[wasm_bindgen(js_namespace = console)]
26
+#[wasm_bindgen]
27 27
 extern "C" {
28
+    #[wasm_bindgen(js_namespace = Math)]
29
+    fn sin(a: f64) -> f64;
30
+    #[wasm_bindgen(js_namespace = Math)]
31
+    fn cos(a: f64) -> f64;
32
+    #[wasm_bindgen(js_namespace = Math)]
33
+    fn pow(a: f64, b: f64) -> f64;
34
+    #[wasm_bindgen(js_namespace = Math)]
35
+    fn sqrt(a: f64) -> f64;
36
+    #[wasm_bindgen(js_namespace = Math)]
37
+    fn atan2(a: f64, b: f64) -> f64;
38
+
39
+    #[wasm_bindgen(js_namespace = console)]
28 40
     fn log(s: &str);
29 41
 }
30 42
 

Loading…
Cancel
Save