4 * Based on a program for some old PDP-11 Graphics Display Processors
9 * Dale Moore <Dale.Moore@cs.cmu.edu>
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
24 #include "screenhack.h"
27 /* If MAXLINES is too big, we might not be able to get it
28 * to the X server in the 2byte length field. Must be less
31 #define MAXLINES (16 * 1024)
32 #define MAXPOINTS MAXLINES
35 * If the pedal has only this many lines, it must be ugly and we dont
51 XColor foreground, background;
60 * Routine (Macro actually)
63 * Assume that degrees is .. oh 360... meaning that
64 * there are 360 degress in a circle. Then this function
65 * would return the sin of the angle in degrees. But lets
66 * say that they are really big degrees, with 4 big degrees
67 * the same as one regular degree. Then this routine
68 * would be called mysin(t, 90) and would return sin(t degrees * 4)
70 #define mysin(t, degrees) sin(t * 2 * M_PI / (degrees))
71 #define mycos(t, degrees) cos(t * 2 * M_PI / (degrees))
77 * Return a random number between a inclusive and b exclusive.
78 * rand (3, 6) returns 3 or 4 or 5, but not 6.
80 #define rand_range(a, b) (a + random() % (b - a))
84 gcd(int m, int n) /* Greatest Common Divisor (also Greates common factor). */
90 if (r == 0) return (n);
96 static int numlines (int a, int b, int d)
100 * Given parameters a and b, how many lines will we have to draw?
104 * This algorithm assumes that r = sin (theta * a), where we
105 * evaluate theta on multiples of b.
107 * LCM (i, j) = i * j / GCD (i, j);
109 * So, at LCM (b, 360) we start over again. But since we
110 * got to LCM (b, 360) by steps of b, the number of lines is
113 * If a is odd, then at 180 we cross over and start the
114 * negative. Someone should write up an elegant way of proving
115 * this. Why? Because I'm not convinced of it myself.
119 #define odd(x) (x & 1)
120 #define even(x) (!odd(x))
121 if ( odd(a) && odd(b) && even(d)) d /= 2;
122 return (d / gcd (d, b));
127 compute_pedal(struct state *st, XPoint *points, int maxpoints)
131 * Basically, it's combination spirograph and string art.
132 * Instead of doing lines, we just use a complex polygon,
133 * and use an even/odd rule for filling in between.
135 * The spirograph, in mathematical terms is a polar
136 * plot of the form r = sin (theta * c);
137 * The string art of this is that we evaluate that
138 * function only on certain multiples of theta. That is
139 * we let theta advance in some random increment. And then
140 * we draw a straight line between those two adjacent points.
142 * Eventually, the lines will start repeating themselves
143 * if we've evaluated theta on some rational portion of the
146 * The number of lines generated is limited to the
147 * ratio of the increment we put on theta to the whole.
148 * If we say that there are 360 degrees in a circle, then we
149 * will never have more than 360 lines.
153 * The number of points.
157 int a, b, d; /* These describe a unique pedal */
161 XPoint *pp = st->points;
165 /* Just to make sure that this division is not done inside the loop */
166 int h_width = st->sizex / 2, h_height = st->sizey / 2 ;
169 d = rand_range (MINLINES, st->maxlines);
171 a = rand_range (1, d);
172 b = rand_range (1, d);
173 numpoints = numlines(a, b, d);
174 if (numpoints > MINLINES) break;
177 /* it might be nice to try to move as much sin and cos computing
178 * (or at least the argument computing) out of the loop.
180 for (count = numpoints; count-- ; )
182 r = mysin (theta * a, d);
184 /* Convert from polar to cartesian coordinates */
185 /* We could round the results, but coercing seems just fine */
186 pp->x = mysin (theta, d) * r * h_width + h_width;
187 pp->y = mycos (theta, d) * r * h_height + h_height;
189 /* Advance index into array */
201 pedal_init (Display *dpy, Window window)
203 struct state *st = (struct state *) calloc (1, sizeof(*st));
205 XWindowAttributes xgwa;
210 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
211 if (st->delay < 0) st->delay = 0;
213 st->maxlines = get_integer_resource (st->dpy, "maxlines", "Integer");
214 if (st->maxlines < MINLINES) st->maxlines = MINLINES;
215 else if (st->maxlines > MAXLINES) st->maxlines = MAXLINES;
217 st->points = (XPoint *)malloc(sizeof(XPoint) * st->maxlines);
219 XGetWindowAttributes (st->dpy, st->window, &xgwa);
220 st->sizex = xgwa.width;
221 st->sizey = xgwa.height;
223 st->cmap = xgwa.colormap;
225 gcv.function = GXcopy;
226 gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
227 gcv.background = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
228 st->gc = XCreateGC (st->dpy, st->window, GCForeground | GCBackground |GCFunction, &gcv);
234 * Since the XFillPolygon doesn't require that the last
235 * point == first point, the number of points is the same
236 * as the number of lines. We just let XFillPolygon supply
237 * the line from the last point to the first point.
241 pedal_draw (Display *dpy, Window window, void *closure)
243 struct state *st = (struct state *) closure;
245 int erase_delay = 10000;
246 int long_delay = 1000000 * st->delay;
248 if (st->erase_p || st->eraser) {
249 st->eraser = erase_window (dpy, window, st->eraser);
251 return (st->eraser ? erase_delay : 1000000);
254 numpoints = compute_pedal(st, st->points, st->maxlines);
256 /* Pick a new foreground color (added by jwz) */
260 hsv_to_rgb (random()%360, 1.0, 1.0,
261 &color.red, &color.green, &color.blue);
262 if (XAllocColor (st->dpy, st->cmap, &color))
264 XSetForeground (st->dpy, st->gc, color.pixel);
265 XFreeColors (st->dpy, st->cmap, &st->foreground.pixel, 1, 0);
266 st->foreground.red = color.red;
267 st->foreground.green = color.green;
268 st->foreground.blue = color.blue;
269 st->foreground.pixel = color.pixel;
273 XFillPolygon (st->dpy, st->window, st->gc, st->points, numpoints,
274 Complex, CoordModeOrigin);
281 pedal_reshape (Display *dpy, Window window, void *closure,
282 unsigned int w, unsigned int h)
284 struct state *st = (struct state *) closure;
290 pedal_event (Display *dpy, Window window, void *closure, XEvent *event)
296 pedal_free (Display *dpy, Window window, void *closure)
298 struct state *st = (struct state *) closure;
306 * If we are trying to save the screen, the background
309 static const char *pedal_defaults [] = {
310 ".background: black",
311 ".foreground: white",
318 static XrmOptionDescRec pedal_options [] = {
319 { "-delay", ".delay", XrmoptionSepArg, 0 },
320 { "-maxlines", ".maxlines", XrmoptionSepArg, 0 },
321 { "-foreground", ".foreground", XrmoptionSepArg, 0 },
322 { "-background", ".background", XrmoptionSepArg, 0 },
326 XSCREENSAVER_MODULE ("Pedal", pedal)