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 }