1 // Written in the D programming language. 2 3 /** 4 This module implements XYZ and xyY _color types. 5 6 Authors: Manu Evans 7 Copyright: Copyright (c) 2015, Manu Evans. 8 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 Source: $(PHOBOSSRC ggplotd/color/xyz.d) 10 */ 11 module ggplotd.color.xyz; 12 13 import ggplotd.color; 14 import ggplotd.color.conv : convertColor; 15 16 import std.traits : isFloatingPoint, isIntegral, isSigned, isSomeChar, Unqual; 17 import std.typetuple : TypeTuple; 18 import std.typecons : tuple; 19 20 @safe pure nothrow @nogc: 21 22 23 /** 24 Detect whether $(D T) is an XYZ color. 25 */ 26 enum isXYZ(T) = isInstanceOf!(XYZ, T); 27 28 /// 29 unittest 30 { 31 static assert(isXYZ!(XYZ!float) == true); 32 static assert(isXYZ!(xyY!double) == false); 33 } 34 35 36 /** 37 Detect whether $(D T) is an xyY color. 38 */ 39 enum isxyY(T) = isInstanceOf!(xyY, T); 40 41 /// 42 unittest 43 { 44 static assert(isxyY!(xyY!float) == true); 45 static assert(isxyY!(XYZ!double) == false); 46 } 47 48 49 /** White points of standard illuminants. */ 50 template WhitePoint(F) if(isFloatingPoint!F) 51 { 52 /** */ 53 enum WhitePoint 54 { 55 /** Incandescent / Tungsten */ 56 A = xyY!F(0.44757, 0.40745, 1.00000), 57 /** [obsolete] Direct sunlight at noon */ 58 B = xyY!F(0.34842, 0.35161, 1.00000), 59 /** [obsolete] Average / North sky Daylight */ 60 C = xyY!F(0.31006, 0.31616, 1.00000), 61 /** Horizon Light, ICC profile PCS (Profile connection space) */ 62 D50 = xyY!F(0.34567, 0.35850, 1.00000), 63 /** Mid-morning / Mid-afternoon Daylight */ 64 D55 = xyY!F(0.33242, 0.34743, 1.00000), 65 /** Noon Daylight: Television, sRGB color space */ 66 D65 = xyY!F(0.31271, 0.32902, 1.00000), 67 /** North sky Daylight */ 68 D75 = xyY!F(0.29902, 0.31485, 1.00000), 69 /** Equal energy */ 70 E = xyY!F(1.0/3.0, 1.0/3.0, 1.00000), 71 /** Daylight Fluorescent */ 72 F1 = xyY!F(0.31310, 0.33727, 1.00000), 73 /** Cool White Fluorescent */ 74 F2 = xyY!F(0.37208, 0.37529, 1.00000), 75 /** White Fluorescent */ 76 F3 = xyY!F(0.40910, 0.39430, 1.00000), 77 /** Warm White Fluorescent */ 78 F4 = xyY!F(0.44018, 0.40329, 1.00000), 79 /** Daylight Fluorescent */ 80 F5 = xyY!F(0.31379, 0.34531, 1.00000), 81 /** Lite White Fluorescent */ 82 F6 = xyY!F(0.37790, 0.38835, 1.00000), 83 /** D65 simulator, Daylight simulator */ 84 F7 = xyY!F(0.31292, 0.32933, 1.00000), 85 /** D50 simulator, Sylvania F40 Design 50 */ 86 F8 = xyY!F(0.34588, 0.35875, 1.00000), 87 /** Cool White Deluxe Fluorescent */ 88 F9 = xyY!F(0.37417, 0.37281, 1.00000), 89 /** Philips TL85, Ultralume 50 */ 90 F10 = xyY!F(0.34609, 0.35986, 1.00000), 91 /** Philips TL84, Ultralume 40 */ 92 F11 = xyY!F(0.38052, 0.37713, 1.00000), 93 /** Philips TL83, Ultralume 30 */ 94 F12 = xyY!F(0.43695, 0.40441, 1.00000) 95 } 96 } 97 98 99 /** 100 A CIE 1931 XYZ color, parameterised for component type. 101 */ 102 struct XYZ(F = float) if(isFloatingPoint!F) 103 { 104 @safe pure nothrow @nogc: 105 106 /** Type of the color components. */ 107 alias ComponentType = F; 108 109 /** X value. */ 110 F X = 0; 111 /** Y value. */ 112 F Y = 0; 113 /** Z value. */ 114 F Z = 0; 115 116 /** Return the XYZ tristimulus values as a tuple. */ 117 @property auto tristimulus() const 118 { 119 return tuple(X, Y, Z); 120 } 121 122 /** Construct a color from XYZ values. */ 123 this(ComponentType X, ComponentType Y, ComponentType Z) 124 { 125 this.X = X; 126 this.Y = Y; 127 this.Z = Z; 128 } 129 130 // casts 131 Color opCast(Color)() const if(isColor!Color) 132 { 133 return convertColor!Color(this); 134 } 135 136 // operators 137 mixin ColorOperators!(TypeTuple!("X","Y","Z")); 138 } 139 140 /// 141 unittest 142 { 143 // CIE XYZ 1931 color with float components 144 alias XYZf = XYZ!float; 145 146 XYZf c = XYZf(0.8, 1, 1.2); 147 148 // tristimulus() returns a tuple of the components 149 assert(c.tristimulus == tuple(c.X, c.Y, c.Z)); 150 151 // test XYZ operators and functions 152 static assert(XYZf(0, 0.5, 0) + XYZf(0.5, 0.5, 1) == XYZf(0.5, 1, 1)); 153 static assert(XYZf(0.5, 0.5, 1) * 100.0 == XYZf(50, 50, 100)); 154 } 155 156 157 /** 158 A CIE 1931 xyY color, parameterised for component type. 159 */ 160 struct xyY(F = float) if(isFloatingPoint!F) 161 { 162 @safe pure nothrow @nogc: 163 164 /** Type of the color components. */ 165 alias ComponentType = F; 166 167 /** x coordinate. */ 168 F x = 0; 169 /** y coordinate. */ 170 F y = 0; 171 /** Y value (luminance). */ 172 F Y = 0; 173 174 /** Construct a color from xyY values. */ 175 this(ComponentType x, ComponentType y, ComponentType Y) 176 { 177 this.x = x; 178 this.y = y; 179 this.Y = Y; 180 } 181 182 // casts 183 Color opCast(Color)() const if(isColor!Color) 184 { 185 return convertColor!Color(this); 186 } 187 188 // operators 189 mixin ColorOperators!(TypeTuple!("x","y","Y")); 190 191 private: 192 alias ParentColor = XYZ!ComponentType; 193 } 194 195 /// 196 unittest 197 { 198 // CIE xyY 1931 color with double components 199 alias xyYd = xyY!double; 200 201 xyYd c = xyYd(0.4, 0.5, 1); 202 203 // test xyY operators and functions 204 static assert(xyYd(0, 0.5, 0) + xyYd(0.5, 0.5, 1) == xyYd(0.5, 1, 1)); 205 static assert(xyYd(0.5, 0.5, 1) * 100.0 == xyYd(50, 50, 100)); 206 }