1 public import std
.random
: uniform
;
4 import std
.exception
: Exception
;
5 import std
.functional
: memoize
;
6 import std
.string
: format
;
8 public alias rnd
= uniform
;
10 class DException
: Exception
{
11 pragma(inline
, true) pure this(T
...)(T args
) {
16 class InternalError
: DException
{
17 pragma(inline
, true) pure this(T
...)(T args
) {
23 bool isvalidcolour(string hexcolour
) {
25 if (hexcolour
[0] == '#') {
30 foreach (char nam
; hexcolour
) {
39 if (!((('0' <= nam
) && (nam
<= '9')) ||
(('A' <= nam
) && (nam
<= 'F')))) {
52 bool isvalidcolour(string hexcolour
) {
53 import std
.regex
: ctRegex
, matchFirst
;
54 auto hexcolourmatch
= ctRegex
!("#?[0-9a-fA-F]{3,6}");
56 if (matchFirst(hexcolour
, hexcolourmatch
).empty
) {
65 ubyte r
=0xFF, g
=0xFF, b
=0xFF;
66 pragma(inline
, true) pure this(ubyte in_r
, ubyte in_g
, ubyte in_b
) {
73 // No constructor for colours of the form 0xFFF, because there's no way to tell the difference between 0xFFF and 0x000FFF
74 pragma(inline
, true) pure this(uint clr
) {
77 /* An interesting optimization. Ordinarily, you do b = clr % 256,
78 * g = (clr / 256) % 256, r = (clr / 256 / 256) % 256. left/right
79 * shifts are cheaper than multiplication, and by reassigning clr,
80 * there's an extra multiplication step that doesn't occur.
82 g
= (clr
>>= 8) % 256;
86 pure this(string hexcolour
, bool hasvalidated
=false) {
89 // strip leading #, if present
90 if ((hexcolour
.length
== 7) ||
(hexcolour
.length
== 4)) {
91 hexcolour
= hexcolour
[1..$];
95 if (hexcolour
.length
== 6) {
96 r
= hexcolour
[0..2].to
!ubyte(16);
97 g
= hexcolour
[2..4].to
!ubyte(16);
98 b
= hexcolour
[4..6].to
!ubyte(16);
100 // The ranges here are to get a string just one character long so to!ubyte works
101 r
= cast(ubyte)(hexcolour
[0..1].to
!ubyte(16)*16);
102 g
= cast(ubyte)(hexcolour
[1..2].to
!ubyte(16)*16);
103 b
= cast(ubyte)(hexcolour
[2..3].to
!ubyte(16)*16);
106 throw new InternalError("RGBColour constructor called but rgbcolour not validated!");
110 pragma(inline
, true) this(string hexcolour
) {
111 if (!isvalidcolour(hexcolour
)) {
112 throw new InternalError("RGBColour constructor called with faulty colour.");
114 this(hexcolour
, true);
117 pragma(inline
, true) pure int toint() {
118 return (255 << 24) |
(r
<< 16) |
(g
<< 8) | b
;
121 RGBColour
darken(ubyte level
) {
123 tmp
.r
= cast(ubyte) ((r
< level
) ?
0 : r
-level
);
124 tmp
.g
= cast(ubyte) ((g
< level
) ?
0 : g
-level
);
125 tmp
.b
= cast(ubyte) ((b
< level
) ?
0 : b
-level
);
130 RGBColour
lighten(ubyte level
) {
133 tmp
.r
= cast(ubyte) (((255-r
) < level
) ?
255 : r
+level
);
134 tmp
.g
= cast(ubyte) (((255-g
) < level
) ?
255 : g
+level
);
135 tmp
.b
= cast(ubyte) (((255-b
) < level
) ?
255 : b
+level
);
141 void exit(ubyte code
) {
142 import core
.runtime
: Runtime
;
143 static import core
.stdc
.stdlib
;
145 core
.stdc
.stdlib
.exit(code
);
148 pure string
fillstr(int length
, char ch
=' ') {
150 while (length
--> 0) {
157 // Taken directly from http://stackoverflow.com/a/41978310
158 /* It's supposed to be incredibly performant...but honestly I used it because
159 * I could copy-paste it easily.
161 pure ubyte _get256colour(RGBColour colour
) {
162 enum ubyte[6] i2cv
= [0, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
163 pragma(inline
, true) {
164 pure ubyte function(ubyte) v2ci
= cast(ubyte function(ubyte) pure) (ubyte v
) => v
< 48 ?
0 : v
< 115 ?
1 : (v
- 35) / 40;
165 auto colour_index
= (int r
, int g
, int b
) => 36*r
+ 6*g
+ b
;
166 auto distance
= (int r1
, int g1
, int b1
, int r2
, int g2
, int b2
) => (r1
-r2
)*(r1
-r2
) + (g1
-g2
)*(g1
-g2
) + (b1
-b2
)*(b1
-b2
);
169 ubyte ir
= v2ci(colour
.r
), ig
= v2ci(colour
.g
), ib
= v2ci(colour
.b
);
170 ubyte average
= (colour
.r
+ colour
.g
+ colour
.b
) / 3;
171 ubyte igray
= average
> 238 ?
23 : (average
-3) / 10;
172 ubyte cr
= i2cv
[ir
], cg
= i2cv
[ig
], cb
= i2cv
[ib
];
173 ubyte gv
= cast(ubyte)(8 + 10 * igray
);
175 int colour_err
= distance(cr
, cg
, cb
, colour
.r
, colour
.g
, colour
.b
);
176 int gray_err
= distance(gv
, gv
, gv
, colour
.r
, colour
.g
, colour
.b
);
178 return cast(ubyte)(colour_err
<= gray_err ?
16 + colour_index(ir
, ig
, ib
) : 232 + igray
);
181 alias get256colour
= memoize
!_get256colour
;
184 pragma(inline
, true) ubyte get256colour(ubyte r
, ubyte g
, ubyte b
) { return get256colour(RGBColour(r
, g
, b
)); }
185 pragma(inline
, true) ubyte get256colour(string colour
) { return get256colour(RGBColour(colour
)); }