1 /**
2  * Color demo for dcell.
3  *
4  * Copyright: Copyright 2025 Garrett D'Amore
5  * Authors: Garrett D'Amore
6  * License:
7  *   Distributed under the Boost Software License, Version 1.0.
8  *   (See accompanying file LICENSE or https://www.boost.org/LICENSE_1_0.txt)
9  *   SPDX-License-Identifier: BSL-1.0
10  */
11 module color;
12 
13 import core.time;
14 import std.stdio;
15 import std.string;
16 import std.random;
17 import std.range;
18 import dcell;
19 
20 class ColorBoxes
21 {
22     import std.random;
23 
24     int r;
25     int g;
26     int b;
27     int ri;
28     int gi;
29     int bi;
30     Random rng;
31     enum inc = 8;
32     int cnt;
33     bool done;
34     Screen s;
35 
36     bool flip() @safe
37     {
38         return (rng.uniform!ubyte() & 0x1) != 0;
39     }
40 
41     this(Screen scr)
42     {
43         rng = rndGen();
44         r = rng.uniform!ubyte();
45         g = rng.uniform!ubyte();
46         b = rng.uniform!ubyte();
47         ri = inc;
48         gi = inc / 4; // humans are very sensitive to green
49         bi = inc;
50         s = scr;
51     }
52 
53     void makeBox() @safe
54     {
55         Coord wsz = s.size();
56         dchar dc = ' ';
57         Style style;
58         cnt++;
59 
60         if (s.colors() == 0)
61         {
62             dchar[] glyphs = ['@', '#', '&', '*', '=', '%', 'Z', 'A'];
63             dc = choice(glyphs, rng);
64             if (flip())
65                 style.attr |= Attr.reverse;
66             else
67                 style.attr &= ~Attr.reverse;
68         }
69         else
70         {
71             r += ri;
72             g += gi;
73             b += bi;
74             if (r >= 256 || r < 0)
75             {
76                 ri = -ri;
77                 r += ri;
78             }
79             if (g >= 256 || g < 0)
80             {
81                 gi = -gi;
82                 g += gi;
83             }
84             if (b >= 256 || b < 0)
85             {
86                 bi = -bi;
87                 b += bi;
88             }
89             if (cnt % (256 / inc) == 0)
90             {
91                 if (flip())
92                 {
93                     ri = -ri;
94                 }
95 
96                 if (flip())
97                 {
98                     gi = -gi;
99                 }
100 
101                 if (flip())
102                 {
103                     bi = -bi;
104                 }
105             }
106             style.bg = fromHex(
107                 int(r) << 16 | int(g) << 8 | int(b));
108         }
109 
110         // half the width and half the height
111         Coord c1 = Coord(wsz.x / 4, wsz.y / 4);
112         Coord c2 = Coord(c1.x + wsz.x / 2, c1.y + wsz.y / 2);
113         Coord pos = c1;
114         Cell cell = Cell(dc, style);
115         for (pos.y = c1.y; pos.y <= c2.y; pos.y++)
116         {
117             for (pos.x = c1.x; pos.x <= c2.x; pos.x++)
118             {
119                 s[pos] = cell;
120             }
121         }
122         s.show();
123     }
124 
125     void handleEvent(Event ev) @safe
126     {
127         switch (ev.type)
128         {
129         case EventType.key:
130             switch (ev.key.key)
131             {
132             case Key.esc, Key.enter:
133                 done = true;
134                 break;
135             case Key.graph:
136                 // Ctrl-L (without other modifiers) used to force a redraw.
137                 if (ev.key.ch == 'l' && ev.key.mod == Modifiers.ctrl)
138                 {
139                     s.resize();
140                     s.sync();
141                 }
142                 break;
143             default:
144 
145             }
146             break;
147         case EventType.resize:
148             s.resize();
149             break;
150         case EventType.closed:
151             done = true;
152             break;
153         case EventType.error:
154             assert(0, "error received");
155         default:
156         }
157     }
158 
159     void run() @safe
160     {
161         s.start();
162         scope (exit)
163         {
164             s.stop();
165         }
166         done = false;
167         makeBox();
168         while (!done)
169         {
170             loop: foreach (ev; s.events())
171             {
172                 switch (ev.type)
173                 {
174                 case EventType.key:
175                     switch (ev.key.key)
176                     {
177                     case Key.esc, Key.enter:
178                         done = true;
179                         break loop;
180                     case Key.graph:
181                         // Ctrl-L (without other modifiers) used to force a redraw.
182                         if (ev.key.ch == 'l' && ev.key.mod == Modifiers.ctrl)
183                         {
184                             s.resize();
185                             s.sync();
186                         }
187                         break;
188                     default:
189                     }
190                     break;
191                 case EventType.resize:
192                     s.resize();
193                     break;
194                 case EventType.closed:
195                     done = true;
196                     break loop;
197                 case EventType.error:
198                     assert(0, "error received");
199                 default:
200                 }
201             }
202             makeBox();
203             s.position = Coord(1, 1);
204             s.waitForEvent(msecs(50));
205         }
206     }
207 }
208 
209 void main()
210 {
211     import std.stdio;
212     import core.time;
213     import core.stdc.stdlib;
214 
215     ColorBoxes cb = new ColorBoxes(newScreen());
216 
217     auto now = MonoTime.currTime();
218     cb.run();
219     auto end = MonoTime.currTime();
220     writefln("Drew %d boxes in %s.", cb.cnt, end - now);
221     writefln("Average per box %s.", (end - now) / cb.cnt);
222 }