source: trunk/third/xscreensaver/hacks/pedal.c @ 12203

Revision 12203, 9.9 KB checked in by ghudson, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12202, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * pedal
3 *
4 * Based on a program for some old PDP-11 Graphics Display Processors
5 * at CMU.
6 *
7 * X version by
8 *
9 *  Dale Moore  <Dale.Moore@cs.cmu.edu>
10 *  24-Jun-1994
11 *
12 *  Copyright (c) 1994, by Carnegie Mellon University.  Permission to use,
13 *  copy, modify, distribute, and sell this software and its documentation
14 *  for any purpose is hereby granted without fee, provided fnord that the
15 *  above copyright notice appear in all copies and that both that copyright
16 *  notice and this permission notice appear in supporting documentation.
17 *  No representations are made about the  suitability of fnord this software
18 *  for any purpose.  It is provided "as is" without express or implied
19 *  warranty.
20 */
21
22#include <math.h>
23#include <stdlib.h>
24#include "screenhack.h"
25
26/* If MAXLINES is too big, we might not be able to get it
27 * to the X server in the 2byte length field. Must be less
28 * than 16k
29 */
30#define MAXLINES (16 * 1024)
31#define MAXPOINTS MAXLINES
32XPoint *points;
33
34/*
35 * If the pedal has only this many lines, it must be ugly and we dont
36 * want to see it.
37 */
38#define MINLINES 7
39
40static int sizex, sizey;
41static int delay;
42static int fadedelay;
43static int maxlines;
44static GC gc;
45static XColor foreground, background;
46static Colormap cmap;
47
48static Bool fade_p;
49
50
51/*
52 * Routine (Macro actually)
53 *   mysin
54 * Description:
55 *   Assume that degrees is .. oh 360... meaning that
56 *   there are 360 degress in a circle.  Then this function
57 *   would return the sin of the angle in degrees.  But lets
58 *   say that they are really big degrees, with 4 big degrees
59 *   the same as one regular degree.  Then this routine
60 *   would be called mysin(t, 90) and would return sin(t degrees * 4)
61 */
62#define mysin(t, degrees) sin(t * 2 * M_PI / (degrees))
63#define mycos(t, degrees) cos(t * 2 * M_PI / (degrees))
64
65/*
66 * Macro:
67 *   rand_range
68 * Description:
69 *   Return a random number between a inclusive  and b exclusive.
70 *    rand (3, 6) returns 3 or 4 or 5, but not 6.
71 */
72#define rand_range(a, b) (a + random() % (b - a))
73
74
75static int
76gcd(int m, int n) /* Greatest Common Divisor (also Greates common factor). */
77{
78    int r;
79
80    for (;;) {
81        r = m % n;
82        if (r == 0) return (n);
83        m = n;
84        n = r;
85    }
86}
87
88static int numlines (int a, int b, int d)
89/*
90 * Description:
91 *
92 *      Given parameters a and b, how many lines will we have to draw?
93 *
94 * Algorithm:
95 *
96 *      This algorithm assumes that r = sin (theta * a), where we
97 *      evaluate theta on multiples of b.
98 *
99 *      LCM (i, j) = i * j / GCD (i, j);
100 *
101 *      So, at LCM (b, 360) we start over again.  But since we
102 *      got to LCM (b, 360) by steps of b, the number of lines is
103 *      LCM (b, 360) / b.
104 *
105 *      If a is odd, then at 180 we cross over and start the
106 *      negative.  Someone should write up an elegant way of proving
107 *      this.  Why?  Because I'm not convinced of it myself.
108 *
109 */
110{
111#define odd(x) (x & 1)
112#define even(x) (!odd(x))
113    if ( odd(a) && odd(b) && even(d)) d /= 2;
114    return  (d / gcd (d, b));
115#undef odd
116}
117
118static int
119compute_pedal(XPoint *points, int maxpoints)
120/*
121 * Description:
122 *
123 *    Basically, it's combination spirograph and string art.
124 *    Instead of doing lines, we just use a complex polygon,
125 *    and use an even/odd rule for filling in between.
126 *
127 *    The spirograph, in mathematical terms is a polar
128 *    plot of the form r = sin (theta * c);
129 *    The string art of this is that we evaluate that
130 *    function only on certain multiples of theta.  That is
131 *    we let theta advance in some random increment.  And then
132 *    we draw a straight line between those two adjacent points.
133 *
134 *    Eventually, the lines will start repeating themselves
135 *    if we've evaluated theta on some rational portion of the
136 *    whole.
137 *
138 *    The number of lines generated is limited to the
139 *    ratio of the increment we put on theta to the whole.
140 *    If we say that there are 360 degrees in a circle, then we
141 *    will never have more than 360 lines.   
142 *
143 * Return:
144 *
145 *    The number of points.
146 *
147 */
148{
149    int a, b, d;  /* These describe a unique pedal */
150
151    double r;
152    int theta = 0;
153    XPoint *pp = points;
154    int count;
155    int numpoints;
156
157    /* Just to make sure that this division is not done inside the loop */
158    int h_width = sizex / 2, h_height = sizey / 2 ;
159
160    for (;;) {
161        d = rand_range (MINLINES, maxlines);
162
163        a = rand_range (1, d);
164        b = rand_range (1, d);
165        numpoints = numlines(a, b, d);
166        if (numpoints > MINLINES) break;
167    }
168
169    /* it might be nice to try to move as much sin and cos computing
170     * (or at least the argument computing) out of the loop.
171     */
172    for (count = numpoints; count-- ; )
173    {
174        r = mysin (theta * a, d);
175
176        /* Convert from polar to cartesian coordinates */
177        /* We could round the results, but coercing seems just fine */
178        pp->x = mysin (theta, d) * r * h_width  + h_width;
179        pp->y = mycos (theta, d) * r * h_height + h_height;
180
181        /* Advance index into array */
182        pp++;
183
184        /* Advance theta */
185        theta += b;
186        theta %= d;
187    }
188
189    return(numpoints);
190}
191
192static void
193init_pedal (Display *dpy, Window window)
194{
195  XGCValues gcv;
196  XWindowAttributes xgwa;
197
198  fade_p = !mono_p;
199
200  delay = get_integer_resource ("delay", "Integer");
201  if (delay < 0) delay = 0;
202
203  fadedelay = get_integer_resource ("fadedelay", "Integer");
204  if (fadedelay < 0) fadedelay = 0;
205
206  maxlines = get_integer_resource ("maxlines", "Integer");
207  if (maxlines < MINLINES) maxlines = MINLINES;
208  else if (maxlines > MAXLINES) maxlines = MAXLINES;
209
210  points = (XPoint *)malloc(sizeof(XPoint) * maxlines);
211
212  XGetWindowAttributes (dpy, window, &xgwa);
213  sizex = xgwa.width;
214  sizey = xgwa.height;
215
216  if ((xgwa.visual->class != GrayScale) && (xgwa.visual->class != PseudoColor))
217    fade_p = False;
218
219  cmap = xgwa.colormap;
220
221  gcv.function = GXcopy;
222  gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
223  gcv.background = get_pixel_resource ("background", "Background", dpy, cmap);
224  gc = XCreateGC (dpy, window, GCForeground | GCBackground |GCFunction, &gcv);
225
226  if (fade_p)
227  {
228      int status;
229      foreground.pixel = gcv.foreground;
230      XQueryColor (dpy, cmap, &foreground);
231
232      status = XAllocColorCells (
233                        dpy,
234                        cmap,
235                        0,
236                        NULL,
237                        0,
238                        &foreground.pixel,
239                        1);
240      if (status)
241      {
242          XStoreColor ( dpy, cmap, &foreground);
243          XSetForeground (dpy, gc, foreground.pixel);
244
245          background.pixel = gcv.background;
246          XQueryColor (dpy, cmap, &background);
247      }
248      else
249      {
250          /* If we cant allocate a color cell, then just forget the
251           * whole fade business.
252           */
253          fade_p = False;
254      }
255  }
256}
257
258static void
259fade_foreground (Display *dpy, Colormap cmap,
260                 XColor from, XColor to, int steps)
261/*
262 * This routine assumes that we have a writeable colormap.
263 * That means that the default colormap is not full, and that
264 * the visual class is PseudoColor or GrayScale.
265 */
266{
267    int i;
268    XColor inbetween;
269    int udelay = fadedelay / (steps + 1);
270
271    inbetween = foreground;
272    for (i = 0; i <= steps; i++ )
273    {
274      inbetween.red   = from.red   + (to.red   - from.red)   * i / steps ;
275      inbetween.green = from.green + (to.green - from.green) * i / steps ;
276      inbetween.blue  = from.blue  + (to.blue  - from.blue)  * i / steps ;
277      XStoreColor (dpy, cmap, &inbetween);
278      /* If we don't sync, these can bunch up */
279      XSync(dpy, False);
280      screenhack_handle_events (dpy);
281      usleep(udelay);
282    }
283}
284
285static void
286pedal (Display *dpy, Window window)
287/*
288 *    Since the XFillPolygon doesn't require that the last
289 *    point == first point, the number of points is the same
290 *    as the number of lines.  We just let XFillPolygon supply
291 *    the line from the last point to the first point.
292 *
293 */
294{
295   int numpoints;
296
297   numpoints = compute_pedal(points, maxlines);
298
299   /* Fade out, make foreground the same as background */
300   if (fade_p)
301     fade_foreground (dpy, cmap, foreground, background, 32);
302
303    /* Clear the window of previous garbage */
304    XClearWindow (dpy, window);
305
306    XFillPolygon (
307                dpy,
308                window,
309                gc,
310                points,
311                numpoints,
312                Complex,
313                CoordModeOrigin);
314
315   /* Pick a new foreground color (added by jwz) */
316   if (! mono_p)
317     {
318       XColor color;
319       hsv_to_rgb (random()%360, 1.0, 1.0,
320                   &color.red, &color.green, &color.blue);
321       XSync(dpy, False);
322       if (fade_p)
323         {
324           foreground.red = color.red;
325           foreground.green = color.green;
326           foreground.blue = color.blue;
327           /* don't do this here -- let fade_foreground() bring it up! */
328           /* XStoreColor (dpy, cmap, &foreground); */
329         }
330       else if (XAllocColor (dpy, cmap, &color))
331         {
332           XSetForeground (dpy, gc, color.pixel);
333           XFreeColors (dpy, cmap, &foreground.pixel, 1, 0);
334           foreground.red = color.red;
335           foreground.green = color.green;
336           foreground.blue = color.blue;
337           foreground.pixel = color.pixel;
338         }
339       XSync(dpy, False);
340     }
341
342    /* Fade in by bringing the foreground back from background */
343    if (fade_p)
344       fade_foreground (dpy, cmap, background, foreground, 32);
345}
346
347
348char *progclass = "Pedal";
349
350/*
351 * If we are trying to save the screen, the background
352 * should be dark.
353 */
354char *defaults [] = {
355  ".background:                 black",
356  ".foreground:                 white",
357  "*delay:                      5",
358  "*fadedelay:                  200000",
359  "*maxlines:                   1000",
360  0
361};
362
363XrmOptionDescRec options [] = {
364  { "-delay",           ".delay",               XrmoptionSepArg, 0 },
365  { "-fadedelay",       ".fadedelay",           XrmoptionSepArg, 0 },
366  { "-maxlines",        ".maxlines",            XrmoptionSepArg, 0 },
367  { "-foreground",      ".foreground",          XrmoptionSepArg, 0 },
368  { "-background",      ".background",          XrmoptionSepArg, 0 },
369  { 0, 0, 0, 0 }
370};
371
372void
373screenhack (Display *dpy, Window window)
374{
375    init_pedal (dpy, window);
376    for (;;) {
377        pedal (dpy, window);
378        XSync(dpy, False);
379        screenhack_handle_events (dpy);
380        if (delay) sleep (delay);
381    }
382}
Note: See TracBrowser for help on using the repository browser.