1 module ggplotd.color.hsx;
2 
3 import ggplotd.color;
4 
5 import std.traits : isFloatingPoint, isIntegral, isSigned, isUnsigned, isSomeChar, Unqual;
6 import std.typetuple : TypeTuple;
7 import std.typecons : tuple;
8 
9 @safe pure nothrow @nogc:
10 
11 /**
12 Detect whether $(D T) is a member of the HSx color family.
13 */
14 enum isHSx(T) = isInstanceOf!(HSx, T);
15 
16 ///
17 unittest
18 {
19     static assert(isHSx!(HSx!(HSxType.HSV, ushort)) == true);
20     static assert(isHSx!string == false);
21 }
22 
23 /**
24 Alias for a HSV (HSB) color.
25 */
26 alias HSV(CT = float, RGBColorSpace cs = RGBColorSpace.sRGB) = HSx!(HSxType.HSV, CT, cs);
27 
28 /**
29 Alias for a HSL color.
30 */
31 alias HSL(CT = float, RGBColorSpace cs = RGBColorSpace.sRGB) = HSx!(HSxType.HSL, CT, cs);
32 
33 /**
34 Alias for a HSI color.
35 */
36 alias HSI(CT = float, RGBColorSpace cs = RGBColorSpace.sRGB) = HSx!(HSxType.HSI, CT, cs);
37 
38 /**
39 Alias for a HCY' color.
40 */
41 alias HCY(CT = float, RGBColorSpace cs = RGBColorSpace.sRGB) = HSx!(HSxType.HCY, CT, cs);
42 
43 /**
44 Define a HSx family colour type.
45 */
46 enum HSxType
47 {
48     /** Hue-saturation-value (aka HSB: Hue-saturation-brightness) */
49     HSV,
50     /** Hue-saturation-lightness */
51     HSL,
52     /** Hue-saturation-intensity */
53     HSI,
54     /** Hue-chroma-luma */
55     HCY
56 }
57 
58 /**
59 HSx color space is used to describe a suite of angular color spaces including HSL, HSV, HSI, HSY.
60 */
61 struct HSx(HSxType type_, CT = float, RGBColorSpace colorSpace_ = RGBColorSpace.sRGB) if(isFloatingPoint!CT || isUnsigned!CT)
62 {
63 @safe pure nothrow @nogc:
64 
65     /** Type of the color components. */
66     alias ComponentType = CT;
67     /** The color space specified. */
68     enum colorSpace = colorSpace_;
69     /** The color type from the HSx family. */
70     enum type = type_;
71 
72     // mixin the color channels according to the type
73     mixin("CT " ~ Components!type[0] ~ " = 0;");
74     mixin("CT " ~ Components!type[1] ~ " = 0;");
75     mixin("CT " ~ Components!type[2] ~ " = 0;");
76 
77     // casts
78     Color opCast(Color)() const if(isColor!Color)
79     {
80         return convertColor!Color(this);
81     }
82 
83     // operators
84     mixin ColorOperators!(Components!type);
85 
86 private:
87     template Components(HSxType type)
88     {
89         static if(type == HSxType.HSV)
90             alias Components = TypeTuple!("h","s","v");
91         else static if(type == HSxType.HSL)
92             alias Components = TypeTuple!("h","s","l");
93         else static if(type == HSxType.HSI)
94             alias Components = TypeTuple!("h","s","i");
95         else static if(type == HSxType.HCY)
96             alias Components = TypeTuple!("h","c","y");
97     }
98     alias ParentColourSpace = RGB!("rgb", CT, false, colorSpace_);
99 }
100 
101 ///
102 unittest
103 {
104     // HSL color with float components
105     alias HSLf = HSx!(HSxType.HSL, float);
106 
107     HSLf c = HSLf(3.1415, 1, 0.5);
108 
109     // test HSL operators and functions
110     static assert(HSLf(3.1415, 0.2, 0.5) + HSLf(0, 0.5, 0.5) == HSLf(3.1415, 0.7, 1));
111     static assert(HSLf(2, 0.5, 1) * 100.0 == HSLf(200, 50, 100));
112 }
113 
114 ///
115 unittest
116 {
117     // HSV color with float components
118     alias HSVf = HSx!(HSxType.HSV, float);
119 
120     HSVf c = HSVf(3.1415, 1, 0.5);
121 
122     // test HSV operators and functions
123     static assert(HSVf(3.1415, 0.2, 0.5) + HSVf(0, 0.5, 0.5) == HSVf(3.1415, 0.7, 1));
124     static assert(HSVf(2, 0.5, 1) * 100.0 == HSVf(200, 50, 100));
125 }
126 
127 ///
128 unittest
129 {
130     // HSI color with float components
131     alias HSIf = HSx!(HSxType.HSI, float);
132 
133     HSIf c = HSIf(3.1415, 1, 0.5);
134 
135     // test HSI operators and functions
136     static assert(HSIf(3.1415, 0.2, 0.5) + HSIf(0, 0.5, 0.5) == HSIf(3.1415, 0.7, 1));
137     static assert(HSIf(2, 0.5, 1) * 100.0 == HSIf(200, 50, 100));
138 }
139 
140 ///
141 unittest
142 {
143     // HCY color with float components
144     alias HCYf = HSx!(HSxType.HCY, float);
145 
146     HCYf c = HCYf(3.1415, 1, 0.5);
147 
148     // test HCY operators and functions
149     static assert(HCYf(3.1415, 0.2, 0.5) + HCYf(0, 0.5, 0.5) == HCYf(3.1415, 0.7, 1));
150     static assert(HCYf(2, 0.5, 1) * 100.0 == HCYf(200, 50, 100));
151 }