1 module sily.color;
2 
3 import std.algorithm : canFind;
4 import std.algorithm.comparison;
5 import std.algorithm.comparison: compclamp = clamp;
6 import std.array : replace;
7 import std.conv;
8 import std.math;
9 import std.regex;
10 import std.stdio;
11 import std.string;
12 import std.uni : toLower;
13 import std.traits: isNumeric;
14 
15 import sily.vector;
16 import sily.meta;
17 import sily.array;
18 
19 alias col = Color;
20 alias Color8 = (R, G, B) => Color(R / 255.0f, G / 255.0f, B / 255.0f);
21 
22 struct Color {
23     public float[4] data = fill!float(1, 4);
24 
25     alias data this;
26     alias arrayof = data;
27 
28     this(in float val) {
29         foreach (i; 0 .. 3) { data[i] = val; }
30         data[3] = 1.0f;
31     }
32 
33     this(in float val, in float a) {
34         foreach (i; 0 .. 3) { data[i] = val; }
35         data[3] = a;
36     }
37 
38     this(in float[3] vals...) {
39         data = vals ~ [1.0f];
40     }
41 
42     this(in float[4] vals...) {
43         data = vals;
44     }
45 
46     ubyte r8() { return cast(ubyte) (data[0] * 255u); }
47     ubyte g8() { return cast(ubyte) (data[1] * 255u); }
48     ubyte b8() { return cast(ubyte) (data[2] * 255u); }
49     ubyte a8() { return cast(ubyte) (data[3] * 255u); }
50 
51     /* -------------------------------------------------------------------------- */
52     /*                         UNARY OPERATIONS OVERRIDES                         */
53     /* -------------------------------------------------------------------------- */
54     
55     // opBinary x [+, -, *, /, %] y
56     auto opBinary(string op, R)(in Color!(R, N) b) const if ( isNumeric!R ) {
57         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
58         VecType ret = VecType();
59         foreach (i; 0 .. 4) { mixin( "ret.data[i] = data[i] " ~ op ~ " b.data[i];" ); }
60         return ret;
61     }
62 
63     // opBinaryRight y [+, -, *, /, %] x
64     auto opBinaryRight(string op, R)(in Color!(R, N) b) const if ( isNumeric!R ) {
65         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
66         VecType ret = VecType();
67         foreach (i; 0 .. 4) { mixin( "ret[i] = b.data[i] " ~ op ~ " data[i];" ); }
68         return ret;
69     }
70 
71     auto opBinary(string op, R)(in R b) const if ( isNumeric!R ) {
72         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
73         VecType ret = VecType();
74         foreach (i; 0 .. 4) { mixin( "ret.data[i] = data[i] " ~ op ~ " b;" ); }
75         return ret;
76     }
77 
78     auto opBinaryRight(string op, R)(in R b) const if ( isNumeric!R ) {
79         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
80         VecType ret = VecType();
81         foreach (i; 0 .. 4) { mixin( "ret[i] = b " ~ op ~ " data[i];" ); }
82         return ret;
83     }
84 
85     // opEquals x == y
86     bool opEquals(R)(in Color!(R, 4) b) const if ( isNumeric!R ) {
87         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
88         bool eq = true;
89         foreach (i; 0 .. 4) { eq = eq && data[i] == b.data[i]; }
90         return eq;
91     }
92 
93     // opCmp x [< > ==] y
94     int opCmp(R)(in Color!(R, N) b) const if ( isNumeric!R ) {
95         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
96         float al = length;
97         float bl = b.length;
98         if (al == bl) return 0;
99         if (al < bl) return -1;
100         return 1;
101     }
102 
103     // opUnary [-, +, --, ++] x
104     auto opUnary(string op)() if(op == "-"){
105         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
106         VecType ret = VecType();
107         if (op == "-")
108             foreach (i; 0 .. 4) { ret.data[i] = -data[i]; }
109         return ret;
110     }
111     
112     // opOpAssign x [+, -, *, /, %]= y
113     auto opOpAssign(string op, R)( in Color!(R, N) b ) if ( isNumeric!R ) { 
114         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
115         foreach (i; 0 .. 4) { mixin( "data[i] = data[i] " ~ op ~ " b.data[i];" ); }
116         return this;
117     }
118     
119     auto opOpAssign(string op, R)( in R b ) if ( isNumeric!R ) { 
120         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
121         foreach (i; 0 .. 4) { mixin( "data[i] = data[i] " ~ op ~ " b;" ); }
122         return this;
123     }
124 
125     size_t toHash() const @nogc @safe pure nothrow {
126         float s = 0;
127         foreach (i; 0 .. 4) { s += data[i]; }
128         return cast(size_t) s;
129     }
130     
131     // incredible magic from terramatter.meta.meta
132     // idk how it works but it works awesome
133     // and im not going to touch it at all
134     enum AccessString = "r g b a";
135     mixin accessByString!(float, 4, "data", AccessString);
136     
137 
138     public Vector4f asVector4f() {
139         return Vector4f(data);
140     }
141 
142     public Vector3f asVector3f() {
143         return Vector3f(data[0..3]);
144     }
145 
146     public Color copyof() {
147         return Color(data);
148     }
149 
150     public string toString() const {
151         import std.conv : to;
152         string s;
153         s ~= "[";
154         foreach (i; 0 .. 4) {
155             s ~= format("%.2f", data[i]);
156             if (i != 4 - 1) s ~= ", ";
157         }
158         s ~= "]";
159         return s;
160     }
161 
162     /* -------------------------------------------------------------------------- */
163     /*                                 PROPERTIES                                 */
164     /* -------------------------------------------------------------------------- */
165 
166     /** 
167      * Inverts color
168      */
169     public void invert() {
170         data[0] = 1.0f - data[0];
171         data[1] = 1.0f - data[1];
172         data[2] = 1.0f - data[2];
173         data[3] = 1.0f - data[3];
174     }
175 
176     /** 
177      * Returns the luminance of the color in the [0.0, 1.0] range
178      */
179     public float luminance() {
180         return compclamp(0.2126f * data[0] + 0.7152f * data[1] + 0.0722f * data[2], 0.0f, 1.0f);
181     } 
182 
183     /** 
184      * Returns the linear interpolation with another color
185      * Params:
186      *   p_to = Color to interpolate with
187      *   p_weight = Interpolation factor in [0.0, 1.0] range
188      */
189 	public void lerp(const Color p_to, float p_weight) {
190 		data[0] += (p_weight * (p_to.data[0] - data[0]));
191 		data[1] += (p_weight * (p_to.data[1] - data[1]));
192 		data[2] += (p_weight * (p_to.data[2] - data[2]));
193 		data[3] += (p_weight * (p_to.data[3] - data[3]));
194 	} 
195 
196     /** 
197      * Darkens color by `p_amount`
198      * Params: 
199      *   p_amount = Amount to darken
200      */
201 	public void darken(float p_amount) {
202 		data[0] = data[0] * (1.0f - p_amount);
203 		data[1] = data[1] * (1.0f - p_amount);
204 		data[2] = data[2] * (1.0f - p_amount);
205 	}
206 
207     /** 
208      * Lightens color by `p_amount`
209      * Params: 
210      *   p_amount = Amount to lighten
211      */
212 	public void lighten(float p_amount) {
213 		data[0] = data[0] + (1.0f - data[0]) * p_amount;
214 		data[1] = data[1] + (1.0f - data[1]) * p_amount;
215 		data[2] = data[2] + (1.0f - data[2]) * p_amount;
216 	}
217 
218     /** 
219      * Clamps color values between `min` and `max`
220      * Params: 
221      *   min = Minimal allowed value
222      *   max = Maximal allowed value
223      */
224     void clamp(float min = 0.0f, float max = 1.0f) {
225         data[0] = compclamp(data[0], min, max);
226         data[1] = compclamp(data[1], min, max);
227         data[2] = compclamp(data[2], min, max);
228         data[3] = compclamp(data[3], min, max);
229     }
230 
231     /* -------------------------------------------------------------------------- */
232     /*                                    HTML                                    */
233     /* -------------------------------------------------------------------------- */
234 
235     private uint tocolbit(uint c_bits, int[4] c_order ...) {
236         uint c_size = 2.pow(c_bits) - 1;
237         uint c_shift = c_bits;
238         uint c = (data[c_order[0]] * c_size).round.to!uint;
239         c <<= c_shift;
240         c |= (data[c_order[1]] * c_size).round.to!uint;
241         c <<= c_shift;
242         c |= (data[c_order[2]] * c_size).round.to!uint;
243         c <<= c_shift;
244         c |= (data[c_order[3]] * c_size).round.to!uint;
245         return c;
246     }
247 
248     // public uint toargb32() { return tocolbit(8, 3, 0, 1, 2); }
249     // public uint toabgr32() { return tocolbit(8, 3, 2, 1, 0); }
250     // public uint torgba32() { return tocolbit(8, 0, 1, 2, 3); }
251     // public uint toargb64() { return tocolbit(16, 3, 0, 1, 2); }
252     // public uint toabgr64() { return tocolbit(16, 3, 2, 1, 0); }
253     // public uint torgba64() { return tocolbit(16, 0, 1, 2, 3); }
254 
255     private string _to_hex(float p_val) const {
256         int v = (p_val * 255).round.to!int;
257         v = v.clamp(0, 255);
258         string ret;
259 
260         for (int i = 0; i < 2; i++) {
261             char[2] c = [ 0, 0 ];
262             int lv = v & 0xF;
263             if (lv < 10) {
264                 c[0] = ('0' + lv).to!char;
265             } else {
266                 c[0] = ('a' + lv - 10).to!char;
267             }
268 
269             v >>= 4;
270             string cs = c.to!string;
271             ret = cs ~ ret;
272         }
273 
274         return ret;
275     }
276 
277     /** 
278      * Returns html representation of color in format `#RRGGBB`
279      *
280      * If `p_alpha` is true returns color in format `#RRGGBBAA`
281      * Params:
282      *   p_alpha = Include alpha?
283      * Returns: Html string
284      */
285     public string toHtml(bool p_alpha = false) const {
286         string txt;
287         txt ~= _to_hex(data[0]);
288         txt ~= _to_hex(data[1]);
289         txt ~= _to_hex(data[2]);
290         if (p_alpha) {
291             txt ~= _to_hex(data[3]);
292         }
293         return txt;
294     }
295 
296     /** 
297      * Returns hex representation of color in format `0xrrggbb`
298      * Returns: uint hex
299      */
300     public uint toHex() {
301         int r = rint(data[0] * 255);
302         int g = rint(data[1] * 255);
303         int b = rint(data[2] * 255);
304         return ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff);
305     }
306 
307     /* -------------------------------------------------------------------------- */
308     /*                                   SETTERS                                  */
309     /* -------------------------------------------------------------------------- */
310 
311     private void setHex(uint p_hex, uint c_mask, uint c_bits, bool c_hasAlpha) {
312         float c_size = (2.pow(c_bits) - 1).to!float;
313         uint c_shift = c_bits;
314         float a = 0;
315         if (c_hasAlpha) {
316             a = (p_hex & c_mask) / c_size;
317             p_hex >>= c_shift;
318         }
319         float b = (p_hex & c_mask) / c_size;
320         p_hex >>= c_shift;
321         float g = (p_hex & c_mask) / c_size;
322         p_hex >>= c_shift;
323         float r = (p_hex & c_mask) / c_size;
324 
325         data = [r, g, b, a];
326     }
327 
328     /** 
329      * Sets color to hexadecimal value
330      * Params:
331      *   p_hex = uint hex value to set color to
332      *   p_hasAlpha = Does p_hex include alpha
333      */
334     public void setHex(uint p_hex, bool p_hasAlpha = false) { setHex(p_hex, 0xFF, 8, p_hasAlpha); }
335     // public void setHex64(uint p_hex, bool p_hasAlpha = false) { setHex(p_hex, 0xFFFF, 16, p_hasAlpha); }
336 
337     /** 
338      * Sets color from hsv
339      * Params:
340      *   p_h = hue
341      *   p_s = saturation
342      *   p_v = value
343      *   p_alpha = alpha
344      */
345     public void setHsv(float p_h, float p_s, float p_v, float p_alpha = 1.0f) {
346         int i;
347         float f, p, q, t;
348         data[3] = p_alpha;
349 
350         if (p_s == 0) {
351             // Achromatic (grey)
352             data[0] = data[1] = data[2] = p_v;
353             return;
354         }
355 
356         p_h *= 6.0f;
357         p_h = p_h.fmod(6);
358         i = p_h.floor().to!int;
359 
360         f = p_h - i;
361         p = p_v * (1 - p_s);
362         q = p_v * (1 - p_s * f);
363         t = p_v * (1 - p_s * (1 - f));
364 
365         switch (i) {
366             case 0: // Red is the dominant color
367                 data[0] = p_v;
368                 data[1] = t;
369                 data[2] = p;
370                 break;
371             case 1: // Green is the dominant color
372                 data[0] = q;
373                 data[1] = p_v;
374                 data[2] = p;
375                 break;
376             case 2:
377                 data[0] = p;
378                 data[1] = p_v;
379                 data[2] = t;
380                 break;
381             case 3: // Blue is the dominant color
382                 data[0] = p;
383                 data[1] = q;
384                 data[2] = p_v;
385                 break;
386             case 4:
387                 data[0] = t;
388                 data[1] = p;
389                 data[2] = p_v;
390                 break;
391             default: // (5) Red is the dominant color
392                 data[0] = p_v;
393                 data[1] = p;
394                 data[2] = q;
395                 break;
396         }
397     }
398 
399     /++ 
400      + Sets color from html string in format `#RRGGBB`
401      + Params:
402      +   html = Color string
403      +/
404     public void setHtml(string html) {
405         auto rg = regex(r"/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i");
406         auto m = matchAll(html, rg);
407         m.popFront();
408         float _r = to!int(m.front.hit, 16) / 255; m.popFront();
409         float _g = to!int(m.front.hit, 16) / 255; m.popFront();
410         float _b = to!int(m.front.hit, 16) / 255; m.popFront();
411         data = [to!float(_r), to!float(_g), to!float(_b), 1.0f];
412     }
413 
414     /* -------------------------------------------------------------------------- */
415     /*                               HSV PROPERTIES                               */
416     /* -------------------------------------------------------------------------- */
417 
418     /** 
419      * Returns `h` component of hsv
420      */
421     public float hue() const {
422         float cmin = data[0].min(data[1]);
423         cmin = cmin.min(data[2]);
424         float cmax = data[0].max(data[1]);
425         cmax = cmax.min(data[2]);
426 
427         float delta = cmax - cmin;
428         if (delta == 0) { return 0; }
429 
430         float h;
431         if (data[0] == cmax) {
432             h = (data[1] - data[2]) / delta;
433         } else
434         if (data[1] == cmax) {
435             h = 2 + (data[2] - data[0]) / delta;
436         } else {
437             h = 4 + (data[2] - data[0]) / delta;
438         }
439 
440         h /= 6.0f;
441         if (h < 0) {
442             h += 1.0f;
443         }
444         return h;
445     }
446 
447     /** 
448      * Returns `s` component of hsv
449      */
450     public float saturation() const {
451         float cmin = data[0].min(data[1]);
452         cmin = cmin.min(data[2]);
453         float cmax = data[0].max(data[1]);
454         cmax = cmax.min(data[2]);
455 
456         float delta = cmax - cmin;
457 
458         return (cmax != 0) ? (delta / cmax) : 0;
459     }
460 
461     /** 
462      * Returns `v` component of hsv
463      */
464     public float value() const {
465         float cmax = data[0].max(data[1]);
466         cmax = cmax.max(data[2]);
467         return cmax;
468     }
469 
470     /* -------------------------------------------------------------------------- */
471     /*                                BASH GETTERS                                */
472     /* -------------------------------------------------------------------------- */
473 
474     /** 
475      * Sets color from ANSI index
476      * Params:
477      *   ansi = Color index
478      */
479     void setAnsi8(int ansi) {
480         if (ansi > 100) ansi -= 92;
481         if (ansi > 90) ansi -= 82;
482         if (ansi > 40) ansi -= 40;
483         if (ansi > 30) ansi -= 30;
484         if (ansi >= lowHEX.length) ansi = lowHEX.length.to!int - 1;
485         if (ansi < 0) ansi = 0;
486         // write(ansi);
487         setHex(lowHEX[ansi]);
488     }
489 
490     /** 
491      * Sets color from ANSI256 index
492      * Params:
493      *   ansi = Color index
494      */
495     void setAnsi(int ansi) {
496         if (ansi < 0 || ansi > 255) {
497             data = [0, 0, 0, 0];
498             return;
499         }
500         if (ansi < 16) {
501             setHex(Color.lowHEX[ansi]);
502             return;
503         }
504 
505         if (ansi > 231) {
506             const int s = (ansi - 232) * 10 + 8;
507             data = [s / 255.0f, s / 255.0f, s / 255.0f, 1];
508             return;
509         }
510 
511         const int n = ansi - 16;
512         int _b = n % 6;
513         int _g = (n - _b) / 6 % 6;
514         int _r = (n - _b - _g * 6) / 36 % 6;
515         _b = _b ? _b * 40 + 55 : 0;
516         _r = _r ? _r * 40 + 55 : 0;
517         _g = _g ? _g * 40 + 55 : 0;
518 
519         data = [_r / 255.0, _g / 255.0, _b / 255.0, 1];
520     }
521 
522     /* -------------------------------------------------------------------------- */
523     /*                                BASH SETTERS                                */
524     /* -------------------------------------------------------------------------- */
525     
526     private alias rint = (a) => to!int(round(a));
527 
528 
529     private float colorDistance(Color e1, Color e2) {
530         int rmean = ( cast(int) e1.r + cast(int) e2.r ) / 2;
531         int r = cast(int) e1.r - cast(int) e2.r;
532         int g = cast(int) e1.g - cast(int) e2.g;
533         int b = cast(int) e1.b - cast(int) e2.b;
534         // return sqrt(
535         //     (((512 + rmean) * r * r) >> 8) + 
536         //     4.0f * g * g + 
537         //     (((767 - rmean) * b * b) >> 8)
538         //     );
539         return sqrt((2 + rmean / 255) * r * r + 4 * g * g + (2 + (255 - rmean) / 255) * b * b + 0.0f);
540     }
541     
542     /** 
543      * Returns closest ANSI8 color index 
544      * Params:
545      *   isBackground = Is color a background
546      * Returns: ANSI8 color
547      */
548     int toAnsi8(bool isBackground = false) {
549         /*
550         * 39 - default foreground, 49 - default backgound
551         * Main colors are from 30 - 39, light variant is 90 - 97.
552         * 97 is white, 30 is black. to get background - add 10 to color code
553         * goes like:
554         * black, red, green, yellow, blue, magenta, cyan, lgray
555         * then repeat with lighter variation
556         */
557 
558         int ri = rint(data[0] * 255);
559         int gi = rint(data[1] * 255);
560         int bi = rint(data[2] * 255);
561 
562         if (ri / 64 == gi / 64 && gi / 64 == bi / 64) {
563             // 0 128 192 255
564             if (ri <= 64) return 30 + (isBackground ? 10 : 0);
565             if (ri <= 160) return 90 + (isBackground ? 10 : 0);
566             if (ri <= 224) return 37 + (isBackground ? 10 : 0);
567             if (ri <= 255) return 97 + (isBackground ? 10 : 0);
568         }
569         
570         // int diff = 255 * 3;
571         float diff = 255 * 3;
572         int pos = 0;
573         // writeln(lowHEX[15] - hex);
574         for (int i = 0; i < 16; i ++) {
575             if (i == 0 || i == 7 || i == 8 || i == 15) continue;
576             int rh = lowRGB[i * 3];
577             int gh = lowRGB[i * 3 + 1];
578             int bh = lowRGB[i * 3 + 2];
579             // int rd = abs(rh - ri);
580             // int gd = abs(gh - gi);
581             // int bd = abs(bh - bi);
582             // int udiff = rd + gd + bd;
583             float udiff = colorDistance(col(ri, gi, bi), col(rh, gh, bh));
584             if (udiff < diff) {
585                 diff = udiff;
586                 pos = i;
587             }
588         }
589         // write(pos);
590 
591         int ansi = pos + 30;
592         if (pos >= 8) ansi += 60 - 8;
593         return ansi + (isBackground ? 10 : 0);
594     }
595 
596     /** 
597      * Returns closest ANSI256 color index
598      * Returns: ANSI256 color
599      */
600     int toAnsi() {
601         /*
602         * 256 ANSI color coding is:
603         * 0 - 14 Special colors, probably check by hand
604         * goes like:
605         * black, red, green, yellow, blue, magenta, cyan, lgray
606         * then repeat with lighter variation
607         * 16 - 231 RGB colors with color coding like this:
608         * Pure R component is on 16, 52, 88, 124, 160, 196. Aka map(r, comp)
609         * B component is r +0..5
610         * G component is rb +0,6,12,18,24,30 (but not 36 coz it's next red)
611         * in end rgb coding, considering mcol = floor(col*5)
612         * rgbansi = 16 + (16 * r) + (6 * g) + b;
613         * 232 - 255 Grayscale from dark to light
614         * refer to https://misc.flogisoft.com/_media/bash/colors_format/256-colors.sh-v2.png
615         */
616 
617         float r = data[0];
618         float g = data[1];
619         float b = data[2];
620 
621         int ri = rint(data[0] * 255);
622         int gi = rint(data[1] * 255);
623         int bi = rint(data[2] * 255);
624 
625         if (ri / 16 == gi / 16 && gi / 16 == bi / 16) {
626             // handles grayscale
627             if (ri < 8) {
628                 return 16;
629             }
630 
631             if (ri > 248) {
632                 return 231;
633             }
634 
635             return rint(((ri - 8.0) / 247.0) * 24.0) + 232;
636         }
637 
638         int ansi = 16
639             + ( 36 * rint(r * 5)) 
640             + ( 6 *  rint(g * 5)) 
641             +        rint(b * 5);
642 
643         return ansi;
644     }
645 
646     /** 
647      * Returns closest bash ANSI8 color string 
648      * Params:
649      *   isBackground = Is color a background
650      * Returns: Bash ANSI8 color string
651      */
652     string toAnsi8String(bool isBackground = false) {
653         return "\033[" ~ toAnsi8(isBackground).to!string ~ "m";
654     }
655 
656     /** 
657      * Returns closest bash ANSI256 color string 
658      * Params:
659      *   isBackground = Is color a background
660      * Returns: Bash ANSI256 color string
661      */
662     string toAnsiString(bool isBackground = false) {
663         return (isBackground ? "\033[48;5;" : "\033[38;5;") ~ 
664             toAnsi().to!string ~ "m";
665     }
666 
667     /** 
668      * Returns bash truecolor string
669      * Params:
670      *   isBackground = Is color a background
671      * Returns: Bash truecolor string
672      */
673     string toTrueColorString(bool isBackground = false) {
674         return (isBackground ? "\033[48;2;" : "\033[38;2;") ~ 
675             rint(data[0] * 255).to!string ~ ";" ~ 
676             rint(data[1] * 255).to!string ~ ";"  ~ 
677             rint(data[2] * 255).to!string  ~ "m";
678         
679     }
680 
681 
682     // TODO add colours from godot
683     // LINK https://github.com/godotengine/godot/blob/master/core/math/color.cpp
684     
685     private static const uint[] lowHEX = [
686     0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0,
687     0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff
688     ];
689 
690     private static const ushort[] lowRGB = [
691         0, 0, 0,  128, 0, 0,  0, 128, 0,  128, 128, 0,  0, 0, 128,  128, 0, 128,  0, 128, 128,  192, 192, 192,
692         128, 128, 128,  255, 0, 0,  0, 255, 0,  255, 255, 0,  0, 0, 255,  255, 0, 255,  0, 255, 255,  255, 255, 255
693     ];
694 }
695 
696 
697 enum Colors
698 {
699     aliceBlue            = Color8(240,248,255), /// <font color=aliceBlue>&#x25FC;</font>
700     antiqueWhite         = Color8(250,235,215), /// <font color=antiqueWhite>&#x25FC;</font>
701     aqua                 = Color8(0,255,255),   /// <font color=aqua>&#x25FC;</font>
702     aquamarine           = Color8(127,255,212), /// <font color=aquamarine>&#x25FC;</font>
703     azure                = Color8(240,255,255), /// <font color=azure>&#x25FC;</font>
704     beige                = Color8(245,245,220), /// <font color=beige>&#x25FC;</font>
705     bisque               = Color8(255,228,196), /// <font color=bisque>&#x25FC;</font>
706     black                = Color8(0,0,0),       /// <font color=black>&#x25FC;</font>
707     blanchedAlmond       = Color8(255,235,205), /// <font color=blanchedAlmond>&#x25FC;</font>
708     blue                 = Color8(0,0,255),     /// <font color=blue>&#x25FC;</font>
709     blueViolet           = Color8(138,43,226),  /// <font color=blueViolet>&#x25FC;</font>
710     brown                = Color8(165,42,42),   /// <font color=brown>&#x25FC;</font>
711     burlyWood            = Color8(222,184,135), /// <font color=burlyWood>&#x25FC;</font>
712     cadetBlue            = Color8(95,158,160),  /// <font color=cadetBlue>&#x25FC;</font>
713     chartreuse           = Color8(127,255,0),   /// <font color=chartreuse>&#x25FC;</font>
714     chocolate            = Color8(210,105,30),  /// <font color=chocolate>&#x25FC;</font>
715     coral                = Color8(255,127,80),  /// <font color=coral>&#x25FC;</font>
716     cornflowerBlue       = Color8(100,149,237), /// <font color=cornflowerBlue>&#x25FC;</font>
717     cornsilk             = Color8(255,248,220), /// <font color=cornsilk>&#x25FC;</font>
718     crimson              = Color8(220,20,60),   /// <font color=crimson>&#x25FC;</font>
719     cyan                 = Color8(0,255,255),   /// <font color=cyan>&#x25FC;</font>
720     darkBlue             = Color8(0,0,139),     /// <font color=darkBlue>&#x25FC;</font>
721     darkCyan             = Color8(0,139,139),   /// <font color=darkCyan>&#x25FC;</font>
722     darkGoldenrod        = Color8(184,134,11),  /// <font color=darkGoldenrod>&#x25FC;</font>
723     darkGray             = Color8(169,169,169), /// <font color=darkGray>&#x25FC;</font>
724     darkGrey             = Color8(169,169,169), /// <font color=darkGrey>&#x25FC;</font>
725     darkGreen            = Color8(0,100,0),     /// <font color=darkGreen>&#x25FC;</font>
726     darkKhaki            = Color8(189,183,107), /// <font color=darkKhaki>&#x25FC;</font>
727     darkMagenta          = Color8(139,0,139),   /// <font color=darkMagenta>&#x25FC;</font>
728     darkOliveGreen       = Color8(85,107,47),   /// <font color=darkOliveGreen>&#x25FC;</font>
729     darkOrange           = Color8(255,140,0),   /// <font color=darkOrange>&#x25FC;</font>
730     darkOrchid           = Color8(153,50,204),  /// <font color=darkOrchid>&#x25FC;</font>
731     darkRed              = Color8(139,0,0),     /// <font color=darkRed>&#x25FC;</font>
732     darkSalmon           = Color8(233,150,122), /// <font color=darkSalmon>&#x25FC;</font>
733     darkSeaGreen         = Color8(143,188,143), /// <font color=darkSeaGreen>&#x25FC;</font>
734     darkSlateBlue        = Color8(72,61,139),   /// <font color=darkSlateBlue>&#x25FC;</font>
735     darkSlateGray        = Color8(47,79,79),    /// <font color=darkSlateGray>&#x25FC;</font>
736     darkSlateGrey        = Color8(47,79,79),    /// <font color=darkSlateGrey>&#x25FC;</font>
737     darkTurquoise        = Color8(0,206,209),   /// <font color=darkTurquoise>&#x25FC;</font>
738     darkViolet           = Color8(148,0,211),   /// <font color=darkViolet>&#x25FC;</font>
739     deepPink             = Color8(255,20,147),  /// <font color=deepPink>&#x25FC;</font>
740     deepSkyBlue          = Color8(0,191,255),   /// <font color=deepSkyBlue>&#x25FC;</font>
741     dimGray              = Color8(105,105,105), /// <font color=dimGray>&#x25FC;</font>
742     dimGrey              = Color8(105,105,105), /// <font color=dimGrey>&#x25FC;</font>
743     dodgerBlue           = Color8(30,144,255),  /// <font color=dodgerBlue>&#x25FC;</font>
744     fireBrick            = Color8(178,34,34),   /// <font color=fireBrick>&#x25FC;</font>
745     floralWhite          = Color8(255,250,240), /// <font color=floralWhite>&#x25FC;</font>
746     forestGreen          = Color8(34,139,34),   /// <font color=forestGreen>&#x25FC;</font>
747     fuchsia              = Color8(255,0,255),   /// <font color=fuchsia>&#x25FC;</font>
748     gainsboro            = Color8(220,220,220), /// <font color=gainsboro>&#x25FC;</font>
749     ghostWhite           = Color8(248,248,255), /// <font color=ghostWhite>&#x25FC;</font>
750     gold                 = Color8(255,215,0),   /// <font color=gold>&#x25FC;</font>
751     goldenrod            = Color8(218,165,32),  /// <font color=goldenrod>&#x25FC;</font>
752     gray                 = Color8(128,128,128), /// <font color=gray>&#x25FC;</font>
753     grey                 = Color8(128,128,128), /// <font color=grey>&#x25FC;</font>
754     green                = Color8(0,128,0),     /// <font color=green>&#x25FC;</font>
755     greenYellow          = Color8(173,255,47),  /// <font color=greenYellow>&#x25FC;</font>
756     honeydew             = Color8(240,255,240), /// <font color=honeydew>&#x25FC;</font>
757     hotPink              = Color8(255,105,180), /// <font color=hotPink>&#x25FC;</font>
758     indianRed            = Color8(205,92,92),   /// <font color=indianRed>&#x25FC;</font>
759     indigo               = Color8(75,0,130),    /// <font color=indigo>&#x25FC;</font>
760     ivory                = Color8(255,255,240), /// <font color=ivory>&#x25FC;</font>
761     khaki                = Color8(240,230,140), /// <font color=khaki>&#x25FC;</font>
762     lavender             = Color8(230,230,250), /// <font color=lavender>&#x25FC;</font>
763     lavenderBlush        = Color8(255,240,245), /// <font color=lavenderBlush>&#x25FC;</font>
764     lawnGreen            = Color8(124,252,0),   /// <font color=lawnGreen>&#x25FC;</font>
765     lemonChiffon         = Color8(255,250,205), /// <font color=lemonChiffon>&#x25FC;</font>
766     lightBlue            = Color8(173,216,230), /// <font color=lightBlue>&#x25FC;</font>
767     lightCoral           = Color8(240,128,128), /// <font color=lightCoral>&#x25FC;</font>
768     lightCyan            = Color8(224,255,255), /// <font color=lightCyan>&#x25FC;</font>
769     lightGoldenrodYellow = Color8(250,250,210), /// <font color=lightGoldenrodYellow>&#x25FC;</font>
770     lightGray            = Color8(211,211,211), /// <font color=lightGray>&#x25FC;</font>
771     lightGrey            = Color8(211,211,211), /// <font color=lightGrey>&#x25FC;</font>
772     lightGreen           = Color8(144,238,144), /// <font color=lightGreen>&#x25FC;</font>
773     lightPink            = Color8(255,182,193), /// <font color=lightPink>&#x25FC;</font>
774     lightSalmon          = Color8(255,160,122), /// <font color=lightSalmon>&#x25FC;</font>
775     lightSeaGreen        = Color8(32,178,170),  /// <font color=lightSeaGreen>&#x25FC;</font>
776     lightSkyBlue         = Color8(135,206,250), /// <font color=lightSkyBlue>&#x25FC;</font>
777     lightSlateGray       = Color8(119,136,153), /// <font color=lightSlateGray>&#x25FC;</font>
778     lightSlateGrey       = Color8(119,136,153), /// <font color=lightSlateGrey>&#x25FC;</font>
779     lightSteelBlue       = Color8(176,196,222), /// <font color=lightSteelBlue>&#x25FC;</font>
780     lightYellow          = Color8(255,255,224), /// <font color=lightYellow>&#x25FC;</font>
781     lime                 = Color8(0,255,0),     /// <font color=lime>&#x25FC;</font>
782     limeGreen            = Color8(50,205,50),   /// <font color=limeGreen>&#x25FC;</font>
783     linen                = Color8(250,240,230), /// <font color=linen>&#x25FC;</font>
784     magenta              = Color8(255,0,255),   /// <font color=magenta>&#x25FC;</font>
785     maroon               = Color8(128,0,0),     /// <font color=maroon>&#x25FC;</font>
786     mediumAquamarine     = Color8(102,205,170), /// <font color=mediumAquamarine>&#x25FC;</font>
787     mediumBlue           = Color8(0,0,205),     /// <font color=mediumBlue>&#x25FC;</font>
788     mediumOrchid         = Color8(186,85,211),  /// <font color=mediumOrchid>&#x25FC;</font>
789     mediumPurple         = Color8(147,112,219), /// <font color=mediumPurple>&#x25FC;</font>
790     mediumSeaGreen       = Color8(60,179,113),  /// <font color=mediumSeaGreen>&#x25FC;</font>
791     mediumSlateBlue      = Color8(123,104,238), /// <font color=mediumSlateBlue>&#x25FC;</font>
792     mediumSpringGreen    = Color8(0,250,154),   /// <font color=mediumSpringGreen>&#x25FC;</font>
793     mediumTurquoise      = Color8(72,209,204),  /// <font color=mediumTurquoise>&#x25FC;</font>
794     mediumVioletRed      = Color8(199,21,133),  /// <font color=mediumVioletRed>&#x25FC;</font>
795     midnightBlue         = Color8(25,25,112),   /// <font color=midnightBlue>&#x25FC;</font>
796     mintCream            = Color8(245,255,250), /// <font color=mintCream>&#x25FC;</font>
797     mistyRose            = Color8(255,228,225), /// <font color=mistyRose>&#x25FC;</font>
798     moccasin             = Color8(255,228,181), /// <font color=moccasin>&#x25FC;</font>
799     navajoWhite          = Color8(255,222,173), /// <font color=navajoWhite>&#x25FC;</font>
800     navy                 = Color8(0,0,128),     /// <font color=navy>&#x25FC;</font>
801     oldLace              = Color8(253,245,230), /// <font color=oldLace>&#x25FC;</font>
802     olive                = Color8(128,128,0),   /// <font color=olive>&#x25FC;</font>
803     oliveDrab            = Color8(107,142,35),  /// <font color=oliveDrab>&#x25FC;</font>
804     orange               = Color8(255,165,0),   /// <font color=orange>&#x25FC;</font>
805     orangeRed            = Color8(255,69,0),    /// <font color=orangeRed>&#x25FC;</font>
806     orchid               = Color8(218,112,214), /// <font color=orchid>&#x25FC;</font>
807     paleGoldenrod        = Color8(238,232,170), /// <font color=paleGoldenrod>&#x25FC;</font>
808     paleGreen            = Color8(152,251,152), /// <font color=paleGreen>&#x25FC;</font>
809     paleTurquoise        = Color8(175,238,238), /// <font color=paleTurquoise>&#x25FC;</font>
810     paleVioletRed        = Color8(219,112,147), /// <font color=paleVioletRed>&#x25FC;</font>
811     papayaWhip           = Color8(255,239,213), /// <font color=papayaWhip>&#x25FC;</font>
812     peachPuff            = Color8(255,218,185), /// <font color=peachPuff>&#x25FC;</font>
813     peru                 = Color8(205,133,63),  /// <font color=peru>&#x25FC;</font>
814     pink                 = Color8(255,192,203), /// <font color=pink>&#x25FC;</font>
815     plum                 = Color8(221,160,221), /// <font color=plum>&#x25FC;</font>
816     powderBlue           = Color8(176,224,230), /// <font color=powderBlue>&#x25FC;</font>
817     purple               = Color8(128,0,128),   /// <font color=purple>&#x25FC;</font>
818     red                  = Color8(255,0,0),     /// <font color=red>&#x25FC;</font>
819     rosyBrown            = Color8(188,143,143), /// <font color=rosyBrown>&#x25FC;</font>
820     royalBlue            = Color8(65,105,225),  /// <font color=royalBlue>&#x25FC;</font>
821     saddleBrown          = Color8(139,69,19),   /// <font color=saddleBrown>&#x25FC;</font>
822     salmon               = Color8(250,128,114), /// <font color=salmon>&#x25FC;</font>
823     sandyBrown           = Color8(244,164,96),  /// <font color=sandyBrown>&#x25FC;</font>
824     seaGreen             = Color8(46,139,87),   /// <font color=seaGreen>&#x25FC;</font>
825     seashell             = Color8(255,245,238), /// <font color=seashell>&#x25FC;</font>
826     sienna               = Color8(160,82,45),   /// <font color=sienna>&#x25FC;</font>
827     silver               = Color8(192,192,192), /// <font color=silver>&#x25FC;</font>
828     skyBlue              = Color8(135,206,235), /// <font color=skyBlue>&#x25FC;</font>
829     slateBlue            = Color8(106,90,205),  /// <font color=slateBlue>&#x25FC;</font>
830     slateGray            = Color8(112,128,144), /// <font color=slateGray>&#x25FC;</font>
831     slateGrey            = Color8(112,128,144), /// <font color=slateGrey>&#x25FC;</font>
832     snow                 = Color8(255,250,250), /// <font color=snow>&#x25FC;</font>
833     springGreen          = Color8(0,255,127),   /// <font color=springGreen>&#x25FC;</font>
834     steelBlue            = Color8(70,130,180),  /// <font color=steelBlue>&#x25FC;</font>
835     tan                  = Color8(210,180,140), /// <font color=tan>&#x25FC;</font>
836     teal                 = Color8(0,128,128),   /// <font color=teal>&#x25FC;</font>
837     thistle              = Color8(216,191,216), /// <font color=thistle>&#x25FC;</font>
838     tomato               = Color8(255,99,71),   /// <font color=tomato>&#x25FC;</font>
839     turquoise            = Color8(64,224,208),  /// <font color=turquoise>&#x25FC;</font>
840     violet               = Color8(238,130,238), /// <font color=violet>&#x25FC;</font>
841     wheat                = Color8(245,222,179), /// <font color=wheat>&#x25FC;</font>
842     white                = Color8(255,255,255), /// <font color=white>&#x25FC;</font>
843     whiteSmoke           = Color8(245,245,245), /// <font color=whiteSmoke>&#x25FC;</font>
844     yellow               = Color8(255,255,0),   /// <font color=yellow>&#x25FC;</font>
845     yellowGreen          = Color8(154,205,50)   /// <font color=yellowGreen>&#x25FC;</font>
846 }