1 /**
2     Color
3 
4     Copyright: (c) Enalye 2017
5     License: Zlib
6     Authors: Enalye
7 */
8 
9 module atelier.core.color;
10 
11 import std.math;
12 import std.typecons;
13 import std.random;
14 public import std.algorithm.comparison : clamp;
15 
16 import bindbc.sdl;
17 
18 import atelier.core.stream;
19 import atelier.core.vec3;
20 
21 /// An RGB color structure.
22 struct Color {
23     /// Basic color.
24     static const Color red = Color(1f, 0f, 0f);
25     /// Ditto
26     static const Color lime = Color(0f, 1f, 0f);
27     /// Ditto
28     static const Color blue = Color(0f, 0f, 1f);
29     /// Ditto
30     static const Color white = Color(1f, 1f, 1f);
31     /// Ditto
32     static const Color black = Color(0f, 0f, 0f);
33     /// Ditto
34     static const Color yellow = Color(1f, 1f, 0f);
35     /// Ditto
36     static const Color cyan = Color(0f, 1f, 1f);
37     /// Ditto
38     static const Color magenta = Color(1f, 0f, 1f);
39     /// Ditto
40     static const Color silver = Color(.75f, .75f, .75f);
41     /// Ditto
42     static const Color gray = Color(.5f, .5f, .5f);
43     /// Ditto
44     static const Color grey = Color(.5f, .5f, .5f);
45     /// Ditto
46     static const Color maroon = Color(.5f, 0f, 0f);
47     /// Ditto
48     static const Color olive = Color(.5f, .5f, 0f);
49     /// Ditto
50     static const Color green = Color(0f, .5f, 0f);
51     /// Ditto
52     static const Color purple = Color(.5f, 0f, .5f);
53     /// Ditto
54     static const Color teal = Color(.5f, 0f, .5f);
55     /// Ditto
56     static const Color navy = Color(0f, 0f, .5f);
57     /// Ditto
58     static const Color pink = Color(1f, .75f, .8f);
59     /// Ditto
60     static const Color orange = Color(1f, .65f, 0f);
61 
62     static @property {
63         /// Random RGB color.
64         Color random() {
65             return Color(uniform01(), uniform01(), uniform01());
66         }
67     }
68 
69     static {
70         /// Take a 0xFFFFFF color format.
71         Color fromHex(int rgbValue) {
72             return Color((rgbValue >> 16) & 0xFF, (rgbValue >> 8) & 0xFF, rgbValue & 0xFF);
73         }
74     }
75 
76     @property {
77         /// Red component, between 0 and 1.
78         float r() const {
79             return _r;
80         }
81         /// Ditto
82         float r(float red) {
83             return _r = clamp(red, 0f, 1f);
84         }
85 
86         /// Green component, between 0 and 1.
87         float g() const {
88             return _g;
89         }
90         /// Ditto
91         float g(float green) {
92             return _g = clamp(green, 0f, 1f);
93         }
94 
95         /// Blue component, between 0 and 1.
96         float b() const {
97             return _b;
98         }
99         /// Ditto
100         float b(float blue) {
101             return _b = clamp(blue, 0f, 1f);
102         }
103 
104         /// Convert to Vec3
105         Vec3f rgb() const {
106             return Vec3f(_r, _g, _b);
107         }
108         /// Convert from Vec3
109         Vec3f rgb(Vec3f v) {
110             set(v.x, v.y, v.z);
111             return v;
112         }
113     }
114 
115     private {
116         float _r = 0f, _g = 0f, _b = 0f;
117     }
118 
119     /// Sets the RGB values, between 0 and 1.
120     this(float red, float green, float blue) {
121         _r = clamp(red, 0f, 1f);
122         _g = clamp(green, 0f, 1f);
123         _b = clamp(blue, 0f, 1f);
124     }
125 
126     /// Sets the RGB values, between 0 and 1.
127     this(Vec3f v) {
128         _r = clamp(v.x, 0f, 1f);
129         _g = clamp(v.y, 0f, 1f);
130         _b = clamp(v.z, 0f, 1f);
131     }
132 
133     /// Sets the RGB values, between 0 and 255.
134     this(int red, int green, int blue) {
135         _r = clamp(red, 0, 255) / 255f;
136         _g = clamp(green, 0, 255) / 255f;
137         _b = clamp(blue, 0, 255) / 255f;
138     }
139 
140     /// Sets the RGB values, between 0 and 1.
141     void set(float red, float green, float blue) {
142         _r = clamp(red, 0f, 1f);
143         _g = clamp(green, 0f, 1f);
144         _b = clamp(blue, 0f, 1f);
145     }
146 
147     /// Sets the RGB values, between 0 and 255.
148     void set(int red, int green, int blue) {
149         _r = clamp(red, 0, 255) / 255f;
150         _g = clamp(green, 0, 255) / 255f;
151         _b = clamp(blue, 0, 255) / 255f;
152     }
153 
154     /// Binary operations
155     Color opBinary(string op)(const Color c) const {
156         return mixin("Color(_r " ~ op ~ " c._r, _g " ~ op ~ " c._g, _b " ~ op ~ " c._b)");
157     }
158 
159     /// Binary operations
160     Color opBinary(string op)(float s) const {
161         return mixin("Color(_r " ~ op ~ " s, _g " ~ op ~ " s, _b " ~ op ~ " s)");
162     }
163 
164     /// Binary operations
165     Color opBinaryRight(string op)(float s) const {
166         return mixin("Color(s " ~ op ~ " _r, s " ~ op ~ " _g, s " ~ op ~ " _b)");
167     }
168 
169     /// Binary operations
170     Color opOpAssign(string op)(const Color c) {
171         mixin("
172 			_r = clamp(_r " ~ op ~ "c._r, 0f, 1f);
173 			_g = clamp(_g " ~ op
174                 ~ "c._g, 0f, 1f);
175 			_b = clamp(_b " ~ op ~ "c._b, 0f, 1f);
176 			");
177         return this;
178     }
179 
180     /// Assignment
181     Color opOpAssign(string op)(float s) {
182         mixin("s = clamp(s, 0f, 1f);_r = _r" ~ op ~ "s;_g = _g" ~ op ~ "s;_b = _b" ~ op ~ "s;");
183         return this;
184     }
185 
186     /// Read from an InStream.
187     void load(InStream stream) {
188         _r = stream.read!float;
189         _g = stream.read!float;
190         _b = stream.read!float;
191     }
192 
193     /// Write to an OutStream.
194     void save(OutStream stream) {
195         stream.write!float(_r);
196         stream.write!float(_g);
197         stream.write!float(_b);
198     }
199 
200     /// Get the SDL struct format for colors.
201     SDL_Color toSDL() const {
202         SDL_Color sdlColor = {
203             cast(ubyte)(_r * 255f), cast(ubyte)(_g * 255f), cast(ubyte)(_b * 255f)
204         };
205         return sdlColor;
206     }
207 }
208 
209 /// Mix 50% of one color with 50% of another.
210 Color mix(Color c1, Color c2) {
211     return Color((c1._r + c2._r) / 2f, (c1._g + c2._g) / 2f, (c1._b + c2._b) / 2f);
212 }