0fdaa90e749e2c73ef5544e424e456f397619b96
[xscreensaver] / hacks / helix.c
1 /* xscreensaver, Copyright (c) 1992-2008 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
12 /* Algorithm from a Mac program by Chris Tate, written in 1988 or so. */
13
14 /* 18-Sep-97: Johannes Keukelaar (johannes@nada.kth.se): Improved screen
15  *            eraser.
16  * 10-May-97: merged ellipse code by Dan Stromberg <strombrg@nis.acs.uci.edu>
17  *            as found in xlockmore 4.03a10.
18  * 1992:      jwz created.
19  */
20
21 /* 25 April 2002: Matthew Strait <straitm@mathcs.carleton.edu> added
22 -subdelay option so the drawing process can be watched */
23
24 #include <math.h>
25 #include "screenhack.h"
26 #include "erase.h"
27
28 enum draw_state { HELIX, DRAW_HELIX, TRIG, DRAW_TRIG, LINGER, ERASE };
29
30 struct state {
31   enum draw_state dstate;
32   double sins [360];
33   double coss [360];
34
35   GC draw_gc;
36   unsigned int default_fg_pixel;
37   int sleep_time;
38   int subdelay;
39   eraser_state *eraser;
40
41   int width, height;
42   Colormap cmap;
43
44   int x1, y1, x2, y2, angle, i;
45
46   int radius1, radius2, d_angle, factor1, factor2, factor3, factor4;
47   int d_angle_offset;
48   int offset, dir, density;
49 };
50
51 static void *
52 helix_init (Display *dpy, Window window)
53 {
54   struct state *st = (struct state *) calloc (1, sizeof(*st));
55   int i;
56   XGCValues gcv;
57   XWindowAttributes xgwa;
58
59   st->sleep_time = get_integer_resource(dpy, "delay", "Integer");
60   st->subdelay = get_integer_resource(dpy, "subdelay", "Integer");
61
62   XGetWindowAttributes (dpy, window, &xgwa);
63   st->width = xgwa.width;
64   st->height = xgwa.height;
65   st->cmap = xgwa.colormap;
66   gcv.foreground = st->default_fg_pixel =
67     get_pixel_resource (dpy, st->cmap, "foreground", "Foreground");
68   st->draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
69   gcv.foreground = get_pixel_resource (dpy, st->cmap, "background", "Background");
70
71   for (i = 0; i < 360; i++)
72     {
73       st->sins [i] = sin ((((double) i) / 180.0) * M_PI);
74       st->coss [i] = cos ((((double) i) / 180.0) * M_PI);
75     }
76
77   st->dstate = (random() & 1) ? HELIX : TRIG;
78
79   return st;
80 }
81
82 static int
83 gcd (int a, int b)
84 {
85   while (b > 0)
86     {
87       int tmp;
88       tmp = a % b;
89       a = b;
90       b = tmp;
91     }
92   return (a < 0 ? -a : a);
93 }
94
95 static void
96 helix (Display *dpy, Window window, struct state *st)
97 {
98   int xmid = st->width / 2;
99   int ymid = st->height / 2;
100   int limit = 1 + (360 / gcd (360, st->d_angle));
101
102   if (st->i == 0)
103     {
104       st->x1 = xmid;
105       st->y1 = ymid + st->radius2;
106       st->x2 = xmid;
107       st->y2 = ymid + st->radius1;
108       st->angle = 0;
109     }
110   
111 /*  for (st->i = 0; st->i < limit; st->i++)*/
112     {
113       int tmp;
114 #define pmod(x,y) (tmp=((x) % (y)), (tmp >= 0 ? tmp : (tmp + (y))))
115
116       st->x1 = xmid + (((double) st->radius1) * st->sins [pmod ((st->angle * st->factor1), 360)]);
117       st->y1 = ymid + (((double) st->radius2) * st->coss [pmod ((st->angle * st->factor2), 360)]);
118       XDrawLine (dpy, window, st->draw_gc, st->x1, st->y1, st->x2, st->y2);
119       st->x2 = xmid + (((double) st->radius2) * st->sins [pmod ((st->angle * st->factor3), 360)]);
120       st->y2 = ymid + (((double) st->radius1) * st->coss [pmod ((st->angle * st->factor4), 360)]);
121       XDrawLine (dpy, window, st->draw_gc, st->x1, st->y1, st->x2, st->y2);
122       st->angle += st->d_angle;
123     }
124     st->i++;
125
126     if (st->i >= limit)
127       st->dstate = LINGER;
128 }
129
130 static void
131 trig (Display *dpy, Window window, struct state *st)
132 {
133   int xmid = st->width / 2;
134   int ymid = st->height / 2;
135
136 /*  while (st->d_angle >= -360 && st->d_angle <= 360)*/
137     {
138       int tmp;
139       int angle = st->d_angle + st->d_angle_offset;
140       st->x1 = (st->sins [pmod(angle * st->factor1, 360)] * xmid) + xmid;
141       st->y1 = (st->coss [pmod(angle * st->factor1, 360)] * ymid) + ymid;
142       st->x2 = (st->sins [pmod(angle * st->factor2 + st->offset, 360)] * xmid) + xmid;
143       st->y2 = (st->coss [pmod(angle * st->factor2 + st->offset, 360)] * ymid) + ymid;
144       XDrawLine(dpy, window, st->draw_gc, st->x1, st->y1, st->x2, st->y2);
145       tmp = (int) 360 / (2 * st->density * st->factor1 * st->factor2);
146       if (tmp == 0)     /* Do not want it getting stuck... */
147         tmp = 1;        /* Would not need if floating point */
148       st->d_angle += st->dir * tmp;
149     }
150
151   if (st->d_angle < -360 || st->d_angle > 360)
152     st->dstate = LINGER;
153 }
154
155
156 #define min(a,b) ((a)<(b)?(a):(b))
157
158 static void
159 random_helix (Display *dpy, Window window, struct state *st,
160               XColor *color, Bool *got_color)
161 {
162   int radius;
163   double divisor;
164
165   radius = min (st->width, st->height) / 2;
166
167   st->i = 0;
168   st->d_angle = 0;
169   st->factor1 = 2;
170   st->factor2 = 2;
171   st->factor3 = 2;
172   st->factor4 = 2;
173
174   divisor = ((frand (3.0) + 1) * (((random() & 1) * 2) - 1));
175
176   if ((random () & 1) == 0)
177     {
178       st->radius1 = radius;
179       st->radius2 = radius / divisor;
180     }
181   else
182     {
183       st->radius2 = radius;
184       st->radius1 = radius / divisor;
185     }
186
187   while (gcd (360, st->d_angle) >= 2)
188     st->d_angle = random () % 360;
189
190 #define random_factor()                         \
191   (((random() % 7) ? ((random() & 1) + 1) : 3)  \
192    * (((random() & 1) * 2) - 1))
193
194   while (gcd (gcd (gcd (st->factor1, st->factor2), st->factor3), st->factor4) != 1)
195     {
196       st->factor1 = random_factor ();
197       st->factor2 = random_factor ();
198       st->factor3 = random_factor ();
199       st->factor4 = random_factor ();
200     }
201
202   if (mono_p)
203     XSetForeground (dpy, st->draw_gc, st->default_fg_pixel);
204   else
205     {
206       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
207                   &color->red, &color->green, &color->blue);
208       if ((*got_color = XAllocColor (dpy, st->cmap, color)))
209         XSetForeground (dpy, st->draw_gc, color->pixel);
210       else
211         XSetForeground (dpy, st->draw_gc, st->default_fg_pixel);
212     }
213
214   XClearWindow (dpy, window);
215 }
216
217 static void
218 random_trig (Display *dpy, Window window, struct state *st,
219              XColor *color, Bool *got_color)
220 {
221   st->d_angle = 0;
222   st->factor1 = (random() % 8) + 1;
223   do {
224     st->factor2 = (random() % 8) + 1;
225   } while (st->factor1 == st->factor2);
226
227   st->dir = (random() & 1) ? 1 : -1;
228   st->d_angle_offset = random() % 360;
229   st->offset = ((random() % ((360 / 4) - 1)) + 1) / 4;
230   st->density = 1 << ((random() % 4) + 4);      /* Higher density, higher angles */
231
232   if (mono_p)
233     XSetForeground (dpy, st->draw_gc, st->default_fg_pixel);
234   else
235     {
236       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
237                   &color->red, &color->green, &color->blue);
238       if ((*got_color = XAllocColor (dpy, st->cmap, color)))
239         XSetForeground (dpy, st->draw_gc, color->pixel);
240       else
241         XSetForeground (dpy, st->draw_gc, st->default_fg_pixel);
242     }
243
244   XClearWindow (dpy, window);
245 }
246
247
248 /* random_helix_or_trig */
249 static unsigned long
250 helix_draw (Display *dpy, Window window, void *closure)
251 {
252   struct state *st = (struct state *) closure;
253   Bool free_color = False;
254   XColor color;
255   int delay = st->subdelay;
256   int erase_delay = 10000;
257   int ii;
258
259   if (st->eraser) {
260     st->eraser = erase_window (dpy, window, st->eraser);
261     if (st->eraser) 
262       delay = erase_delay;
263     goto END;
264   }
265
266   switch (st->dstate) 
267     {
268     case LINGER:
269       delay = st->sleep_time * 1000000;
270       st->dstate = ERASE;
271       break;
272
273     case ERASE:
274       st->eraser = erase_window (dpy, window, st->eraser);
275       delay = erase_delay;
276       if (free_color) XFreeColors (dpy, st->cmap, &color.pixel, 1, 0);
277       st->dstate = (random() & 1) ? HELIX : TRIG;
278       break;
279
280     case DRAW_HELIX:
281       for (ii = 0; ii < 10; ii++) {
282         helix (dpy, window, st);
283         if (st->dstate != DRAW_HELIX)
284           break;
285       }
286       break;
287
288     case DRAW_TRIG:
289       for (ii = 0; ii < 5; ii++) {
290         trig (dpy, window, st);
291         if (st->dstate != DRAW_TRIG)
292           break;
293       }
294       break;
295
296     case HELIX:
297       random_helix (dpy, window, st, &color, &free_color);
298       st->dstate = DRAW_HELIX;
299       break;
300
301     case TRIG:
302       random_trig(dpy, window, st, &color, &free_color);
303       st->dstate = DRAW_TRIG;
304       break;
305
306     default: 
307       abort();
308     }
309
310  END:
311   return delay;
312 }
313
314 static void
315 helix_reshape (Display *dpy, Window window, void *closure, 
316                  unsigned int w, unsigned int h)
317 {
318   struct state *st = (struct state *) closure;
319   st->width = w;
320   st->height = h;
321 }
322
323 static Bool
324 helix_event (Display *dpy, Window window, void *closure, XEvent *event)
325 {
326   return False;
327 }
328
329 static void
330 helix_free (Display *dpy, Window window, void *closure)
331 {
332   struct state *st = (struct state *) closure;
333   free (st);
334 }
335
336
337 \f
338 static const char *helix_defaults [] = {
339   ".background: black",
340   ".foreground: white",
341   "*fpsSolid:   true",
342   "*delay:      5",
343   "*subdelay:   20000",
344 #ifdef HAVE_MOBILE
345   "*ignoreRotation: True",
346 #endif
347   0
348 };
349
350 static XrmOptionDescRec helix_options [] = {   
351   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
352   { "-subdelay",        ".subdelay",            XrmoptionSepArg, 0 },
353   { 0,                  0,                      0,               0 },
354 };
355
356 XSCREENSAVER_MODULE ("Helix", helix)