summaryrefslogtreecommitdiffstats
path: root/hacks/xspirograph.c
blob: a67d8b2031870e5e6491fd5ac0e2d816061f240f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* The Spiral Generator, Copyright (c) 2000 
 * by Rohit Singh <rohit_singh@hotmail.com>
 * 
 * Contains code from / To be used with:
 * xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
 * Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notices appear in all copies and that both that
 * copyright notices and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 *
 * Modified (Dec 2001) by Matthew Strait <straitm@mathcs.carleton.edu>
 * Added -subdelay and -alwaysfinish
 * Prevented redrawing over existing lines
 */

#include <math.h>
#include "screenhack.h"
#include "erase.h"

struct state {
  Display *dpy;
  Window window;
  XWindowAttributes xgwa;

  GC 	draw_gc;
  int 	long_delay;
  int 	sub_sleep_time;
  int 	num_layers;
  unsigned int default_fg_pixel;
  Bool	always_finish_p;
  XColor	color;
  int got_color;

  int theta;
  float firstx, firsty;
  int x1, y1, x2, y2;

  int counter;
  int distance;
  int radius1, radius2;
  double divisor;

  enum curstate { NEW_LAYER, DRAW, ERASE1, ERASE2 } drawstate;
  eraser_state *eraser;
};


static void
init_tsg (struct state *st)
{
  XGCValues	gcv;
  Colormap	cmap;

  XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
  cmap = st->xgwa.colormap;
  gcv.foreground = st->default_fg_pixel =
    get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
  st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
  gcv.foreground = get_pixel_resource (st->dpy, cmap, "background", "Background");
}


static Bool
go (struct state *st, int radius1, int radius2, int d)
{
  int width, height;
  int xmid, ymid;
  float tmpx, tmpy;
  int delta;

  width  = st->xgwa.width;
  height = st->xgwa.height;
  delta = 1;
  xmid = width / 2;
  ymid = height / 2;

  if (st->theta == 1) {
    st->x1 = xmid + radius1 - radius2 + d;
    st->y1 = ymid;
  }

/*  for (theta = 1; / * theta < ( 360 * 100 ) * /; theta++) */
                  /* see below about alwaysfinish */
    {
	tmpx = xmid + ((       radius1	          /* * * * *            */
                  - radius2        )		 /* This algo simulates	*/
                  * cos((      st->theta 		/* the rotation of a    */
                  * M_PI           ) 		/* circular disk inside */
                  / 180           )) 		/* a hollow circular 	*/
                  + (              d 		/* rim. A point on the  */
                  * cos((((  radius1 		/* disk dist d from the	*/
                  * st->theta      )		/* centre, traces the 	*/
                  - delta          )		/* path given by this   */
                  / radius2        )  		/* equation.	        */
                  *             M_PI		/* A deviation (error)  */
                  / 180            )            /* of delta needs to be */
                                   );           /* given, which greatly */
						/* adds to the beauty   */
	tmpy = ymid + (				/* of the figure.       */
                     ( radius1 - radius2	/*			*/
                      ) * sin			/* Imperfection adds to */
                       (			/* beauty, symbolically */
                        ( st->theta * M_PI     	/* ...			*/
                         ) / 180		/* Algo deduced by      */
                          )			/* Rohit Singh, Jan'00  */
                           ) +                  /* based on a toy he    */
                            ( d * sin           /* used to play with    */
                             (                  /* when he was a kid.  */
                              (                 /*            * * * * */
                               (          	
                                ( radius1 * st->theta
                                 ) - delta
                                  ) / radius2
                                   ) * M_PI / 180
                                    )
                                     );
        
	/*makes integers from the calculated values to do the drawing*/
	st->x2 = tmpx;
	st->y2 = tmpy;

	/*stores the first values for later reference*/
	if(st->theta == 1)
	{
  		st->firstx = tmpx;
  		st->firsty = tmpy;
	}

        if (st->theta != 1)
          XDrawLine (st->dpy, st->window, st->draw_gc, 
                     st->x1, st->y1, st->x2, st->y2);

	st->x1 = st->x2;
	st->y1 = st->y2;

	/* compares the exact values calculated to the first
	   exact values calculated */
	/* this will break when nothing new is being drawn */
	if(tmpx == st->firstx && tmpy == st->firsty && st->theta != 1) {
          st->firstx = st->firsty = 0;
          st->theta = 1;
          return True;
        }

	/* this will break after 36000 iterations if 
	   the -alwaysfinish option is not specified */
	if(!st->always_finish_p && st->theta > ( 360 * 100 ) ) {
          st->firstx = st->firsty = 0;
          st->theta = 1;
          return True;
        }
    }

    st->theta++;

    return False;
}


#define min(a,b) ((a)<(b)?(a):(b))


