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