17ef55343f339c457d66ee7a1fe341dcdb73bd80
[xscreensaver] / hacks / xspirograph.c
1 /* The Spiral Generator, Copyright (c) 2000 
2  * by Rohit Singh <rohit_singh@hotmail.com>
3  * 
4  * Contains code from / To be used with:
5  * xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
6  * Jamie Zawinski <jwz@jwz.org>
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notices appear in all copies and that both that
11  * copyright notices and this permission notice appear in supporting
12  * documentation.  No representations are made about the suitability of this
13  * software for any purpose.  It is provided "as is" without express or 
14  * implied warranty.
15  *
16  * Modified (Dec 2001) by Matthew Strait <straitm@mathcs.carleton.edu>
17  * Added -subdelay and -alwaysfinish
18  * Prevented redrawing over existing lines
19  */
20
21 #include <math.h>
22 #include "screenhack.h"
23 #include "erase.h"
24
25 struct state {
26   Display *dpy;
27   Window window;
28   XWindowAttributes xgwa;
29
30   GC    draw_gc;
31   int   long_delay;
32   int   sub_sleep_time;
33   int   num_layers;
34   unsigned int default_fg_pixel;
35   Bool  always_finish_p;
36   XColor        color;
37   int got_color;
38
39   int theta;
40   float firstx, firsty;
41   int x1, y1, x2, y2;
42
43   int counter;
44   int distance;
45   int radius1, radius2;
46   double divisor;
47
48   enum curstate { NEW_LAYER, DRAW, ERASE1, ERASE2 } drawstate;
49   eraser_state *eraser;
50 };
51
52
53 static void
54 init_tsg (struct state *st)
55 {
56   XGCValues     gcv;
57   Colormap      cmap;
58
59   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
60   cmap = st->xgwa.colormap;
61   gcv.foreground = st->default_fg_pixel =
62     get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
63   st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
64   gcv.foreground = get_pixel_resource (st->dpy, cmap, "background", "Background");
65 }
66
67
68 static Bool
69 go (struct state *st, int radius1, int radius2, int d)
70 {
71   int width, height;
72   int xmid, ymid;
73   float tmpx, tmpy;
74   int delta;
75
76   width  = st->xgwa.width;
77   height = st->xgwa.height;
78   delta = 1;
79   xmid = width / 2;
80   ymid = height / 2;
81
82   if (st->theta == 1) {
83     st->x1 = xmid + radius1 - radius2 + d;
84     st->y1 = ymid;
85   }
86
87 /*  for (theta = 1; / * theta < ( 360 * 100 ) * /; theta++) */
88                   /* see below about alwaysfinish */
89     {
90         tmpx = xmid + ((       radius1            /* * * * *            */
91                   - radius2        )             /* This algo simulates */
92                   * cos((      st->theta                /* the rotation of a    */
93                   * M_PI           )            /* circular disk inside */
94                   / 180           ))            /* a hollow circular    */
95                   + (              d            /* rim. A point on the  */
96                   * cos((((  radius1            /* disk dist d from the */
97                   * st->theta      )            /* centre, traces the   */
98                   - delta          )            /* path given by this   */
99                   / radius2        )            /* equation.            */
100                   *             M_PI            /* A deviation (error)  */
101                   / 180            )            /* of delta needs to be */
102                                    );           /* given, which greatly */
103                                                 /* adds to the beauty   */
104         tmpy = ymid + (                         /* of the figure.       */
105                      ( radius1 - radius2        /*                      */
106                       ) * sin                   /* Imperfection adds to */
107                        (                        /* beauty, symbolically */
108                         ( st->theta * M_PI      /* ...                  */
109                          ) / 180                /* Algo deduced by      */
110                           )                     /* Rohit Singh, Jan'00  */
111                            ) +                  /* based on a toy he    */
112                             ( d * sin           /* used to play with    */
113                              (                  /* when he was a kid.  */
114                               (                 /*            * * * * */
115                                (                
116                                 ( radius1 * st->theta
117                                  ) - delta
118                                   ) / radius2
119                                    ) * M_PI / 180
120                                     )
121                                      );
122         
123         /*makes integers from the calculated values to do the drawing*/
124         st->x2 = tmpx;
125         st->y2 = tmpy;
126
127         /*stores the first values for later reference*/
128         if(st->theta == 1)
129         {
130                 st->firstx = tmpx;
131                 st->firsty = tmpy;
132         }
133
134         if (st->theta != 1)
135           XDrawLine (st->dpy, st->window, st->draw_gc, 
136                      st->x1, st->y1, st->x2, st->y2);
137
138         st->x1 = st->x2;
139         st->y1 = st->y2;
140
141         /* compares the exact values calculated to the first
142            exact values calculated */
143         /* this will break when nothing new is being drawn */
144         if(tmpx == st->firstx && tmpy == st->firsty && st->theta != 1) {
145           st->firstx = st->firsty = 0;
146           st->theta = 1;
147           return True;
148         }
149
150         /* this will break after 36000 iterations if 
151            the -alwaysfinish option is not specified */
152         if(!st->always_finish_p && st->theta > ( 360 * 100 ) ) {
153           st->firstx = st->firsty = 0;
154           st->theta = 1;
155           return True;
156         }
157     }
158
159     st->theta++;
160
161     return False;
162 }
163
164
165 #define min(a,b) ((a)<(b)?(a):(b))
166
167
168 static void
169 pick_new (struct state *st)
170 {
171   int radius = min (st->xgwa.width, st->xgwa.height) / 2;
172   st->divisor = ((frand (3.0) + 1) * (((random() & 1) * 2) - 1));
173   st->radius1 = radius;
174   st->radius2 = radius / st->divisor + 5;
175   st->distance = 100 + (random() % 200);
176   st->theta = 1;
177 }
178
179
180 static void *
181 xspirograph_init (Display *dpy, Window window)
182 {
183   struct state *st = (struct state *) calloc (1, sizeof(*st));
184   st->dpy = dpy;
185   st->window = window;
186   st->long_delay = get_integer_resource(st->dpy, "delay", "Integer");
187   st->sub_sleep_time = get_integer_resource(st->dpy, "subdelay", "Integer");
188   st->num_layers = get_integer_resource(st->dpy, "layers", "Integer");
189   st->always_finish_p = get_boolean_resource (st->dpy, "alwaysfinish", "Boolean");
190   
191   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
192
193   init_tsg (st);
194   st->theta = 1;
195   st->drawstate = NEW_LAYER;
196
197   return st;
198 }
199
200
201 static void
202 new_colors (struct state *st)
203 {
204   if (mono_p)
205     XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel);
206   else
207     {
208       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
209                   &st->color.red, &st->color.green, &st->color.blue);
210       if ((st->got_color = XAllocColor (st->dpy, st->xgwa.colormap,
211                                         &st->color)))
212         XSetForeground (st->dpy, st->draw_gc, st->color.pixel);
213       else
214         XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel);
215     }
216 }
217
218
219
220 static unsigned long
221 xspirograph_draw (Display *dpy, Window window, void *closure)
222 {
223   struct state *st = (struct state *) closure;
224   Bool free_color = False;
225   Bool flip_p = (st->counter & 1);
226   int i;
227
228   switch (st->drawstate) {
229     case ERASE1:
230       /* 5 sec delay before starting the erase */
231       st->drawstate = ERASE2;
232       /* shouldn't this use the configured long_delay value??? */
233       return (st->long_delay == 0 ? 0 : 5000000);
234
235     case ERASE2:
236       /* erase, delaying 1/50th sec between frames */
237       st->eraser = erase_window(st->dpy, st->window, st->eraser);
238       if (st->eraser)
239         /* shouldn't this be a configured pause??? */
240         return 20000;
241       st->drawstate = NEW_LAYER;
242       /* just finished erasing -- leave screen black for 1 sec */
243       return (st->long_delay == 0 ? 0 : 1000000);
244
245     case DRAW:
246       /* most common case put in front */
247       for (i = 0; i < 1000; i++) {
248         if (go(st, st->radius1, (flip_p ? st->radius2 : -st->radius2),
249                st->distance)) {
250           st->drawstate = NEW_LAYER;
251           break;
252         }
253       }
254       /* Next draw is delayed sleep_time (initialized value)*/
255       return st->sub_sleep_time;
256
257     case NEW_LAYER:
258       /* Increment counter */
259       st->counter++;
260       if (st->counter > (2 * st->num_layers)) {
261         /* reset to zero, free, and erase next time through */
262         st->counter = 0;
263         if (free_color)
264           XFreeColors (st->dpy, st->xgwa.colormap, &st->color.pixel, 1, 0);
265         st->drawstate = ERASE1;
266       } else {
267         /* first, third, fifth, ... time through */
268         if (!flip_p)
269           pick_new (st);
270
271         new_colors (st);
272         st->drawstate = DRAW;
273       }
274       /* No delay to the next draw */
275       return 0;
276
277     default:
278       /* OOPS!! */
279       fprintf(stderr, "%s: invalid state\n", progname);
280       exit(1);
281   }
282
283   return st->sub_sleep_time;
284   /* notreached */
285 }
286
287
288 static void
289 xspirograph_reshape (Display *dpy, Window window, void *closure, 
290                  unsigned int w, unsigned int h)
291 {
292   struct state *st = (struct state *) closure;
293   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
294 }
295
296 static Bool
297 xspirograph_event (Display *dpy, Window window, void *closure, XEvent *event)
298 {
299   return False;
300 }
301
302 static void
303 xspirograph_free (Display *dpy, Window window, void *closure)
304 {
305   struct state *st = (struct state *) closure;
306   free (st);
307 }
308
309
310 static const char *xspirograph_defaults [] = {
311   ".background:         black",
312   ".foreground:         white",
313   "*fpsSolid:           true",
314   "*delay:              5",
315   "*subdelay:           20000",
316   "*layers:             2",
317   "*alwaysfinish:       false",
318   0
319 };
320
321 static XrmOptionDescRec xspirograph_options [] = {   
322   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
323   { "-subdelay",        ".subdelay",            XrmoptionSepArg, 0 },
324   { "-layers",          ".layers",              XrmoptionSepArg, 0 },
325   { "-alwaysfinish",    ".alwaysfinish",        XrmoptionNoArg, "true"},
326   { "-noalwaysfinish",  ".alwaysfinish",        XrmoptionNoArg, "false"},
327   { 0, 0, 0, 0 }
328 };
329
330 XSCREENSAVER_MODULE ("XSpirograph", xspirograph)