4 * Based on a program for some old PDP-11 Graphics Display Processors
9 * Dale Moore <Dale.Moore@cs.cmu.edu>
12 * Copyright \(co 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"
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
30 #define MAXLINES (16 * 1024)
31 #define MAXPOINTS MAXLINES
35 * If the pedal has only this many lines, it must be ugly and we dont
40 static int sizex, sizey;
45 static XColor foreground, background;
52 * Routine (Macro actually)
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)
62 #define mysin(t, degrees) sin(t * 2 * M_PI / (degrees))
63 #define mycos(t, degrees) cos(t * 2 * M_PI / (degrees))
69 * Return a random number between a inclusive and b exclusive.
70 * rand (3, 6) returns 3 or 4 or 5, but not 6.
72 #define rand_range(a, b) (a + random() % (b - a))
79 * Greatest Common Divisor (also Greates common factor).
86 if (r == 0) return (n);
92 static int numlines (a, b, d)
99 * Given parameters a and b, how many lines will we have to draw?
103 * This algorithm assumes that r = sin (theta * a), where we
104 * evaluate theta on multiples of b.
106 * LCM (i, j) = i * j / GCD (i, j);
108 * So, at LCM (b, 360) we start over again. But since we
109 * got to LCM (b, 360) by steps of b, the number of lines is
112 * If a is odd, then at 180 we cross over and start the
113 * negative. Someone should write up an elegant way of proving
114 * this. Why? Because I'm not convinced of it myself.
118 #define odd(x) (x & 1)
119 #define even(x) (!odd(x))
120 if ( odd(a) && odd(b) && even(d)) d /= 2;
121 return (d / gcd (d, b));
126 compute_pedal(points, maxpoints)
132 * Basically, it's combination spirograph and string art.
133 * Instead of doing lines, we just use a complex polygon,
134 * and use an even/odd rule for filling in between.
136 * The spirograph, in mathematical terms is a polar
137 * plot of the form r = sin (theta * c);
138 * The string art of this is that we evaluate that
139 * function only on certain multiples of theta. That is
140 * we let theta advance in some random increment. And then
141 * we draw a straight line between those two adjacent points.
143 * Eventually, the lines will start repeating themselves
144 * if we've evaluated theta on some rational portion of the
147 * The number of lines generated is limited to the
148 * ratio of the increment we put on theta to the whole.
149 * If we say that there are 360 degrees in a circle, then we
150 * will never have more than 360 lines.
154 * The number of points.
158 int a, b, d; /* These describe a unique pedal */
166 /* Just to make sure that this division is not done inside the loop */
167 int h_width = sizex / 2, h_height = sizey / 2 ;
170 d = rand_range (MINLINES, maxlines);
172 a = rand_range (1, d);
173 b = rand_range (1, d);
174 numpoints = numlines(a, b, d);
175 if (numpoints > MINLINES) break;
178 /* it might be nice to try to move as much sin and cos computing
179 * (or at least the argument computing) out of the loop.
181 for (count = numpoints; count-- ; )
183 r = mysin (theta * a, d);
185 /* Convert from polar to cartesian coordinates */
186 /* We could round the results, but coercing seems just fine */
187 pp->x = mysin (theta, d) * r * h_width + h_width;
188 pp->y = mycos (theta, d) * r * h_height + h_height;
190 /* Advance index into array */
202 init_pedal (dpy, window)
207 XWindowAttributes xgwa;
211 delay = get_integer_resource ("delay", "Integer");
212 if (delay < 0) delay = 0;
214 fadedelay = get_integer_resource ("fadedelay", "Integer");
215 if (fadedelay < 0) fadedelay = 0;
217 maxlines = get_integer_resource ("maxlines", "Integer");
218 if (maxlines < MINLINES) maxlines = MINLINES;
219 else if (maxlines > MAXLINES) maxlines = MAXLINES;
221 points = (XPoint *)malloc(sizeof(XPoint) * maxlines);
223 XGetWindowAttributes (dpy, window, &xgwa);
227 if ((xgwa.visual->class != GrayScale) && (xgwa.visual->class != PseudoColor))
230 cmap = xgwa.colormap;
232 gcv.function = GXcopy;
233 gcv.subwindow_mode = IncludeInferiors;
234 gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
235 gcv.background = get_pixel_resource ("background", "Background", dpy, cmap);
239 GCForeground | GCBackground |GCFunction | GCSubwindowMode ,
245 foreground.pixel = gcv.foreground;
246 XQueryColor (dpy, cmap, &foreground);
248 status = XAllocColorCells (
258 XStoreColor ( dpy, cmap, &foreground);
259 XSetForeground (dpy, gc, foreground.pixel);
261 background.pixel = gcv.background;
262 XQueryColor (dpy, cmap, &background);
266 /* If we cant allocate a color cell, then just forget the
267 * whole fade business.
275 fade_foreground (dpy, cmap, from, to, steps)
282 * This routine assumes that we have a writeable colormap.
283 * That means that the default colormap is not full, and that
284 * the visual class is PseudoColor or GrayScale.
289 int udelay = fadedelay / (steps + 1);
291 inbetween = foreground;
292 for (i = 0; i <= steps; i++ )
294 inbetween.red = from.red + (to.red - from.red) * i / steps ;
295 inbetween.green = from.green + (to.green - from.green) * i / steps ;
296 inbetween.blue = from.blue + (to.blue - from.blue) * i / steps ;
297 XStoreColor (dpy, cmap, &inbetween);
298 /* If we don't sync, these can bunch up */
309 * Since the XFillPolygon doesn't require that the last
310 * point == first point, the number of points is the same
311 * as the number of lines. We just let XFillPolygon supply
312 * the line from the last point to the first point.
318 numpoints = compute_pedal(points, maxlines);
320 /* Fade out, make foreground the same as background */
322 fade_foreground (dpy, cmap, foreground, background, 32);
324 /* Clear the window of previous garbage */
325 XClearWindow (dpy, window);
336 /* Pick a new foreground color (added by jwz) */
340 hsv_to_rgb (random()%360, 1.0, 1.0,
341 &color.red, &color.green, &color.blue);
345 foreground.red = color.red;
346 foreground.green = color.green;
347 foreground.blue = color.blue;
348 XStoreColor (dpy, cmap, &foreground);
350 else if (XAllocColor (dpy, cmap, &color))
352 XSetForeground (dpy, gc, color.pixel);
353 XFreeColors (dpy, cmap, &foreground.pixel, 1, 0);
354 foreground.red = color.red;
355 foreground.green = color.green;
356 foreground.blue = color.blue;
357 foreground.pixel = color.pixel;
362 /* Fade in by bringing the foreground back from background */
364 fade_foreground (dpy, cmap, background, foreground, 32);
368 char *progclass = "Pedal";
371 * If we are trying to save the screen, the background
374 char *defaults [] = {
376 "*fadedelay: 200000",
378 "*foreground: white",
379 "*background: black",
383 XrmOptionDescRec options [] = {
384 { "-delay", ".delay", XrmoptionSepArg, 0 },
385 { "-fadedelay", ".fadedelay", XrmoptionSepArg, 0 },
386 { "-maxlines", ".maxlines", XrmoptionSepArg, 0 },
387 { "-foreground", ".foreground", XrmoptionSepArg, 0 },
388 { "-background", ".background", XrmoptionSepArg, 0 },
391 int options_size = (sizeof (options) / sizeof (options[0]));
394 screenhack (dpy, window)
398 init_pedal (dpy, window);
402 if (delay) sleep (delay);