3a26984ea8d9084333bea03746f6a54977b811f6
[xscreensaver] / hacks / helix.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
2  *  Jamie Zawinski <jwz@netscape.com>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Algorithm from a Mac program by Chris Tate, written in 1988 or so. */
14
15 /* 18-Sep-97: Johannes Keukelaar (johannes@nada.kth.se): Improved screen
16  *            eraser.
17  * 10-May-97: merged ellipse code by Dan Stromberg <strombrg@nis.acs.uci.edu>
18  *            as found in xlockmore 4.03a10.
19  * 1992:      jwz created.
20  */
21
22 #include <math.h>
23 #include "screenhack.h"
24 #include "erase.h"
25
26 static double sins [360];
27 static double coss [360];
28
29 static GC draw_gc;
30 static unsigned int default_fg_pixel;
31 static int sleep_time;
32
33 static void
34 init_helix (Display *dpy, Window window)
35 {
36   int i;
37   XGCValues gcv;
38   XWindowAttributes xgwa;
39   Colormap cmap;
40   XGetWindowAttributes (dpy, window, &xgwa);
41   cmap = xgwa.colormap;
42   gcv.foreground = default_fg_pixel =
43     get_pixel_resource ("foreground", "Foreground", dpy, cmap);
44   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
45   gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
46
47   for (i = 0; i < 360; i++)
48     {
49       sins [i] = sin ((((double) i) / 180.0) * M_PI);
50       coss [i] = cos ((((double) i) / 180.0) * M_PI);
51     }
52 }
53
54 static int
55 gcd (int a, int b)
56 {
57   while (b > 0)
58     {
59       int tmp;
60       tmp = a % b;
61       a = b;
62       b = tmp;
63     }
64   return (a < 0 ? -a : a);
65 }
66
67 static void
68 helix (Display *dpy, Window window,
69        int radius1, int radius2, int d_angle,
70        int factor1, int factor2, int factor3, int factor4)
71 {
72   XWindowAttributes xgwa;
73   int width, height;
74   int xmid, ymid;
75   int x1, y1, x2, y2, angle, limit;
76   int i;
77
78   XClearWindow (dpy, window);
79   XGetWindowAttributes (dpy, window, &xgwa);
80   width = xgwa.width;
81   height = xgwa.height;
82
83   xmid = width / 2;
84   ymid = height / 2;
85   x1 = xmid;
86   y1 = ymid + radius2;
87   x2 = xmid;
88   y2 = ymid + radius1;
89   angle = 0;
90   limit = 1 + (360 / gcd (360, d_angle));
91   
92   for (i = 0; i < limit; i++)
93     {
94       int tmp;
95 #define pmod(x,y) (tmp=((x) % (y)), (tmp >= 0 ? tmp : (tmp + (y))))
96
97       x1 = xmid + (((double) radius1) * sins [pmod ((angle * factor1), 360)]);
98       y1 = ymid + (((double) radius2) * coss [pmod ((angle * factor2), 360)]);
99       XDrawLine (dpy, window, draw_gc, x1, y1, x2, y2);
100       x2 = xmid + (((double) radius2) * sins [pmod ((angle * factor3), 360)]);
101       y2 = ymid + (((double) radius1) * coss [pmod ((angle * factor4), 360)]);
102
103       XDrawLine (dpy, window, draw_gc, x1, y1, x2, y2);
104       angle += d_angle;
105       XFlush (dpy);
106     }
107 }
108
109 static void
110 trig (Display *dpy, Window window,
111       int d_angle, int factor1, int factor2,
112       int offset, int d_angle_offset, int dir, int density)
113 {
114   XWindowAttributes xgwa;
115   int width, height;
116   int xmid, ymid;
117   int x1, y1, x2, y2;
118   int tmp, angle;
119   Colormap cmap;
120
121   XClearWindow (dpy, window);
122   XGetWindowAttributes (dpy, window, &xgwa);
123   cmap = xgwa.colormap;
124   width = xgwa.width;
125   height = xgwa.height;
126
127   xmid = width / 2;
128   ymid = height / 2;
129
130   while (d_angle >= -360 && d_angle <= 360)
131     {
132       angle = d_angle + d_angle_offset;
133       x1 = (sins [pmod(angle * factor1, 360)] * xmid) + xmid;
134       y1 = (coss [pmod(angle * factor1, 360)] * ymid) + ymid;
135       x2 = (sins [pmod(angle * factor2 + offset, 360)] * xmid) + xmid;
136       y2 = (coss [pmod(angle * factor2 + offset, 360)] * ymid) + ymid;
137       XDrawLine(dpy, window, draw_gc, x1, y1, x2, y2);
138       tmp = (int) 360 / (2 * density * factor1 * factor2);
139       if (tmp == 0)     /* Do not want it getting stuck... */
140         tmp = 1;        /* Would not need if floating point */
141       d_angle += dir * tmp;
142     }
143 }
144
145 #define min(a,b) ((a)<(b)?(a):(b))
146
147 static void
148 random_helix (Display *dpy, Window window, XColor *color, Bool *got_color)
149 {
150   Colormap cmap;
151   int width, height;
152   int radius, radius1, radius2, d_angle, factor1, factor2, factor3, factor4;
153   double divisor;
154   XWindowAttributes xgwa;
155   XGetWindowAttributes (dpy, window, &xgwa);
156   width = xgwa.width;
157   height = xgwa.height;
158   cmap = xgwa.colormap;
159
160   radius = min (width, height) / 2;
161
162   d_angle = 0;
163   factor1 = 2;
164   factor2 = 2;
165   factor3 = 2;
166   factor4 = 2;
167
168   divisor = ((frand (3.0) + 1) * (((random() & 1) * 2) - 1));
169
170   if ((random () & 1) == 0)
171     {
172       radius1 = radius;
173       radius2 = radius / divisor;
174     }
175   else
176     {
177       radius2 = radius;
178       radius1 = radius / divisor;
179     }
180
181   while (gcd (360, d_angle) >= 2)
182     d_angle = random () % 360;
183
184 #define random_factor()                         \
185   (((random() % 7) ? ((random() & 1) + 1) : 3)  \
186    * (((random() & 1) * 2) - 1))
187
188   while (gcd (gcd (gcd (factor1, factor2), factor3), factor4) != 1)
189     {
190       factor1 = random_factor ();
191       factor2 = random_factor ();
192       factor3 = random_factor ();
193       factor4 = random_factor ();
194     }
195
196   if (mono_p)
197     XSetForeground (dpy, draw_gc, default_fg_pixel);
198   else
199     {
200       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
201                   &color->red, &color->green, &color->blue);
202       if ((*got_color = XAllocColor (dpy, cmap, color)))
203         XSetForeground (dpy, draw_gc, color->pixel);
204       else
205         XSetForeground (dpy, draw_gc, default_fg_pixel);
206     }
207   helix (dpy, window, radius1, radius2, d_angle,
208          factor1, factor2, factor3, factor4);
209 }
210
211 static void
212 random_trig (Display *dpy, Window window, XColor *color, Bool *got_color)
213 {
214   Colormap cmap;
215   int width, height;
216   int radius, d_angle, factor1, factor2;
217   int offset, d_angle_offset, dir, density;
218
219   XWindowAttributes xgwa;
220   XGetWindowAttributes (dpy, window, &xgwa);
221   width = xgwa.width;
222   height = xgwa.height;
223   cmap = xgwa.colormap;
224
225   radius = min (width, height) / 2;
226
227   d_angle = 0;
228   factor1 = (random() % 8) + 1;
229   do {
230     factor2 = (random() % 8) + 1;
231   } while (factor1 == factor2);
232
233   dir = (random() & 1) ? 1 : -1;
234   d_angle_offset = random() % 360;
235   offset = ((random() % ((360 / 4) - 1)) + 1) / 4;
236   density = 1 << ((random() % 4) + 4);  /* Higher density, higher angles */
237
238   if (mono_p)
239     XSetForeground (dpy, draw_gc, default_fg_pixel);
240   else
241     {
242       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
243                   &color->red, &color->green, &color->blue);
244       if ((*got_color = XAllocColor (dpy, cmap, color)))
245         XSetForeground (dpy, draw_gc, color->pixel);
246       else
247         XSetForeground (dpy, draw_gc, default_fg_pixel);
248     }
249   trig (dpy, window, d_angle, factor1, factor2,
250         offset, d_angle_offset, dir, density);
251 }
252
253 static void
254 random_helix_or_trig (Display *dpy, Window window)
255 {
256   Bool free_color = False;
257   XColor color;
258   int width, height;
259   XWindowAttributes xgwa;
260   Colormap cmap;
261   XGetWindowAttributes (dpy, window, &xgwa);
262   width = xgwa.width;
263   height = xgwa.height;
264   cmap = xgwa.colormap;
265
266   if (random() & 1)
267     random_helix(dpy, window, &color, &free_color);
268   else
269     random_trig(dpy, window, &color, &free_color);
270
271   XSync (dpy, True);
272   sleep ( sleep_time );
273
274   erase_full_window(dpy, window);
275
276   if (free_color) XFreeColors (dpy, cmap, &color.pixel, 1, 0);
277   XSync (dpy, True);
278   sleep (1);
279 }
280
281 \f
282 char *progclass = "Helix";
283
284 char *defaults [] = {
285   "*background: black",
286   "*delay:      5",
287   0
288 };
289
290 XrmOptionDescRec options [] = {   
291   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
292   { 0 },
293 };
294 int options_size = (sizeof (options) / sizeof (options[0]));
295
296 void
297 screenhack (Display *dpy, Window window)
298 {
299   sleep_time = get_integer_resource("delay", "Integer");
300   init_helix (dpy, window);
301   while (1)
302     random_helix_or_trig (dpy, window);
303 }