1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* spline --- spline fun */
3 
4 #if 0
5 static const char sccsid[] = "@(#)spline.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1992 by Jef Poskanzer
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000:
26  * 10-May-1997: Compatible with xscreensaver
27  * 13-Sep-1996: changed FOLLOW to a runtime option "-erase"
28  * 17-Jan-1996: added compile time option, FOLLOW to erase old splines like Qix
29  *              thanks to Richard Duran <rduran@cs.utep.edu>
30  * 09-Mar-1995: changed how batchcount is used
31  * 02-Sep-1993: xlock version: David Bagley <bagleyd AT verizon.net>
32  *              reminds me of a great "Twilight Zone" episode.
33  * 1992: X11 version Jef Poskanzer <jef@netcom.com>, <jef@well.sf.ca.us>
34  *
35  * spline fun #3
36  */
37 
38 /*-
39  * original copyright
40  * xsplinefun.c - X11 version of spline fun #3
41  * Displays colorful moving splines in the X11 root window.
42  * Copyright (C) 1992 by Jef Poskanzer
43  * Permission to use, copy, modify, and distribute this software and its
44  * documentation for any purpose and without fee is hereby granted, provided
45  * that the above copyright notice appear in all copies and that both that
46  * copyright notice and this permission notice appear in supporting
47  * documentation.  This software is provided "as is" without express or
48  * implied warranty.
49  */
50 
51 #ifdef STANDALONE
52 #define MODE_spline
53 #define DEFAULTS "*delay: 30000 \n" \
54 	"*count: -6 \n" \
55 	"*cycles: 2048 \n" \
56 	"*ncolors: 200 \n" \
57 	"*fullrandom: True \n" \
58 
59 # define free_spline 0
60 # define reshape_spline 0
61 # define spline_handle_event 0
62 #define UNIFORM_COLORS
63 #define BRIGHT_COLORS
64 #include "xlockmore.h"		/* in xscreensaver distribution */
65 #else /* STANDALONE */
66 #include "xlock.h"		/* in xlockmore distribution */
67 
68 #endif /* STANDALONE */
69 
70 #ifdef MODE_spline
71 
72 #define DEF_ERASE  "False"
73 
74 static Bool erase;
75 
76 static XrmOptionDescRec opts[] =
77 {
78 	{(char *) "-erase", (char *) ".spline.erase", XrmoptionNoArg, (caddr_t) "on"},
79 	{(char *) "+erase", (char *) ".spline.erase", XrmoptionNoArg, (caddr_t) "off"}
80 };
81 
82 static argtype vars[] =
83 {
84 	{(void *) & erase, (char *) "erase", (char *) "Erase", (char *) DEF_ERASE, t_Bool}
85 };
86 
87 static OptionStruct desc[] =
88 {
89 	{(char *) "-/+erase", (char *) "turn on/off the following erase of splines"}
90 };
91 
92 ENTRYPOINT ModeSpecOpt spline_opts =
93 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
94 
95 #ifdef USE_MODULES
96 ModStruct   spline_description =
97 {"spline", "init_spline", "draw_spline", "release_spline",
98  "refresh_spline", "init_spline", (char *) NULL, &spline_opts,
99  30000, -6, 2048, 1, 64, 0.3, "",
100  "Shows colorful moving splines", 0, NULL};
101 
102 #endif
103 
104 #define MINPOINTS 3
105 
106 /* How many segments to draw per cycle when redrawing */
107 #define REDRAWSTEP 3
108 
109 #define SPLINE_THRESH 5
110 
111 typedef struct {
112 	XPoint      pos, delta;
113 } splinepointstruct;
114 
115 typedef struct {
116 	/* ERASE stuff */
117 	int         first;
118 	int         last;
119 	int         max_delta;
120 	XPoint    **splineq;
121 	int         redrawing, redrawpos;
122 	Bool        erase;
123 
124 	int         width;
125 	int         height;
126 	int         color;
127 	int         points;
128 	int         nsplines;
129 	splinepointstruct *pt;
130 } splinestruct;
131 
132 static splinestruct *splines = (splinestruct *) NULL;
133 
134 static void XDrawSpline(Display * display, Drawable d, GC gc,
135 			int x0, int y0, int x1, int y1, int x2, int y2);
136 
137 static void
free_spline_screen(splinestruct * sp)138 free_spline_screen(splinestruct *sp)
139 {
140 	if (sp == NULL) {
141 		return;
142 	}
143 	if (sp->pt != NULL) {
144 		free(sp->pt);
145 		sp->pt = (splinepointstruct *) NULL;
146 	}
147 	if (sp->splineq != NULL) {
148 		int         spline;
149 
150 		for (spline = 0; spline < sp->nsplines; ++spline)
151 			if (sp->splineq[spline] != NULL)
152 				free(sp->splineq[spline]);
153 		free(sp->splineq);
154 		sp->splineq = (XPoint **) NULL;
155 	}
156 	sp = NULL;
157 }
158 
159 ENTRYPOINT void
init_spline(ModeInfo * mi)160 init_spline(ModeInfo * mi)
161 {
162 	int         i;
163 	splinestruct *sp;
164 
165 	MI_INIT(mi, splines);
166 	sp = &splines[MI_SCREEN(mi)];
167 
168 	sp->width = MI_WIDTH(mi);
169 	sp->height = MI_HEIGHT(mi);
170 	/* batchcount is the upper bound on the number of points */
171 	sp->points = MI_COUNT(mi);
172 	if (sp->points < -MINPOINTS) {
173 		if (sp->pt) {
174 			free(sp->pt);
175 			sp->pt = (splinepointstruct *) NULL;
176 		}
177 		if (sp->erase) {
178 			if (sp->splineq)
179 				for (i = 0; i < sp->nsplines; ++i)
180 					if (sp->splineq[i]) {
181 						free(sp->splineq[i]);
182 						sp->splineq[i] = (XPoint *) NULL;
183 					}
184 		}
185 		sp->points = NRAND(-sp->points - MINPOINTS + 1) + MINPOINTS;
186 	} else if (sp->points < MINPOINTS)
187 		sp->points = MINPOINTS;
188 	if (MI_IS_FULLRANDOM(mi))
189 		sp->erase = (Bool) (LRAND() & 1);
190 	else
191 		sp->erase = erase;
192 	if (sp->pt == NULL)
193 		if ((sp->pt = (splinepointstruct *) malloc(sp->points *
194 				sizeof (splinepointstruct))) == NULL) {
195 			free_spline_screen(sp);
196 			return;
197 		}
198 
199 	if (sp->erase) {
200 		sp->max_delta = 16;
201 		sp->redrawing = 0;
202 		sp->last = 0;
203 		sp->nsplines = MI_CYCLES(mi) / 64 + 1;	/* So as to be compatible */
204 		if (sp->splineq == NULL)
205 			if ((sp->splineq = (XPoint **) calloc(sp->nsplines,
206 					sizeof (XPoint *))) == NULL) {
207 				free_spline_screen(sp);
208 				return;
209 			}
210 		for (i = 0; i < sp->nsplines; ++i)
211 			if (sp->splineq[i] == NULL)
212 				if ((sp->splineq[i] = (XPoint *) calloc(sp->points,
213 						sizeof (XPoint))) == NULL) {
214 					free_spline_screen(sp);
215 					return;
216 				}
217 	} else {
218 		sp->max_delta = 3;
219 		sp->nsplines = 0;
220 	}
221 
222 	MI_CLEARWINDOW(mi);
223 
224 	/* Initialize points. */
225 	for (i = 0; i < sp->points; ++i) {
226 		sp->pt[i].pos.x = NRAND(sp->width);
227 		sp->pt[i].pos.y = NRAND(sp->height);
228 		sp->pt[i].delta.x = NRAND(sp->max_delta * 2) - sp->max_delta;
229 		if (sp->pt[i].delta.x <= 0 && sp->width > 1)
230 			--sp->pt[i].delta.x;
231 		sp->pt[i].delta.y = NRAND(sp->max_delta * 2) - sp->max_delta;
232 		if (sp->pt[i].delta.y <= 0 && sp->height > 1)
233 			--sp->pt[i].delta.y;
234 	}
235 	if (MI_NPIXELS(mi) > 2)
236 		sp->color = NRAND(MI_NPIXELS(mi));
237 }
238 
239 ENTRYPOINT void
draw_spline(ModeInfo * mi)240 draw_spline(ModeInfo * mi)
241 {
242 	Display    *display = MI_DISPLAY(mi);
243 	Window      window = MI_WINDOW(mi);
244 	GC          gc = MI_GC(mi);
245 	int         i, t, px, py, zx, zy, nx, ny;
246 	splinestruct *sp;
247 
248 	if (splines == NULL)
249 		return;
250 	sp = &splines[MI_SCREEN(mi)];
251 	if (sp->pt == NULL)
252 		return;
253 
254 	MI_IS_DRAWN(mi) = True;
255 	if (sp->erase)
256 		sp->first = (sp->last + 2) % sp->nsplines;
257 
258 	/* Move the points. */
259 	for (i = 0; i < sp->points; i++) {
260 		for (;;) {
261 			t = sp->pt[i].pos.x + sp->pt[i].delta.x;
262 			if (t >= 0 && t < sp->width)
263 				break;
264 			sp->pt[i].delta.x = NRAND(sp->max_delta * 2) - sp->max_delta;
265 			if (sp->pt[i].delta.x <= 0 && sp->width > 1)
266 				--sp->pt[i].delta.x;
267 		}
268 		sp->pt[i].pos.x = t;
269 		for (;;) {
270 			t = sp->pt[i].pos.y + sp->pt[i].delta.y;
271 			if (t >= 0 && t < sp->height)
272 				break;
273 			sp->pt[i].delta.y = NRAND(sp->max_delta * 2) - sp->max_delta;
274 			if (sp->pt[i].delta.y <= 0 && sp->height > 1)
275 				--sp->pt[i].delta.y;
276 		}
277 		sp->pt[i].pos.y = t;
278 	}
279 
280 	if (sp->erase) {
281 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
282 
283 		/* Erase first figure. */
284 		px = zx = (sp->splineq[sp->first][0].x +
285 			   sp->splineq[sp->first][sp->points - 1].x) / 2;
286 		py = zy = (sp->splineq[sp->first][0].y +
287 			   sp->splineq[sp->first][sp->points - 1].y) / 2;
288 		for (i = 0; i < sp->points - 1; ++i) {
289 			nx = (sp->splineq[sp->first][i + 1].x +
290 			      sp->splineq[sp->first][i].x) / 2;
291 			ny = (sp->splineq[sp->first][i + 1].y +
292 			      sp->splineq[sp->first][i].y) / 2;
293 			XDrawSpline(display, window, gc,
294 				    px, py, sp->splineq[sp->first][i].x,
295 				    sp->splineq[sp->first][i].y, nx, ny);
296 			px = nx;
297 			py = ny;
298 		}
299 
300 		XDrawSpline(display, window, gc,
301 			    px, py, sp->splineq[sp->first][sp->points - 1].x,
302 			    sp->splineq[sp->first][sp->points - 1].y, zx, zy);
303 	}
304 	if (MI_NPIXELS(mi) > 2) {
305 		XSetForeground(display, gc, MI_PIXEL(mi, sp->color));
306 		if (++sp->color >= MI_NPIXELS(mi))
307 			sp->color = 0;
308 	} else
309 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
310 
311 	/* Draw the figure. */
312 	px = zx = (sp->pt[0].pos.x + sp->pt[sp->points - 1].pos.x) / 2;
313 	py = zy = (sp->pt[0].pos.y + sp->pt[sp->points - 1].pos.y) / 2;
314 	for (i = 0; i < sp->points - 1; ++i) {
315 		nx = (sp->pt[i + 1].pos.x + sp->pt[i].pos.x) / 2;
316 		ny = (sp->pt[i + 1].pos.y + sp->pt[i].pos.y) / 2;
317 		XDrawSpline(display, window, gc,
318 			    px, py, sp->pt[i].pos.x, sp->pt[i].pos.y, nx, ny);
319 		px = nx;
320 		py = ny;
321 	}
322 
323 	XDrawSpline(display, window, gc, px, py,
324 	 sp->pt[sp->points - 1].pos.x, sp->pt[sp->points - 1].pos.y, zx, zy);
325 
326 	if (sp->erase) {
327 		for (i = 0; i < sp->points; ++i) {
328 			sp->splineq[sp->last][i].x = sp->pt[i].pos.x;
329 			sp->splineq[sp->last][i].y = sp->pt[i].pos.y;
330 		}
331 		sp->last++;
332 		if (sp->last >= sp->nsplines)
333 			sp->last = 0;
334 
335 		if (sp->redrawing) {
336 			int         j, k;
337 
338 			sp->redrawpos++;
339 			/* This compensates for the changed sp->last
340 			   since last callback */
341 
342 			for (k = 0; k < REDRAWSTEP; k++) {
343 				j = (sp->last - sp->redrawpos + sp->nsplines) %
344 					sp->nsplines;
345 #ifdef BACKWARDS
346 				/* Draw backwards, probably will not see it,
347 				   but its the thought ... */
348 				px = zx = (sp->splineq[j][0].x +
349 				       sp->splineq[j][sp->points - 1].x) / 2;
350 				py = zy = (sp->splineq[j][0].y +
351 				       sp->splineq[j][sp->points - 1].y) / 2;
352 				for (i = sp->points - 1; i > 0; --i) {
353 					nx = (sp->splineq[j][i - 1].x +
354 					      sp->splineq[j][i].x) / 2;
355 					ny = (sp->splineq[j][i - 1].y +
356 					      sp->splineq[j][i].y) / 2;
357 					XDrawSpline(display, window, gc,
358 						 px, py, sp->splineq[j][i].x,
359 						sp->splineq[j][i].y, nx, ny);
360 					px = nx;
361 					py = ny;
362 				}
363 
364 				XDrawSpline(display, window, gc,
365 					    px, py, sp->splineq[j][0].x,
366 					    sp->splineq[j][0].y, zx, zy);
367 #else
368 				px = zx = (sp->splineq[j][0].x +
369 				       sp->splineq[j][sp->points - 1].x) / 2;
370 				py = zy = (sp->splineq[j][0].y +
371 				       sp->splineq[j][sp->points - 1].y) / 2;
372 				for (i = 0; i < sp->points - 1; ++i) {
373 					nx = (sp->splineq[j][i + 1].x +
374 					      sp->splineq[j][i].x) / 2;
375 					ny = (sp->splineq[j][i + 1].y +
376 					      sp->splineq[j][i].y) / 2;
377 					XDrawSpline(display, window, gc,
378 						 px, py, sp->splineq[j][i].x,
379 						sp->splineq[j][i].y, nx, ny);
380 					px = nx;
381 					py = ny;
382 				}
383 
384 				XDrawSpline(display, window, gc,
385 				    px, py, sp->splineq[j][sp->points - 1].x,
386 				   sp->splineq[j][sp->points - 1].y, zx, zy);
387 #endif
388 				if (++(sp->redrawpos) >= sp->nsplines - 1) {
389 					sp->redrawing = 0;
390 					break;
391 				}
392 			}
393 		}
394 	} else if (++sp->nsplines > MI_CYCLES(mi))
395 		init_spline(mi);
396 }
397 
398 /* X spline routine. */
399 
400 static void
XDrawSpline(Display * display,Drawable d,GC gc,int x0,int y0,int x1,int y1,int x2,int y2)401 XDrawSpline(Display * display, Drawable d, GC gc, int x0, int y0, int x1, int y1, int x2, int y2)
402 {
403 	register int xa, ya, xb, yb, xc, yc, xp, yp;
404 
405 	xa = (x0 + x1) / 2;
406 	ya = (y0 + y1) / 2;
407 	xc = (x1 + x2) / 2;
408 	yc = (y1 + y2) / 2;
409 	xb = (xa + xc) / 2;
410 	yb = (ya + yc) / 2;
411 
412 	xp = (x0 + xb) / 2;
413 	yp = (y0 + yb) / 2;
414 	if (ABS(xa - xp) + ABS(ya - yp) > SPLINE_THRESH)
415 		XDrawSpline(display, d, gc, x0, y0, xa, ya, xb, yb);
416 	else
417 		XDrawLine(display, d, gc, x0, y0, xb, yb);
418 
419 	xp = (x2 + xb) / 2;
420 	yp = (y2 + yb) / 2;
421 	if (ABS(xc - xp) + ABS(yc - yp) > SPLINE_THRESH)
422 		XDrawSpline(display, d, gc, xb, yb, xc, yc, x2, y2);
423 	else
424 		XDrawLine(display, d, gc, xb, yb, x2, y2);
425 }
426 
427 ENTRYPOINT void
release_spline(ModeInfo * mi)428 release_spline(ModeInfo * mi)
429 {
430 	if (splines != NULL) {
431 		int         screen;
432 
433 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
434 			free_spline_screen(&splines[screen]);
435 		free(splines);
436 		splines = (splinestruct *) NULL;
437 	}
438 }
439 
440 #ifndef STANDALONE
441 ENTRYPOINT void
refresh_spline(ModeInfo * mi)442 refresh_spline(ModeInfo * mi)
443 {
444 	splinestruct *sp;
445 
446 	if (splines == NULL)
447 		return;
448 	sp = &splines[MI_SCREEN(mi)];
449 
450 	if (sp->erase) {
451 		sp->redrawing = 1;
452 		sp->redrawpos = 1;
453 	} else {
454 		MI_CLEARWINDOW(mi);
455 	}
456 }
457 #endif
458 
459 XSCREENSAVER_MODULE ("Spline", spline)
460 
461 #endif /* MODE_spline */
462