1 module armos.types.color;
2 import armos.math;
3 
4 /++
5 +/
6 struct BaseColor(T, T Limit){
7     alias C = BaseColor!(T, Limit);
8 
9     public{
10         /// Upper limit of each channel.
11         enum T limit = Limit;
12 
13         /// Red Channel.
14         T r = limit;
15 
16         /// Green Channel.
17         T g = limit;
18 
19         /// Blue Channel.
20         T b = limit; 
21 
22         /// Alpha Channel.
23         T a = limit;
24 
25         /++
26             16進数のカラーコードで色を指定します.
27         +/
28         this(in int hexColor, in float alpha = limit){
29             char r255 = (hexColor >> 16) & 0xff;
30             char g255 = (hexColor >> 8) & 0xff;
31             char b255 = (hexColor >> 0) & 0xff;
32             import std.conv:to;
33             r = (r255.to!float*limit/255.0f).to!T;
34             g = (g255.to!float*limit/255.0f).to!T;
35             b = (b255.to!float*limit/255.0f).to!T;
36             a = cast(T)alpha;
37         }
38 
39         /++
40             RGBAで色を指定します.透明度は省略可能です.
41         +/
42         this(in float red, in float green, in float blue, in float alpha = limit){
43             assert(red   <= limit);
44             assert(green <= limit);
45             assert(blue  <= limit);
46             assert(alpha <= limit);
47             r = armos.math.clamp(cast(T)red, T(0), limit);
48             g = armos.math.clamp(cast(T)green, T(0), limit);
49             b = armos.math.clamp(cast(T)blue, T(0), limit);
50             a = armos.math.clamp(cast(T)alpha, T(0), limit);
51         }
52         
53         this(V)(V v)if(isVector!(V) && V.dimention == 4){
54             import std.conv;
55             r = (v.x.to!float * limit.to!float).to!T;
56             g = (v.y.to!float * limit.to!float).to!T;
57             b = (v.z.to!float * limit.to!float).to!T;
58             a = (v.w.to!float * limit.to!float).to!T;
59         }
60 
61         /++
62             色の加算を行います.
63         +/
64         C opAdd(in C color)const{
65             import std.conv;
66 
67             C result;
68             float alphaPercentageL = this.a.to!float/limit.to!float;
69             float alphaPercentageR = color.a.to!float/color.limit.to!float;
70             import std.math;
71 
72             result.r = fmax(fmin( this.r.to!float * alphaPercentageL + color.r.to!float * alphaPercentageR, this.limit), float(0)).to!T;
73             result.g = fmax(fmin( this.g.to!float * alphaPercentageL + color.g.to!float * alphaPercentageR, this.limit), float(0)).to!T;
74             result.b = fmax(fmin( this.b.to!float * alphaPercentageL + color.b.to!float * alphaPercentageR, this.limit), float(0)).to!T;
75             return result;
76         }
77         unittest{
78             alias C = BaseColor!(char, 255);
79             immutable colorL = C(128, 64, 0, 255);
80             immutable colorR = C(128, 0, 64, 255);
81             assert(colorL + colorR == C(255, 64, 64, 255));
82         }
83 
84         /++
85             色の減算を行います.
86         +/
87         C opSub(in C color)const{
88             import std.conv;
89             C result;
90             float alphaPercentageL = this.a.to!float/limit.to!float;
91             float alphaPercentageR = color.a.to!float/color.limit.to!float;
92             import std.math;
93 
94             result.r = fmax(fmin( this.r.to!float * alphaPercentageL - color.r.to!float * alphaPercentageR, this.limit), float(0)).to!T;
95             result.g = fmax(fmin( this.g.to!float * alphaPercentageL - color.g.to!float * alphaPercentageR, this.limit), float(0)).to!T;
96             result.b = fmax(fmin( this.b.to!float * alphaPercentageL - color.b.to!float * alphaPercentageR, this.limit), float(0)).to!T;
97             return result;
98         }
99         unittest{
100             alias C = BaseColor!(char, 255);
101             immutable colorL = C(128, 64, 64, 255);
102             immutable colorR = C(128, 0, 32, 255);
103             assert(colorL - colorR == C(0, 64, 32, 255));
104         }
105 
106         /++
107             色をHSBで指定します.
108             Params: 
109             hue = [0, 255]
110             saturation = [0, 255]
111             value = [0, 255]
112         +/
113         C hsb(Hue, Saturation, Value)(in Hue hue, in Saturation saturation, in Value value)if(__traits(isArithmetic, Hue, Saturation, Value)){
114             import std.math;
115             import std.conv;
116             immutable float castedLimit = limit.to!float;
117             immutable float h = hue.to!float*360.0f/castedLimit;
118             immutable int hi = ( floor(h / 60.0f) % 6 ).to!int;
119             immutable f  = (h / 60.0f) - floor(h / 60.0f);
120             immutable p  = round(value * (1.0f - (saturation / castedLimit)));
121             immutable q  = round(value * (1.0f - (saturation / castedLimit) * f));
122             immutable t  = round(value * (1.0f - (saturation / castedLimit) * (1.0f - f)));
123             float red, green, blue;
124             switch (hi) {
125                 case 0:
126                     red = value, green = t,     blue = p;
127                     break;
128                 case 1:
129                     red = q,     green = value, blue = p;
130                     break;
131                 case 2:
132                     red = p,     green = value, blue = t;
133                     break;
134                 case 3:
135                     red = p,     green = q,     blue = value;
136                     break;
137                 case 4:
138                     red = t,     green = p,     blue = value;
139                     break;
140                 case 5:
141                     red = value, green = p,     blue = q;
142                     break;
143                 default:
144                     break;
145             }
146 
147             r = red.to!T;
148             g = green.to!T;
149             b = blue.to!T;
150             return this;
151         }
152         unittest{
153             import std.conv;
154             auto cColor = BaseColor!(char, 255)(128, 0, 0, 255);
155             cColor.hsb(0, 255, 255);
156             assert(cColor.r.to!int == 255);
157             assert(cColor.g.to!int == 0);
158             assert(cColor.b.to!int == 0);
159 
160             cColor.hsb(255, 255, 255);
161             assert(cColor.r.to!int == 255);
162             assert(cColor.g.to!int == 0);
163             assert(cColor.b.to!int == 0);
164             // import std.stdio;
165             // cColor.r.to!int.writeln;
166             // cColor.g.to!int.writeln;
167             // cColor.b.to!int.writeln;
168 
169             cColor.hsb(255.0/3.0, 255.0, 255.0);
170             assert(cColor.r.to!int == 0);
171             assert(cColor.g.to!int == 255);
172             assert(cColor.b.to!int == 0);
173         }
174 
175         F opCast(F)()const if(!isVector!F && is(F == BaseColor!(typeof( F.r ), F.limit))){
176             import std.conv:to;
177             F castedColor= F(0, 0, 0, 0);
178             float c = cast(float)castedColor.limit / cast(float)limit;
179             alias CT = typeof(F.r);
180             castedColor.r = cast(CT)( cast(float)r*c );
181             castedColor.g = cast(CT)( cast(float)g*c );
182             castedColor.b = cast(CT)( cast(float)b*c );
183             castedColor.a = cast(CT)( cast(float)a*c );
184             return castedColor;
185         }
186         unittest{
187             import std.stdio;
188             import std.math;
189             auto cColor = BaseColor!(char, 255)(128, 0, 0, 255);
190             assert(approxEqual( ( cast(BaseColor!(float, 1.0f))cColor ).r, 128.0/255.0 ));
191 
192             auto fColor = BaseColor!(float, 1.0f)(0.5, 0.0, 0.0, 1.0);
193             assert(approxEqual( ( cast(BaseColor!(char, 255))cColor ).r, 128));
194         }
195 
196         
197         CastType opCast(CastType)()const if(isVector!CastType && CastType.dimention == 4 && is(CastType.elementType == T)){
198             return CastType!(T, 4)(r, g, b, a);
199         }
200     }//public
201 
202     private{
203     }//private
204 }//struct BaseColor
205 
206 /++
207 色を表すstructです.
208 最小値は0,最大値は1です.
209 +/
210 alias BaseColor!(float, 1.0f) Color;
211 unittest{
212     assert(__traits(compiles, (){
213                 auto color = Color();
214                 }));
215 }
216 
217 /++
218 色を表すstructです.浮動小数点型で値を持ちます.
219 
220 最小値は0.0,最大値は1.0です.
221 +/
222 deprecated alias BaseColor!(float, 1.0f) FloatColor;
223 unittest{
224     assert(__traits(compiles, (){
225                 auto color = FloatColor();
226                 }));
227 }