began for maemo
[xscreensaver] / 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   int new_layer;
49   int erasing;
50   eraser_state *eraser;
51 };
52
53
54 static void
55 init_tsg (struct state *st)
56 {
57   XGCValues     gcv;
58   Colormap      cmap;
59
60   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
61   cmap = st->xgwa.colormap;
62   gcv.foreground = st->default_fg_pixel =
63     get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
64   st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
65   gcv.foreground = get_pixel_resource (st->dpy, cmap, "background", "Background");
66 }
67
68
69 static Bool
70 go (struct state *st, int radius1, int radius2, int d)
71 {
72   int width, height;
73   int xmid, ymid;
74   float tmpx, tmpy;
75   int delta;
76
77   width  = st->xgwa.width;
78   height = st->xgwa.height;
79   delta = 1;
80   xmid = width / 2;
81   ymid = height / 2;
82
83   if (st->theta == 1) {
84     st->x1 = xmid + radius1 - radius2 + d;
85     st->y1 = ymid;
86   }
87
88 /*  for (theta = 1; / * theta < ( 360 * 100 ) * /; theta++) */
89                   /* see below about alwaysfinish */
90     {
91         tmpx = xmid + ((       radius1            /* * * * *            */
92                   - radius2        )             /* This algo simulates */
93                   * cos((      st->theta                /* the rotation of a    */
94                   * M_PI           )            /* circular disk inside */
95                   / 180           ))            /* a hollow circular    */
96                   + (              d            /* rim. A point on the  */
97                   * cos((((  radius1            /* disk dist d from the */
98                   * st->theta      )            /* centre, traces the   */
99                   - delta          )            /* path given by this   */
100                   / radius2        )            /* equation.            */
101                   *             M_PI            /* A deviation (error)  */
102                   / 180            )            /* of delta needs to be */
103                                    );           /* given, which greatly */
104                                                 /* adds to the beauty   */
105         tmpy = ymid + (                         /* of the figure.       */
106                      ( radius1 - radius2        /*                      */
107                       ) * sin                   /* Imperfection adds to */
108                        (                        /* beauty, symbolically */
109                         ( st->theta * M_PI      /* ...                  */
110                          ) / 180                /* Algo deduced by      */
111                           )                     /* Rohit Singh, Jan'00  */
112                            ) +                  /* based on a toy he    */
113                             ( d * sin           /* used to play with    */
114                              (                  /* when he was a kid.  */
115                               (                 /*            * * * * */
116                                (                
117                                 ( radius1 * st->theta
118                                  ) - delta
119                                   ) / radius2
120                                    ) * M_PI / 180
121                                     )
122                                      );
123         
124         /*makes integers from the calculated values to do the drawing*/
125         st->x2 = tmpx;
126         st->y2 = tmpy;
127
128         /*stores the first values for later reference*/
129         if(st->theta == 1)
130         {
131                 st->firstx = tmpx;
132                 st->firsty = tmpy;
133         }
134
135         if (st->theta != 1)
136           XDrawLine (st->dpy, st->window, st->draw_gc, 
137                      st->x1, st->y1, st->x2, st->y2);
138
139         st->x1 = st->x2;
140         st->y1 = st->y2;
141
142         /* compares the exact values calculated to the first
143            exact values calculated */
144         /* this will break when nothing new is being drawn */
145         if(tmpx == st->firstx && tmpy == st->firsty && st->theta != 1) {
146           st->firstx = st->firsty = 0;
147           st->theta = 1;
148           return True;
149         }
150
151         /* this will break after 36000 iterations if 
152            the -alwaysfinish option is not specified */
153         if(!st->always_finish_p && st->theta > ( 360 * 100 ) ) {
154           st->firstx = st->firsty = 0;
155           st->theta = 1;
156           return True;
157         }
158     }
159
160     st->theta++;
161
162     return False;
163 }
164
165
166 #define min(a,b) ((a)<(b)?(a):(b))
167
168
169 static void
170 pick_new (struct state *st)
171 {
172   int radius = min (st->xgwa.width, st->xgwa.height) / 2;
173   st->divisor = ((frand (3.0) + 1) * (((random() & 1) * 2) - 1));
174   st->radius1 = radius;
175   st->radius2 = radius / st->divisor + 5;
176   st->distance = 100 + (random() % 200);
177   st->theta = 1;
178 }
179
180
181 static void *
182 xspirograph_init (Display *dpy, Window window)
183 {
184   struct state *st = (struct state *) calloc (1, sizeof(*st));
185   st->dpy = dpy;
186   st->window = window;
187   st->long_delay = get_integer_resource(st->dpy, "delay", "Integer");
188   st->sub_sleep_time = get_integer_resource(st->dpy, "subdelay", "Integer");
189   st->num_layers = get_integer_resource(st->dpy, "layers", "Integer");
190   st->always_finish_p = get_boolean_resource (st->dpy, "alwaysfinish", "Boolean");
191   
192   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
193
194   init_tsg (st);
195   st->theta = 1;
196   st->new_layer = 1;
197
198   return st;
199 }
200
201
202 static void
203 new_colors (struct state *st)
204 {
205   if (mono_p)
206     XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel);
207   else
208     {
209       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
210                   &st->color.red, &st->color.green, &st->color.blue);
211       if ((st->got_color = XAllocColor (st->dpy, st->xgwa.colormap,
212                                         &st->color)))
213         XSetForeground (st->dpy, st->draw_gc, st->color.pixel);
214       else
215         XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel);
216     }
217 }
218
219
220
221 static unsigned long
222 xspirograph_draw (Display *dpy, Window window, void *closure)
223 {
224   struct state *st = (struct state *) closure;
225   Bool free_color = False;
226   Bool flip_p;
227
228   /* 5 sec delay before starting the erase */
229   if (st->erasing == 2) {
230     st->erasing--;
231     return (st->long_delay == 0 ? 0 : 5000000);
232   }
233
234   /* erase, delaying 1/50th sec between frames */
235   if (st->erasing || st->eraser) {
236     st->erasing = 0;
237     st->eraser = erase_window (st->dpy, st->window, st->eraser);
238     if (st->eraser)
239       return 20000;
240     else
241       /* just finished erasing -- leave screen black for 1 sec */
242       return (st->long_delay == 0 ? 0 : 1000000);
243   }
244
245   flip_p = (st->counter & 1);
246
247   if (st->new_layer) {
248
249     st->new_layer = 0;
250     st->counter++;
251
252     if (st->counter > (2 * st->num_layers))
253       {
254         st->counter = 0;
255
256         if (free_color)
257           XFreeColors (st->dpy, st->xgwa.colormap, &st->color.pixel, 1, 0);
258
259         st->erasing = 2;
260       }
261
262     if (! flip_p)
263       pick_new (st);
264
265     new_colors (st);
266     st->new_layer = 0;
267   }
268
269   {
270     int i;
271     for (i = 0; i < 1000; i++) {
272       if (go (st, st->radius1, 
273               (flip_p ? st->radius2 : -st->radius2),
274               st->distance)) {
275         st->new_layer = 1;
276         break;
277       }
278     }
279   }
280
281   return st->sub_sleep_time;
282 }
283
284
285 static void
286 xspirograph_reshape (Display *dpy, Window window, void *closure, 
287                  unsigned int w, unsigned int h)
288 {
289   struct state *st = (struct state *) closure;
290   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
291 }
292
293 static Bool
294 xspirograph_event (Display *dpy, Window window, void *closure, XEvent *event)
295 {
296   return False;
297 }
298
299 static void
300 xspirograph_free (Display *dpy, Window window, void *closure)
301 {
302   struct state *st = (struct state *) closure;
303   free (st);
304 }
305
306
307 static const char *xspirograph_defaults [] = {
308   ".background:         black",
309   ".foreground:         white",
310   "*delay:              5",
311   "*subdelay:           20000",
312   "*layers:             2",
313   "*alwaysfinish:       false",
314   0
315 };
316
317 static XrmOptionDescRec xspirograph_options [] = {   
318   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
319   { "-subdelay",        ".subdelay",            XrmoptionSepArg, 0 },
320   { "-layers",          ".layers",              XrmoptionSepArg, 0 },
321   { "-alwaysfinish",    ".alwaysfinish",        XrmoptionNoArg, "true"},
322   { "-noalwaysfinish",  ".alwaysfinish",        XrmoptionNoArg, "false"},
323   { 0, 0, 0, 0 }
324 };
325
326 XSCREENSAVER_MODULE ("XSpiroGraph", xspirograph)