static void
pick_new (struct state *st)
{
  int radius = min (st->xgwa.width, st->xgwa.height) / 2;
  st->divisor = ((frand (3.0) + 1) * (((random() & 1) * 2) - 1));
  st->radius1 = radius;
  st->radius2 = radius / st->divisor + 5;
  st->distance = 100 + (random() % 200);
  st->theta = 1;
}


static void *
xspirograph_init (Display *dpy, Window window)
{
  struct state *st = (struct state *) calloc (1, sizeof(*st));
  st->dpy = dpy;
  st->window = window;
  st->long_delay = get_integer_resource(st->dpy, "delay", "Integer");
  st->sub_sleep_time = get_integer_resource(st->dpy, "subdelay", "Integer");
  st->num_layers = get_integer_resource(st->dpy, "layers", "Integer");
  st->always_finish_p = get_boolean_resource (st->dpy, "alwaysfinish", "Boolean");
  
  XGetWindowAttributes (st->dpy, st->window, &st->xgwa);

  init_tsg (st);
  st->theta = 1;
  st->drawstate = NEW_LAYER;

  return st;
}


static void
new_colors (struct state *st)
{
  if (mono_p)
    XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel);
  else
    {
      hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
		  &st->color.red, &st->color.green, &st->color.blue);
      if ((st->got_color = XAllocColor (st->dpy, st->xgwa.colormap,
                                        &st->color)))
	XSetForeground (st->dpy, st->draw_gc, st->color.pixel);
      else
	XSetForeground (st->dpy, st->draw_gc, st->default_fg_pixel);
    }
}



static unsigned long
xspirograph_draw (Display *dpy, Window window, void *closure)
{
  struct state *st = (struct state *) closure;
  Bool free_color = False;
  Bool flip_p = (st->counter & 1);
  int i;

  switch (st->drawstate) {
    case ERASE1:
      /* 5 sec delay before starting the erase */
      st->drawstate = ERASE2;
      /* shouldn't this use the configured long_delay value??? */
      return (st->long_delay == 0 ? 0 : 5000000);

    case ERASE2:
      /* erase, delaying 1/50th sec between frames */
      st->eraser = erase_window(st->dpy, st->window, st->eraser);
      if (st->eraser)
	/* shouldn't this be a configured pause??? */
	return 20000;
      st->drawstate = NEW_LAYER;
      /* just finished erasing -- leave screen black for 1 sec */
      return (st->long_delay == 0 ? 0 : 1000000);

    case DRAW:
      /* most common case put in front */
      for (i = 0; i < 1000; i++) {
        if (go(st, st->radius1, (flip_p ? st->radius2 : -st->radius2),
	       st->distance)) {
	  st->drawstate = NEW_LAYER;
	  break;
	}
      }
      /* Next draw is delayed sleep_time (initialized value)*/
      return st->sub_sleep_time;

    case NEW_LAYER:
      /* Increment counter */
      st->counter++;
      if (st->counter > (2 * st->num_layers)) {
	/* reset to zero, free, and erase next time through */
	st->counter = 0;
	if (free_color)
	  XFreeColors (st->dpy, st->xgwa.colormap, &st->color.pixel, 1, 0);
	st->drawstate = ERASE1;
      } else {
	/* first, third, fifth, ... time through */
	if (!flip_p)
	  pick_new (st);

	new_colors (st);
	st->drawstate = DRAW;
      }
      /* No delay to the next draw */
      return 0;

    default:
      /* OOPS!! */
      fprintf(stderr, "%s: invalid state\n", progname);
      exit(1);
  }

  return st->sub_sleep_time;
  /* notreached */
}


static void
xspirograph_reshape (Display *dpy, Window window, void *closure, 
                 unsigned int w, unsigned int h)
{
  struct state *st = (struct state *) closure;
  XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
}

static Bool
xspirograph_event (Display *dpy, Window window, void *closure, XEvent *event)
{
  return False;
}

static void
xspirograph_free (Display *dpy, Window window, void *closure)
{
  struct state *st = (struct state *) closure;
  XFreeGC (dpy, st->draw_gc);
  free (st);
}


static const char *xspirograph_defaults [] = {
  ".background:		black",
  ".foreground:		white",
  "*fpsSolid:		true",
  "*delay:      	5",
  "*subdelay:   	20000",
  "*layers:     	2",
  "*alwaysfinish:	false",
#ifdef HAVE_MOBILE
  "*ignoreRotation:     True",
#endif
  0
};

static XrmOptionDescRec xspirograph_options [] = {   
  { "-delay",           ".delay",               XrmoptionSepArg, 0 },
  { "-subdelay",        ".subdelay",            XrmoptionSepArg, 0 },
  { "-layers",          ".layers",        	XrmoptionSepArg, 0 },
  { "-alwaysfinish",    ".alwaysfinish",	XrmoptionNoArg, "true"},
  { "-noalwaysfinish",  ".alwaysfinish",	XrmoptionNoArg, "false"},
  { 0, 0, 0, 0 }
};

XSCREENSAVER_MODULE ("XSpirograph", xspirograph)