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
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! A simple crate that calculates color temperatures
//!
//! It implements an algorithm by Tanner Helland that calculates
//! RGB values for specific color temperatures. It can also do
//! the inverse by aproxmiation.
//!
//! This crate includes unit tests to ensure functionality
//!
//! ## Examples
//!
//! Following are some simple examples of how to use this crate
//!
//! ```rust
//! extern crate colortemp;
//!
//! let mut rgb = colortemp::temp_to_rgb(2000);
//! println!("{:?}", rgb);
//! ```
//!
//! The values generated by this crate can include uncertainty
//! and might therefore not be suitable for scientific computing.
//!
//! If you wish to change this, PR's are always welcome 😁

mod tests;

macro_rules! normalise {
    ($x:expr) => {{
        if $x < 0.0 {
            $x = 0.;
        } else if $x > 255.0 {
            $x = 255.0;
        }
    }};
}

pub use colortemp::*;
mod colortemp {


    /// A simple container format to describe an RGB value
    #[derive(Debug, PartialEq)]
    pub struct RGB {
        pub r: f64,
        pub g: f64,
        pub b: f64,
    }


    // Calculate the RGB value of a color temperature (in Kelvin)
    pub fn temp_to_rgb(kelvin: i64) -> RGB {
        let (mut r, mut g, mut b);
        let temp = kelvin / 100;

        /* Calculate red */
        if temp <= 66 {
            r = 255.;
        } else {
            r = (temp as f64) - 60.;
            r = 329.698727446 * r.powf(-329.698727446);
            normalise!(r);
        }

        /* Calculate green */
        if temp <= 66 {
            g = temp as f64;
            g = 99.4708025861 * g.ln() - 161.1195681661;
            normalise!(g);
        } else {
            g = temp as f64 - 60.;
            g = 288.1221695283 * g.powf(-0.0755148492);
            normalise!(g);
        }

        /* Feeling bluueeee */
        if temp >= 66 {
            b = 255.;
        } else {

            if temp <= 19 {
                b = 0.;
            } else {
                b = temp as f64 - 10.;
                b = 138.5177312231 * b.ln() - 305.0447927307;
                normalise!(b);
            }
        }

        return RGB {
            r: r.round(),
            g: g.round(),
            b: b.round(),
        };
    }


    /// Calculates the color temperature for a given RGB value
    ///
    /// This is implemented via a reverse lookup onto @temperature_to_rgb and
    /// as such should be considered rather slow
    ///
    pub fn rgb_to_temp(col: RGB) -> i64 {
        let (r, b) = (col.r, col.b);

        let mut temp = 0;
        let mut test_rgb;

        let epsilon = 2.;
        let (mut min, mut max) = (1000, 40000);

        while (max as f64) - (min as f64) > epsilon {
            temp = (max + min) / 2;
            test_rgb = temp_to_rgb(temp);
            if (test_rgb.b / test_rgb.r) >= (b / r) {
                max = temp;
            } else {
                min = temp;
            }
        }

        return temp;
    }
}