1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* petal --- mathematical flowers */
3
4 #if 0
5 static const char sccsid[] = "@(#)petal.c 5.00 2000/11/01 xlockmore";
6
7 #endif
8
9 /*-
10 * Permission to use, copy, modify, and distribute this software and its
11 * documentation for any purpose and without fee is hereby granted,
12 * provided that the above copyright notice appear in all copies and that
13 * both that copyright notice and this permission notice appear in
14 * supporting documentation.
15 *
16 * This file is provided AS IS with no warranties of any kind. The author
17 * shall have no liability with respect to the infringement of copyrights,
18 * trade secrets or any patents by this file or any part thereof. In no
19 * event will the author be liable for any lost revenue or profits or
20 * other special, indirect and consequential damages.
21 *
22 * Revision History:
23 * 01-Nov-2000: Allocation checks
24 * 10-May-1997: Compatible with xscreensaver
25 * 12-Aug-1995: xlock version
26 * Jan-1995: xscreensaver version (Jamie Zawinski <jwz AT jwz.org>)
27 * 24-Jun-1994: X11 version (Dale Moore <Dale.Moore@cs.cmu.edu>)
28 * Based on a program for some old PDP-11 Graphics
29 * Display Processors at CMU.
30 */
31
32 /*-
33 * original copyright
34 * Copyright \(co 1994, by Carnegie Mellon University. Permission to use,
35 * copy, modify, distribute, and sell this software and its documentation
36 * for any purpose is hereby granted without fee, provided fnord that the
37 * above copyright notice appear in all copies and that both that copyright
38 * notice and this permission notice appear in supporting documentation.
39 * No representations are made about the suitability of fnord this software
40 * for any purpose. It is provided "as is" without express or implied
41 * warranty.
42 */
43
44 #ifdef STANDALONE
45 #define MODE_petal
46 #define DEFAULTS "*delay: 10000 \n" \
47 "*count: -500 \n" \
48 "*cycles: 400 \n" \
49 "*ncolors: 64 \n" \
50 "*wireframe: False \n" \
51 "*fullrandom: False \n" \
52
53 # define free_petal 0
54 # define reshape_petal 0
55 # define petal_handle_event 0
56 #include "xlockmore.h" /* in xscreensaver distribution */
57 #else /* STANDALONE */
58 #include "xlock.h" /* in xlockmore distribution */
59 #endif /* STANDALONE */
60
61 #ifdef MODE_petal
62
63 ENTRYPOINT ModeSpecOpt petal_opts =
64 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
65
66 #ifdef USE_MODULES
67 ModStruct petal_description =
68 {"petal", "init_petal", "draw_petal", "release_petal",
69 "refresh_petal", "init_petal", (char *) NULL, &petal_opts,
70 10000, -500, 400, 1, 64, 1.0, "",
71 "Shows various GCD Flowers", 0, NULL};
72
73 #endif
74
75 #define TWOPI (2.0*M_PI)
76
77 /*-
78 * If MAXLINES is too big, we might not be able to get it
79 * to the X server in the 2byte length field.
80 * Must be less * than 16k
81 */
82 #define MAXLINES (16*1024)
83
84 /*-
85 * If the petal has only this many lines, it must be ugly and we do not
86 * want to see it.
87 */
88 #define MINLINES 5
89
90 typedef struct {
91 Bool painted;
92 int width, height;
93 int lines;
94 int time;
95 int npoints;
96 long color;
97 XPoint *points;
98 Bool wireframe;
99 } petalstruct;
100
101 static petalstruct *petals = (petalstruct *) NULL;
102
103 /*-
104 * Macro:
105 * sine
106 * cosine
107 * Description:
108 * Assume that degrees is .. oh 360... meaning that
109 * there are 360 degrees in a circle. Then this function
110 * would return the sin of the angle in degrees. But lets
111 * say that they are really big degrees, with 4 big degrees
112 * the same as one regular degree. Then this routine
113 * would be called mysin(t, 90) and would return sin(t degrees * 4)
114 */
115 #define sine(t, degrees) sin(t * TWOPI / (degrees))
116 #define cosine(t, degrees) cos(t * TWOPI / (degrees))
117
118 /*-
119 * Macro:
120 * rand_range
121 * Description:
122 * Return a random number between a inclusive and b exclusive.
123 * rand (3, 6) returns 3 or 4 or 5, but not 6.
124 */
125 #define rand_range(a, b) (a + NRAND(b - a))
126
127 static int
gcd(int m,int n)128 gcd(int m, int n)
129 /*-
130 * Greatest Common Divisor (also Greatest common factor).
131 */
132 {
133 int r;
134
135 for (;;) {
136 r = m % n;
137 if (r == 0)
138 return (n);
139 m = n;
140 n = r;
141 }
142 }
143
144 static int
numlines(int a,int b,int d)145 numlines(int a, int b, int d)
146 /*-
147 * Description:
148 *
149 * Given parameters a and b, how many lines will we have to draw?
150 *
151 * Algorithm:
152 *
153 * This algorithm assumes that r = sin (theta * a), where we
154 * evaluate theta on multiples of b.
155 *
156 * LCM (i, j) = i * j / GCD (i, j);
157 *
158 * So, at LCM (b, 360) we start over again. But since we
159 * got to LCM (b, 360) by steps of b, the number of lines is
160 * LCM (b, 360) / b.
161 *
162 * If a is odd, then at 180 we cross over and start the
163 * negative. Someone should write up an elegant way of proving
164 * this. Why? Because I'm not convinced of it myself.
165 *
166 */
167 {
168 #define odd(x) (x & 1)
169 #define even(x) (!odd(x))
170 if (odd(a) && odd(b) && even(d))
171 d /= 2;
172 return (d / gcd(d, b));
173 #undef odd
174 }
175
176 static int
compute_petal(XPoint * points,int lines,int sizex,int sizey)177 compute_petal(XPoint * points, int lines, int sizex, int sizey)
178 /*-
179 * Description:
180 *
181 * Basically, it's combination spirograph and string art.
182 * Instead of doing lines, we just use a complex polygon,
183 * and use an even/odd rule for filling in between.
184 *
185 * The spirograph, in mathematical terms is a polar
186 * plot of the form r = sin (theta * c);
187 * The string art of this is that we evaluate that
188 * function only on certain multiples of theta. That is
189 * we let theta advance in some random increment. And then
190 * we draw a straight line between those two adjacent points.
191 *
192 * Eventually, the lines will start repeating themselves
193 * if we've evaluated theta on some rational portion of the
194 * whole.
195 *
196 * The number of lines generated is limited to the
197 * ratio of the increment we put on theta to the whole.
198 * If we say that there are 360 degrees in a circle, then we
199 * will never have more than 360 lines.
200 *
201 * Return:
202 *
203 * The number of points.
204 *
205 */
206 {
207 int a, b, d; /* These describe a unique petal */
208
209 double r;
210 int theta = 0;
211 XPoint *p = points;
212 int count;
213 int npoints;
214
215 for (;;) {
216 d = lines;
217
218 a = rand_range(1, d);
219 b = rand_range(1, d);
220 npoints = numlines(a, b, d);
221 if (npoints >= MINLINES)
222 break;
223 }
224
225 /* it might be nice to try to move as much sin and cos computing
226 * (or at least the argument computing) out of the loop.
227 */
228 for (count = npoints; count--;) {
229 r = sine(theta * a, d);
230
231 /* Convert from polar to cartesian coordinates */
232 /* We could round the results, but coercing seems just fine */
233 p->x = (int) (sine(theta, d) * r * sizex + sizex);
234 p->y = (int) (cosine(theta, d) * r * sizey + sizey);
235
236 /* Advance index into array */
237 p++;
238
239 /* Advance theta */
240 theta += b;
241 theta %= d;
242 }
243 *p = points[0]; /* Tack on another for XDrawLines */
244
245 return (npoints);
246 }
247
248 static void
petal(ModeInfo * mi)249 petal(ModeInfo * mi)
250 {
251 petalstruct *pp = &petals[MI_SCREEN(mi)];
252
253 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), pp->color);
254 if (pp->wireframe) {
255 XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
256 pp->points, pp->npoints + 1, CoordModeOrigin);
257 } else {
258 XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
259 pp->points, pp->npoints, Complex, CoordModeOrigin);
260 }
261 }
262
263 static void
random_petal(ModeInfo * mi)264 random_petal(ModeInfo * mi)
265 {
266 petalstruct *pp = &petals[MI_SCREEN(mi)];
267
268 pp->npoints = compute_petal(pp->points, pp->lines,
269 pp->width / 2, pp->height / 2);
270
271 if (MI_NPIXELS(mi) <= 2)
272 pp->color = MI_WHITE_PIXEL(mi);
273 else
274 pp->color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
275 petal(mi);
276 }
277
278
279 static void
free_petal_screen(petalstruct * pp)280 free_petal_screen(petalstruct *pp)
281 {
282 if (pp == NULL) {
283 return;
284 }
285 if (pp->points)
286 free(pp->points);
287 pp = NULL;
288 }
289
290 ENTRYPOINT void
init_petal(ModeInfo * mi)291 init_petal(ModeInfo * mi)
292 {
293 petalstruct *pp;
294
295 MI_INIT(mi, petals);
296 pp = &petals[MI_SCREEN(mi)];
297
298 pp->lines = MI_COUNT(mi);
299 if (pp->lines > MAXLINES)
300 pp->lines = MAXLINES;
301 else if (pp->lines < -MINLINES) {
302 if (pp->points) {
303 free(pp->points);
304 pp->points = (XPoint *) NULL;
305 }
306 pp->lines = NRAND(-pp->lines - MINLINES + 1) + MINLINES;
307 } else if (pp->lines < MINLINES)
308 pp->lines = MINLINES;
309 if (!pp->points)
310 if ((pp->points = (XPoint *) malloc((pp->lines + 1) *
311 sizeof (XPoint))) == NULL) {
312 return;
313 }
314 pp->width = MI_WIDTH(mi);
315 pp->height = MI_HEIGHT(mi);
316
317 pp->time = 0;
318 if (MI_IS_FULLRANDOM(mi))
319 pp->wireframe = (Bool) (LRAND() & 1);
320 else
321 pp->wireframe = MI_IS_WIREFRAME(mi);
322
323 #ifdef STANDALONE
324 MI_CLEARWINDOWCOLORMAPFAST(mi, MI_GC(mi), MI_BLACK_PIXEL(mi));
325 #else
326 MI_CLEARWINDOW(mi);
327 #endif
328 pp->painted = False;
329
330 random_petal(mi);
331 }
332
333 ENTRYPOINT void
draw_petal(ModeInfo * mi)334 draw_petal(ModeInfo * mi)
335 {
336 petalstruct *pp;
337
338 if (petals == NULL)
339 return;
340 pp = &petals[MI_SCREEN(mi)];
341
342 MI_IS_DRAWN(mi) = True;
343 if (++pp->time > MI_CYCLES(mi))
344 init_petal(mi);
345 else
346 pp->painted = True;
347 }
348
349 ENTRYPOINT void
release_petal(ModeInfo * mi)350 release_petal(ModeInfo * mi)
351 {
352 if (petals != NULL) {
353 int screen;
354
355 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
356 petalstruct *pp = &petals[screen];
357
358 free_petal_screen(pp);
359 }
360 free(petals);
361 petals = (petalstruct *) NULL;
362 }
363 }
364
365 #ifndef STANDALONE
366 ENTRYPOINT void
refresh_petal(ModeInfo * mi)367 refresh_petal(ModeInfo * mi)
368 {
369 petalstruct *pp;
370
371 if (petals == NULL)
372 return;
373 pp = &petals[MI_SCREEN(mi)];
374
375 if (pp->painted) {
376 MI_CLEARWINDOW(mi);
377 petal(mi);
378 pp->painted = False;
379 }
380 }
381 #endif
382
383 XSCREENSAVER_MODULE ("Petal", petal)
384
385 #endif /* MODE_petal */
386