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