1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.stdlib.color;
7 
8 import std.conv : to;
9 import std.algorithm.comparison : clamp;
10 import grimoire.assembly, grimoire.compiler, grimoire.runtime;
11 import grimoire.stdlib.util;
12 
13 package void grLoadStdLibColor(GrLibrary library) {
14     auto colorType = library.addClass("Color", ["r", "g", "b"], [
15             grFloat, grFloat, grFloat
16             ]);
17 
18     library.addPrimitive(&_makeColor, "Color", [], [colorType]);
19     library.addPrimitive(&_makeColor3, "Color", [grFloat, grFloat, grFloat], [
20             colorType
21             ]);
22 
23     library.addPrimitive(&_makeColor3i, "Color", [grInt, grInt, grInt], [
24             colorType
25             ]);
26 
27     static foreach (op; ["+", "-", "*", "/", "%"]) {
28         library.addOperator(&_opBinaryColor!op, op, [colorType, colorType], colorType);
29         library.addOperator(&_opBinaryScalarColor!op, op, [colorType, grFloat], colorType);
30         library.addOperator(&_opBinaryScalarRightColor!op, op, [
31                 grFloat, colorType
32                 ], colorType);
33     }
34 
35     library.addPrimitive(&_mixColor, "mix", [colorType, colorType], [colorType]);
36     library.addPrimitive(&_lerpColor, "lerp", [colorType, colorType, grFloat], [
37             colorType
38             ]);
39 
40     library.addCast(&_castArrayToColor, grIntArray, colorType);
41     library.addCast(&_castColorToString, colorType, grString);
42 
43     library.addPrimitive(&_unpack, "unpack", [colorType], [
44             grFloat, grFloat, grFloat
45             ]);
46 
47     library.addPrimitive(&_print, "print", [colorType]);
48     library.addPrimitive(&_printl, "printl", [colorType]);
49 }
50 
51 private void _makeColor(GrCall call) {
52     GrObject self = call.createObject("Color");
53     if (!self) {
54         call.raise("UnknownClassError");
55         return;
56     }
57     self.setFloat("r", 0f);
58     self.setFloat("g", 0f);
59     self.setFloat("b", 0f);
60     call.setObject(self);
61 }
62 
63 private void _makeColor3(GrCall call) {
64     GrObject self = call.createObject("Color");
65     if (!self) {
66         call.raise("UnknownClassError");
67         return;
68     }
69     self.setFloat("r", call.getFloat(0));
70     self.setFloat("g", call.getFloat(1));
71     self.setFloat("b", call.getFloat(2));
72     call.setObject(self);
73 }
74 
75 private void _makeColor3i(GrCall call) {
76     GrObject self = call.createObject("Color");
77     if (!self) {
78         call.raise("UnknownClassError");
79         return;
80     }
81     self.setFloat("r", clamp(call.getInt(0) / 255f, 0f, 1f));
82     self.setFloat("g", clamp(call.getInt(1) / 255f, 0f, 1f));
83     self.setFloat("b", clamp(call.getInt(2) / 255f, 0f, 1f));
84     call.setObject(self);
85 }
86 
87 private void _opBinaryColor(string op)(GrCall call) {
88     GrObject self = call.createObject("Color");
89     if (!self) {
90         call.raise("UnknownClassError");
91         return;
92     }
93     GrObject c1 = call.getObject(0);
94     GrObject c2 = call.getObject(1);
95     if (!c1 || !c2) {
96         call.raise("NullError");
97         return;
98     }
99     mixin("self.setFloat(\"r\", c1.getFloat(\"r\")" ~ op ~ "c2.getFloat(\"r\"));");
100     mixin("self.setFloat(\"g\", c1.getFloat(\"g\")" ~ op ~ "c2.getFloat(\"g\"));");
101     mixin("self.setFloat(\"b\", c1.getFloat(\"b\")" ~ op ~ "c2.getFloat(\"b\"));");
102     call.setObject(self);
103 }
104 
105 private void _opBinaryScalarColor(string op)(GrCall call) {
106     GrObject self = call.createObject("Color");
107     if (!self) {
108         call.raise("UnknownClassError");
109         return;
110     }
111     GrObject c = call.getObject(0);
112     const GrFloat s = call.getFloat(1);
113     if (!c) {
114         call.raise("NullError");
115         return;
116     }
117     mixin("self.setFloat(\"r\", c.getFloat(\"r\")" ~ op ~ "s);");
118     mixin("self.setFloat(\"g\", c.getFloat(\"g\")" ~ op ~ "s);");
119     mixin("self.setFloat(\"b\", c.getFloat(\"b\")" ~ op ~ "s);");
120     call.setObject(self);
121 }
122 
123 private void _opBinaryScalarRightColor(string op)(GrCall call) {
124     GrObject self = call.createObject("Color");
125     if (!self) {
126         call.raise("UnknownClassError");
127         return;
128     }
129     GrObject c = call.getObject(0);
130     const GrFloat s = call.getFloat(1);
131     if (!c) {
132         call.raise("NullError");
133         return;
134     }
135     mixin("self.setFloat(\"r\", s" ~ op ~ "c.getFloat(\"r\"));");
136     mixin("self.setFloat(\"g\", s" ~ op ~ "c.getFloat(\"g\"));");
137     mixin("self.setFloat(\"b\", s" ~ op ~ "c.getFloat(\"b\"));");
138     call.setObject(self);
139 }
140 
141 private void _mixColor(GrCall call) {
142     GrObject self = call.createObject("Color");
143     if (!self) {
144         call.raise("UnknownClassError");
145         return;
146     }
147     GrObject c1 = call.getObject(0);
148     GrObject c2 = call.getObject(1);
149     if (!c1 || !c2) {
150         call.raise("NullError");
151         return;
152     }
153     self.setFloat("r", (c1.getFloat("r") + c2.getFloat("r")) / 2f);
154     self.setFloat("g", (c1.getFloat("g") + c2.getFloat("g")) / 2f);
155     self.setFloat("b", (c1.getFloat("b") + c2.getFloat("b")) / 2f);
156     call.setObject(self);
157 }
158 
159 private void _lerpColor(GrCall call) {
160     GrObject self = call.createObject("Color");
161     if (!self) {
162         call.raise("UnknownClassError");
163         return;
164     }
165     GrObject c1 = call.getObject(0);
166     GrObject c2 = call.getObject(1);
167     const GrFloat t = call.getFloat(2);
168     if (!c1 || !c2) {
169         call.raise("NullError");
170         return;
171     }
172     self.setFloat("r", (t * c2.getFloat("r")) + ((1f - t) * c1.getFloat("r")));
173     self.setFloat("g", (t * c2.getFloat("g")) + ((1f - t) * c1.getFloat("g")));
174     self.setFloat("b", (t * c2.getFloat("b")) + ((1f - t) * c1.getFloat("b")));
175     call.setObject(self);
176 }
177 
178 private void _castArrayToColor(GrCall call) {
179     GrIntArray array = call.getIntArray(0);
180     if (array.data.length == 3) {
181         GrObject self = call.createObject("Color");
182         if (!self) {
183             call.raise("UnknownClassError");
184             return;
185         }
186         self.setFloat("r", array.data[0]);
187         self.setFloat("g", array.data[1]);
188         self.setFloat("b", array.data[2]);
189         call.setObject(self);
190         return;
191     }
192     call.raise("Cannot convert array to Color, invalid size");
193 }
194 
195 private void _castColorToString(GrCall call) {
196     GrObject self = call.getObject(0);
197     if (!self) {
198         call.raise("NullError");
199         return;
200     }
201     call.setString("Color(" ~ to!GrString(self.getFloat("r")) ~ ", " ~ to!GrString(
202             self.getFloat("g")) ~ ", " ~ to!GrString(self.getFloat("b")) ~ ")");
203 }
204 
205 private void _unpack(GrCall call) {
206     GrObject self = call.getObject(0);
207     if (!self) {
208         call.raise("NullError");
209         return;
210     }
211     call.setFloat(self.getFloat("r"));
212     call.setFloat(self.getFloat("g"));
213     call.setFloat(self.getFloat("b"));
214 }
215 
216 private void _print(GrCall call) {
217     GrObject self = call.getObject(0);
218     if (!self) {
219         call.raise("NullError");
220         return;
221     }
222     _stdOut("Color(" ~ to!GrString(self.getFloat("r")) ~ ", " ~ to!GrString(
223             self.getFloat("g")) ~ ", " ~ to!GrString(self.getFloat("b")) ~ ")");
224 }
225 
226 private void _printl(GrCall call) {
227     GrObject self = call.getObject(0);
228     if (!self) {
229         call.raise("NullError");
230         return;
231     }
232     _stdOut("Color(" ~ to!GrString(self.getFloat("r")) ~ ", " ~ to!GrString(
233             self.getFloat("g")) ~ ", " ~ to!GrString(self.getFloat("b")) ~ ")\n");
234 }