From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / hexadrop.c
1 /* xscreensaver, Copyright (c) 1999-2014 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Draws a grid of hexagons or other shapes and drops them out.
12  * Created 8-Jul-2013.
13  */
14
15 #include <math.h>
16 #include "screenhack.h"
17
18 #define countof(x) (sizeof(x)/sizeof(*(x)))
19 #define ABS(x) ((x)<0?-(x):(x))
20
21 typedef struct {
22   int sides;
23   int cx, cy;
24   double th0, th, radius, i, speed;
25   int colors[2];
26   Bool initted_p;
27 } cell;
28
29 typedef struct {
30   Display *dpy;
31   Window window;
32   XWindowAttributes xgwa;
33
34   int ncells, cells_size, gw, gh;
35   cell *cells;
36
37   int delay;
38   double speed;
39   int sides;
40   Bool lockstep_p;
41   Bool uniform_p;
42   Bool initted_p;
43
44   int ncolors;
45   XColor *colors;
46   GC gc;
47
48 } state;
49
50
51 static void
52 make_cells (state *st)
53 {
54   int grid_size = get_integer_resource (st->dpy, "size", "Size");
55   cell *cells2;
56   int size, r, gw, gh, x, y, i;
57   double th = 0;
58
59   grid_size = get_integer_resource (st->dpy, "size", "Size");
60   if (grid_size < 5) grid_size = 5;
61
62   size = ((st->xgwa.width > st->xgwa.height
63            ? st->xgwa.width : st->xgwa.height)
64           / grid_size);
65   gw = st->xgwa.width  / size;
66   gh = st->xgwa.height / size;
67
68   switch (st->sides) {
69   case 8:
70     r  = size * 0.75;
71     th = M_PI / st->sides;
72     gw *= 1.25;
73     gh *= 1.25;
74     break;
75   case 6:
76     r  = size / sqrt(3);
77     th = M_PI / st->sides;
78     gh *= 1.2;
79     break;
80   case 3:
81     size *= 2;
82     r  = size / sqrt(3);
83     th = M_PI / st->sides / 2;
84     break;
85   case 4:
86     size /= 2;
87     r  = size * sqrt (2);
88     th = M_PI / st->sides;
89     break;
90   default:
91     abort();
92     break;
93   }
94
95   gw += 3;      /* leave a few extra columns off screen just in case */
96   gh += 3;
97
98   st->ncells = gw * gh;
99
100   if (st->initted_p && !st->cells) abort();
101   if (!st->initted_p && st->cells) abort();
102
103   cells2 = (cell *) calloc (st->ncells, sizeof(*cells2));
104   if (! cells2) abort();
105
106   if (st->cells)
107     {
108       for (y = 0; y < (st->gh < gh ? st->gh : gh); y++)
109         for (x = 0; x < (st->gw < gw ? st->gw : gw); x++)
110           cells2[y * gw + x] = st->cells [y * st->gw + x];
111       free (st->cells);
112       st->cells = 0;
113     }
114
115   st->cells = cells2;
116   st->gw = gw;
117   st->gh = gh;
118
119   i = 0;
120   for (y = 0; y < gh; y++)
121     for (x = 0; x < gw; x++)
122       {
123         cell *c = &st->cells[i];
124         c->sides = st->sides;
125         c->radius = r;
126         c->th = th;
127
128         switch (st->sides) {
129         case 8:
130           if (x & 1)
131             {
132               c->cx = x * size;
133               c->radius /= 2;
134               c->th = M_PI / 4;
135               c->sides = 4;
136               c->radius *= 1.1;
137             }
138           else
139             {
140               c->cx = x * size;
141               c->radius *= 1.02;
142               c->radius--;
143             }
144
145           if (y & 1)
146             c->cx -= size;
147
148           c->cy = y * size;
149
150          break;
151         case 6:
152           c->cx = x * size;
153           c->cy = y * size * sqrt(3)/2;
154           if (y & 1)
155             c->cx -= size * 0.5;
156           break;
157         case 4:
158           c->cx = x * size * 2;
159           c->cy = y * size * 2;
160           break;
161         case 3:
162           c->cx = x * size * 0.5;
163           c->cy = y * size * sqrt(3)/2;
164           if ((x & 1) ^ (y & 1))
165             {
166               c->th = th + M_PI;
167               c->cy -= (r * 0.5);
168             }
169           break;
170         default:
171           abort();
172         }
173
174         if (! c->initted_p)
175           {
176             c->speed = st->speed * (st->uniform_p ? 1 : (0.1 + frand(0.9)));
177             c->i = st->lockstep_p ? 0 : random() % r;
178             c->colors[0] = (st->lockstep_p ? 0 : random() % st->ncolors);
179             c->colors[1] = 0;
180             c->initted_p = True;
181           }
182
183         c->radius += 2;  /* Avoid rounding errors */
184
185         if (c->i > c->radius) c->i = c->radius;
186         if (c->colors[0] >= st->ncolors) c->colors[0] = st->ncolors-1;
187         if (c->colors[1] >= st->ncolors) c->colors[1] = st->ncolors-1;
188
189         i++;
190       }
191
192   st->initted_p = True;
193 }
194
195
196 static void
197 draw_cell (state *st, cell *c)
198 {
199   XPoint points[20];
200   int i, j;
201   for (j = 0; j <= 1; j++)
202     {
203       int r = (j == 0 ? c->radius : c->i);
204       for (i = 0; i < c->sides; i++)
205         {
206           double th = i * M_PI * 2 / c->sides;
207           points[i].x = c->cx + r * cos (th + c->th) + 0.5;
208           points[i].y = c->cy + r * sin (th + c->th) + 0.5;
209         }
210       XSetForeground (st->dpy, st->gc, st->colors[c->colors[j]].pixel);
211       XFillPolygon (st->dpy, st->window, st->gc, points, c->sides,
212                     Convex, CoordModeOrigin);
213     }
214
215   c->i -= c->speed;
216   if (c->i < 0)
217     {
218       c->i = c->radius;
219       c->colors[1] = c->colors[0];
220       if (c != &st->cells[0])
221         c->colors[0] = st->cells[0].colors[0];
222       else
223         c->colors[0] = random() % st->ncolors;
224     }
225 }
226
227
228 static void
229 hexadrop_init_1 (Display *dpy, Window window, state *st)
230 {
231   XGCValues gcv;
232   char *s1, *s2;
233
234   st->dpy = dpy;
235   st->window = window;
236   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
237   st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
238   st->speed = get_float_resource (st->dpy, "speed", "Speed");
239   if (st->speed < 0) st->speed = 0;
240
241   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
242
243   if (st->ncolors < 2) st->ncolors = 2;
244
245   st->colors = (XColor *) calloc (sizeof(*st->colors), st->ncolors);
246
247   if (st->ncolors < 10)
248     make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
249                           st->colors, &st->ncolors, False, True, 0, True);
250   else
251     make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
252                           st->colors, &st->ncolors, True, 0, True);
253   XSetWindowBackground (dpy, window, st->colors[0].pixel);
254
255   s1 = get_string_resource (st->dpy, "uniform", "Uniform");
256   s2 = get_string_resource (st->dpy, "lockstep", "Lockstep");
257
258   if ((!s1 || !*s1 || !strcasecmp(s1, "maybe")) &&
259       (!s2 || !*s2 || !strcasecmp(s2, "maybe")))
260     {
261       /* When being random, don't do both. */
262       st->uniform_p = random() & 1;
263       st->lockstep_p = st->uniform_p ? 0 : random() & 1;
264     }
265   else 
266     {
267       if (!s1 || !*s1 || !strcasecmp(s1, "maybe"))
268         st->uniform_p = random() & 1;
269       else
270         st->uniform_p = get_boolean_resource (st->dpy, "uniform", "Uniform");
271
272       if (!s2 || !*s2 || !strcasecmp(s2, "maybe"))
273         st->lockstep_p = random() & 1;
274       else
275         st->lockstep_p = get_boolean_resource (st->dpy, "lockstep","Lockstep");
276     }
277
278
279   st->sides = get_integer_resource (st->dpy, "sides", "Sides");
280   if (! (st->sides == 0 || st->sides == 3 || st->sides == 4 || 
281          st->sides == 6 || st->sides == 8))
282     {
283       printf ("%s: invalid number of sides: %d\n", progname, st->sides);
284       st->sides = 0;
285     }
286
287   if (! st->sides)
288     {
289       static int defs[] = { 3, 3, 3,
290                             4,
291                             6, 6, 6, 6,
292                             8, 8, 8 };
293       st->sides = defs[random() % countof(defs)];
294     }
295
296   make_cells (st);
297   gcv.foreground = st->colors[0].pixel;
298   st->gc = XCreateGC (dpy, window, GCForeground, &gcv);
299 }
300
301
302 static void *
303 hexadrop_init (Display *dpy, Window window)
304 {
305   state *st = (state *) calloc (1, sizeof(*st));
306   hexadrop_init_1 (dpy, window, st);
307   return st;
308 }
309
310
311
312 static unsigned long
313 hexadrop_draw (Display *dpy, Window window, void *closure)
314 {
315   state *st = (state *) closure;
316   int i;
317
318   for (i = 0; i < st->ncells; i++)
319     draw_cell (st, &st->cells[i]);
320
321   return st->delay;
322 }
323
324
325 static void
326 hexadrop_reshape (Display *dpy, Window window, void *closure, 
327                  unsigned int w, unsigned int h)
328 {
329   state *st = (state *) closure;
330   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
331   make_cells (st);
332 }
333
334
335 static void
336 hexadrop_free (Display *dpy, Window window, void *closure)
337 {
338   state *st = (state *) closure;
339   if (st->colors)
340     {
341       free_colors (st->xgwa.screen, st->xgwa.colormap, st->colors, st->ncolors);
342       free (st->colors);
343       st->colors = 0;
344     }
345   if (st->cells) 
346     {
347       free (st->cells);
348       st->cells = 0;
349     }
350   if (st->gc)
351     {
352       XFreeGC (st->dpy, st->gc);
353       st->gc = 0;
354     }
355 }
356
357
358 static Bool
359 hexadrop_event (Display *dpy, Window window, void *closure, XEvent *event)
360 {
361   state *st = (state *) closure;
362
363   if (screenhack_event_helper (dpy, window, event))
364     {
365       cell *c = st->cells;
366       st->cells = 0;
367       hexadrop_free (st->dpy, st->window, st);
368       free (st->cells);
369       st->cells = c;
370       hexadrop_init_1 (st->dpy, st->window, st);
371       return True;
372     }
373
374   return False;
375 }
376
377
378 static const char *hexadrop_defaults [] = {
379   ".background:         black",
380   ".foreground:         white",
381   "*fpsSolid:           true",
382   "*delay:              30000",
383   "*sides:              0",
384   "*size:               15",
385   "*speed:              1.0",
386   "*ncolors:            128",
387   "*uniform:            Maybe",
388   "*lockstep:           Maybe",
389 #ifdef USE_IPHONE
390   "*ignoreRotation:     True",
391 #endif
392   0
393 };
394
395 static XrmOptionDescRec hexadrop_options [] = {
396   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
397   { "-sides",           ".sides",       XrmoptionSepArg, 0 },
398   { "-size",            ".size",        XrmoptionSepArg, 0 },
399   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
400   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
401   { "-uniform-speed",   ".uniform",     XrmoptionNoArg, "True"  },
402   { "-no-uniform-speed",".uniform",     XrmoptionNoArg, "False" },
403   { "-lockstep",        ".lockstep",    XrmoptionNoArg, "True"  },
404   { "-no-lockstep",     ".lockstep",    XrmoptionNoArg, "False" },
405   { 0, 0, 0, 0 }
406 };
407
408 XSCREENSAVER_MODULE ("Hexadrop", hexadrop)