1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

use cie1931::Cie1931xyY;

// Color Temperature in Kelvin
#[derive(Debug, Clone, Copy)]
pub struct ColorTemp(pub u16);

impl ColorTemp {
    pub fn new(k: u16) -> ColorTemp {
        ColorTemp(k)
    }
}

impl ColorTemp {
    #[allow(non_snake_case)]
    pub fn to_cie1931xyY(&self) -> Option<Cie1931xyY>
    {
        // We use the Planckian locus to compute this, using the approximation
        // given in https://en.wikipedia.org/wiki/Planckian_locus

        // Only works for the given range:
        if self.0 < 1667 { return None; }
        if self.0 > 25000 { return None; }

        let ct = self.0 as f32;

        let x = if ct < 4000.0 {
            -0.2661239 * (10.0_f32).powi(9) / ct.powi(3)
                - 0.2343580 * (10.0_f32).powi(6) / ct.powi(2)
                + 0.8776956 * (10.0_f32).powi(3) / ct
                + 0.179910
        } else {
            -3.0258469 * (10.0_f32).powi(9) / ct.powi(3)
                + 2.1070379 * (10.0_f32).powi(6) / ct.powi(2)
                + 0.2226347 * (10.0_f32).powi(3) / ct
                + 0.240390
        };
        let y = if ct < 2222.0 {
            -1.1063814 * x.powi(3)
                - 1.34811020 * x.powi(2)
                + 2.18444832 * x
                - 0.20219683
        } else if ct < 4000.0 {
            -0.9549476 * x.powi(3)
                - 1.37418593 * x.powi(2)
                + 2.09137015 * x
                - 0.16748867
        } else {
            3.0817580 * x.powi(3)
                - 5.87338670 * x.powi(2)
                + 3.75112997 * x
                - 0.37001483
        };

        Some(Cie1931xyY::new(x, y, 1.0))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_planckian_locus() {
        let ct = ColorTemp::new(2222);
        let xy = ct.to_cie1931xyY().unwrap();
        assert!(xy.v.x > 0.5030);
        assert!(xy.v.x < 0.5035);
        assert!(xy.v.y > 0.4151);
        assert!(xy.v.y < 0.4154);

        let ct = ColorTemp::new(4000);
        let xy = ct.to_cie1931xyY().unwrap();
        assert!(xy.v.x > 0.3802);
        assert!(xy.v.x < 0.3807);
        assert!(xy.v.y > 0.3766);
        assert!(xy.v.y < 0.3769);

        let ct = ColorTemp::new(10000);
        let xy = ct.to_cie1931xyY().unwrap();
        assert!(xy.v.x > 0.2805);
        assert!(xy.v.x < 0.2808);
        assert!(xy.v.y > 0.2882);
        assert!(xy.v.y < 0.2884);
    }
}