ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[xscreensaver] / hacks / braid.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * braid --- draws random color-cyling rotating braids around a circle.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)braid.c       4.00 97/01/01 xlockmore";
6 #endif
7
8 /*
9  * Copyright (c) 1995 by John Neil.
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History:
24  * 10-May-97: jwz@netscape.com: turned into a standalone program.
25  * 01-Sep-95: color knotted components differently, J. Neil.
26  * 29-Aug-95: Written.  John Neil <neil@math.idbsu.edu>
27  */
28
29 #ifdef STANDALONE
30 # define PROGCLASS                                      "Braid"
31 # define HACK_INIT                                      init_braid
32 # define HACK_DRAW                                      draw_braid
33 # define braid_opts                                     xlockmore_opts
34 # define DEFAULTS       "*count:                15    \n"                       \
35                                         "*size:            -7     \n"                   \
36                                         "*cycles:               100   \n"                       \
37                                         "*delay:                1000  \n"                       \
38                                         "*ncolors:              64    \n"                       \
39                                         "*eraseSpeed:   400 \n"                         \
40                                         "*eraseMode:    -1 \n"
41 # define UNIFORM_COLORS
42 # include "xlockmore.h"                         /* from the xscreensaver distribution */
43 # include "erase.h"
44 #else  /* !STANDALONE */
45 # include "xlock.h"                                     /* from the xlockmore distribution */
46 #endif /* !STANDALONE */
47
48 ModeSpecOpt braid_opts = {
49   0, NULL, 0, NULL, NULL };
50
51
52 #if defined( COLORROUND ) && defined( COLORCOMP )
53 #undef COLORROUND
54 #undef COLORCOMP
55 #endif
56
57 #if !defined( COLORROUND ) && !defined( COLORCOMP )
58 #if 0
59 /* to color in a circular pattern use COLORROUND */
60 #define COLORROUND
61 #else
62 /* to color by component use COLORCOMP */
63 #define COLORCOMP
64 #endif
65 #endif
66
67 #define MAXLENGTH  50           /* the maximum length of a braid word */
68 #define MINLENGTH  8            /* the minimum length of a braid word */
69 #define MAXSTRANDS  15          /* the maximum number of strands in the braid */
70 #define MINSTRANDS  3           /* the minimum number of strands in the braid */
71 #define SPINRATE  12.0          /* the rate at which the colors spin */
72
73 #define INTRAND(min,max) (NRAND((max+1)-(min))+(min))
74 #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min)))
75
76 typedef struct {
77         int         braidword[MAXLENGTH];
78         int         components[MAXSTRANDS];
79         int         startcomp[MAXLENGTH][MAXSTRANDS];
80         int         nstrands;
81         int         braidlength;
82         float       startcolor;
83         int         center_x;
84         int         center_y;
85         float       min_radius;
86         float       max_radius;
87         float       top, bottom, left, right;
88         int         age;
89     int         color_direction;
90 } braidtype;
91
92 static braidtype *braids = NULL;
93
94 static int
95 applyword(braidtype * braid, int string, int position)
96 {
97         int         i, c;
98
99         c = string;
100         for (i = position; i < braid->braidlength; i++) {
101                 if (c == ABS(braid->braidword[i]))
102                         c--;
103                 else if (c == ABS(braid->braidword[i]) - 1)
104                         c++;
105         }
106         for (i = 0; i < position; i++) {
107                 if (c == ABS(braid->braidword[i]))
108                         c--;
109                 else if (c == ABS(braid->braidword[i]) - 1)
110                         c++;
111         }
112         return c;
113 }
114
115 #if 0
116 static int
117 applywordto(braidtype * braid, int string, int position)
118 {
119         int         i, c;
120
121         c = string;
122         for (i = 0; i < position; i++) {
123                 if (c == ABS(braid->braidword[i])) {
124                         c--;
125                 } else if (c == ABS(braid->braidword[i]) - 1) {
126                         c++;
127                 }
128         }
129         return c;
130 }
131 #endif
132
133 static int
134 applywordbackto(braidtype * braid, int string, int position)
135 {
136         int         i, c;
137
138         c = string;
139         for (i = position - 1; i >= 0; i--) {
140                 if (c == ABS(braid->braidword[i])) {
141                         c--;
142                 } else if (c == ABS(braid->braidword[i]) - 1) {
143                         c++;
144                 }
145         }
146         return c;
147 }
148
149 void
150 init_braid(ModeInfo * mi)
151 {
152         Display    *display = MI_DISPLAY(mi);
153         braidtype  *braid;
154         int         used[MAXSTRANDS];
155         int         i, count, comp, c;
156         float       min_length;
157
158         if (braids == NULL) {
159                 if ((braids = (braidtype *) calloc(MI_NUM_SCREENS(mi),
160                                                 sizeof (braidtype))) == NULL)
161                         return;
162         }
163         braid = &braids[MI_SCREEN(mi)];
164
165         braid->center_x = MI_WIN_WIDTH(mi) / 2;
166         braid->center_y = MI_WIN_HEIGHT(mi) / 2;
167         braid->age = 0;
168
169         /* jwz: go in the other direction sometimes. */
170         braid->color_direction = ((LRAND() & 1) ? 1 : -1);
171         XClearWindow(display, MI_WINDOW(mi));
172
173         min_length = (braid->center_x > braid->center_y) ?
174                 braid->center_y : braid->center_x;
175         braid->min_radius = min_length * 0.30;
176         braid->max_radius = min_length * 0.90;
177
178         if (MI_BATCHCOUNT(mi) < MINSTRANDS)
179                 braid->nstrands = MINSTRANDS;
180         else
181                 braid->nstrands = INTRAND(MINSTRANDS,
182                                   MAX(MIN(MIN(MAXSTRANDS, MI_BATCHCOUNT(mi)),
183                                           (int) ((braid->max_radius - braid->min_radius) / 5.0)), MINSTRANDS));
184         braid->braidlength = INTRAND(MINLENGTH, MIN(MAXLENGTH, braid->nstrands * 6));
185
186         for (i = 0; i < braid->braidlength; i++) {
187                 braid->braidword[i] =
188                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
189                 if (i > 0)
190                         while (braid->braidword[i] == -braid->braidword[i - 1])
191                                 braid->braidword[i] = INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
192         }
193
194         while (braid->braidword[0] == -braid->braidword[braid->braidlength - 1])
195                 braid->braidword[braid->braidlength - 1] =
196                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
197
198         do {
199                 (void) memset((char *) used, 0, sizeof (used));
200                 count = 0;
201                 for (i = 0; i < braid->braidlength; i++)
202                         used[ABS(braid->braidword[i])]++;
203                 for (i = 0; i < braid->nstrands; i++)
204                         count += (used[i] > 0) ? 1 : 0;
205                 if (count < braid->nstrands - 1) {
206                         braid->braidword[braid->braidlength] =
207                                 INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
208                         while (braid->braidword[braid->braidlength] ==
209                                -braid->braidword[braid->braidlength - 1] &&
210                                braid->braidword[0] == -braid->braidword[braid->braidlength])
211                                 braid->braidword[braid->braidlength] =
212                                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
213                         braid->braidlength++;
214                 }
215         } while (count < braid->nstrands - 1 && braid->braidlength < MAXLENGTH);
216
217         braid->startcolor = (MI_NPIXELS(mi) > 2) ?
218                 (float) NRAND(MI_NPIXELS(mi)) : 0.0;
219         /* XSetLineAttributes (display, MI_GC(mi), 2, LineSolid, CapRound, 
220            JoinRound); */
221
222         (void) memset((char *) braid->components, 0, sizeof (braid->components));
223         c = 1;
224         comp = 0;
225         braid->components[0] = 1;
226         do {
227                 i = comp;
228                 do {
229                         i = applyword(braid, i, 0);
230                         braid->components[i] = braid->components[comp];
231                 } while (i != comp);
232                 count = 0;
233                 for (i = 0; i < braid->nstrands; i++)
234                         if (braid->components[i] == 0)
235                                 count++;
236                 if (count > 0) {
237                         for (comp = 0; braid->components[comp] != 0; comp++);
238                         braid->components[comp] = ++c;
239                 }
240         } while (count > 0);
241
242         {
243           int line_width = MI_SIZE(mi);
244           if (line_width == 0)
245                 line_width = -8;
246           if (line_width < 0)
247                 line_width = NRAND(-line_width)+1;
248           if (line_width == 1)
249                 line_width = 0;
250           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), line_width,
251                                                  LineSolid,
252                                                  (line_width <= 3 ? CapButt : CapRound),
253                                                  JoinMiter);
254         }
255
256
257         for (i = 0; i < braid->nstrands; i++)
258                 if (!(braid->components[i] & 1))
259                         braid->components[i] *= -1;
260 }
261
262 void
263 draw_braid(ModeInfo * mi)
264 {
265         Display    *display = MI_DISPLAY(mi);
266         Window      window = MI_WINDOW(mi);
267         braidtype  *braid = &braids[MI_SCREEN(mi)];
268         float       num_points, t_inc;
269         float       theta, psi;
270         float       t, r_diff;
271         int         i, s;
272         float       x_1, y_1, x_2, y_2, r1, r2;
273         float       color, color_use = 0.0, color_inc;
274
275         num_points = 500.0;
276         theta = (2.0 * M_PI) / (float) (braid->braidlength);
277         t_inc = (2.0 * M_PI) / num_points;
278         color_inc = (float) MI_NPIXELS(mi) / num_points;
279         color_inc *= braid->color_direction;
280
281         braid->startcolor += SPINRATE * color_inc;
282         if (braid->startcolor >= MI_NPIXELS(mi))
283                 braid->startcolor = 0.0;
284
285         r_diff = (braid->max_radius - braid->min_radius) / (float) (braid->nstrands);
286
287         color = braid->startcolor;
288         psi = 0.0;
289         for (i = 0; i < braid->braidlength; i++) {
290                 psi += theta;
291                 for (t = 0.0; t < theta; t += t_inc) {
292 #ifdef COLORROUND
293                         color += color_inc;
294                         if (color >= (float) (MI_NPIXELS(mi)))
295                                 color = 0.0;
296                         color_use = color;
297 #endif
298                         for (s = 0; s < braid->nstrands; s++) {
299                                 if (ABS(braid->braidword[i]) == s)
300                                         continue;
301                                 if (ABS(braid->braidword[i]) - 1 == s) {
302                                         /* crosSINFg */
303 #ifdef COLORCOMP
304                                         if (MI_NPIXELS(mi) > 2) {
305                                                 color_use = color + SPINRATE *
306                                                         braid->components[applywordbackto(braid, s, i)] +
307                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
308                                                 while (color_use >= (float) MI_NPIXELS(mi))
309                                                         color_use -= (float) MI_NPIXELS(mi);
310                                                 while (color_use < 0.0)
311                                                         color_use += (float) MI_NPIXELS(mi);
312                                         }
313 #endif
314 #ifdef COLORROUND
315                                         if (MI_NPIXELS(mi) > 2) {
316                                                 color_use += SPINRATE * color_inc;
317                                                 while (color_use >= (float) (MI_NPIXELS(mi)))
318                                                         color_use -= (float) MI_NPIXELS(mi);
319                                         }
320 #endif
321                                         r1 = braid->min_radius + r_diff * (float) (s);
322                                         r2 = braid->min_radius + r_diff * (float) (s + 1);
323                                         if (braid->braidword[i] > 0 ||
324                                             (FABSF(t - theta / 2.0) > theta / 7.0)) {
325                                                 x_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r2 +
326                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r1)) *
327                                                         COSF(t + psi) + braid->center_x;
328                                                 y_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r2 +
329                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r1)) *
330                                                         SINF(t + psi) + braid->center_y;
331                                                 x_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r2 +
332                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r1)) *
333                                                         COSF(t + t_inc + psi) + braid->center_x;
334                                                 y_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r2 +
335                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r1)) *
336                                                         SINF(t + t_inc + psi) + braid->center_y;
337                                                 if (MI_NPIXELS(mi) > 2)
338                                                         XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
339                                                 else
340                                                         XSetForeground(display, MI_GC(mi), MI_WIN_WHITE_PIXEL(mi));
341
342                                                 XDrawLine(display, window, MI_GC(mi),
343                                                           (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
344                                         }
345 #ifdef COLORCOMP
346                                         if (MI_NPIXELS(mi) > 2) {
347                                                 color_use = color + SPINRATE *
348                                                         braid->components[applywordbackto(braid, s + 1, i)] +
349                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
350                                                 while (color_use >= (float) MI_NPIXELS(mi))
351                                                         color_use -= (float) MI_NPIXELS(mi);
352                                                 while (color_use < 0.0)
353                                                         color_use += (float) MI_NPIXELS(mi);
354                                         }
355 #endif
356                                         if (braid->braidword[i] < 0 ||
357                                             (FABSF(t - theta / 2.0) > theta / 7.0)) {
358                                                 x_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r1 +
359                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r2)) *
360                                                         COSF(t + psi) + braid->center_x;
361                                                 y_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r1 +
362                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r2)) *
363                                                         SINF(t + psi) + braid->center_y;
364                                                 x_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r1 +
365                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r2)) *
366                                                         COSF(t + t_inc + psi) + braid->center_x;
367                                                 y_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r1 +
368                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r2)) *
369                                                         SINF(t + t_inc + psi) + braid->center_y;
370                                                 if (MI_NPIXELS(mi) > 2)
371                                                         XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
372                                                 else
373                                                         XSetForeground(display, MI_GC(mi), MI_WIN_WHITE_PIXEL(mi));
374
375                                                 XDrawLine(display, window, MI_GC(mi),
376                                                           (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
377                                         }
378                                 } else {
379                                         /* no crosSINFg */
380 #ifdef COLORCOMP
381                                         if (MI_NPIXELS(mi) > 2) {
382                                                 color_use = color + SPINRATE *
383                                                         braid->components[applywordbackto(braid, s, i)] +
384                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
385                                                 while (color_use >= (float) MI_NPIXELS(mi))
386                                                         color_use -= (float) MI_NPIXELS(mi);
387                                                 while (color_use < 0.0)
388                                                         color_use += (float) MI_NPIXELS(mi);
389                                         }
390 #endif
391 #ifdef COLORROUND
392                                         if (MI_NPIXELS(mi) > 2) {
393                                                 color_use += SPINRATE * color_inc;
394                                                 while (color_use >= (float) MI_NPIXELS(mi))
395                                                         color_use -= (float) MI_NPIXELS(mi);
396                                         }
397 #endif
398                                         r1 = braid->min_radius + r_diff * (float) (s);
399                                         x_1 = r1 * COSF(t + psi) + braid->center_x;
400                                         y_1 = r1 * SINF(t + psi) + braid->center_y;
401                                         x_2 = r1 * COSF(t + t_inc + psi) + braid->center_x;
402                                         y_2 = r1 * SINF(t + t_inc + psi) + braid->center_y;
403                                         if (MI_NPIXELS(mi) > 2)
404                                                 XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
405                                         else
406                                                 XSetForeground(display, MI_GC(mi), MI_WIN_WHITE_PIXEL(mi));
407
408                                         XDrawLine(display, window, MI_GC(mi),
409                                                   (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
410                                 }
411                         }
412                 }
413         }
414
415         if (++braid->age > MI_CYCLES(mi)) {
416 #ifdef STANDALONE
417           erase_full_window(MI_DISPLAY(mi), MI_WINDOW(mi));
418 #endif
419           init_braid(mi);
420         }
421 }
422
423 void
424 release_braid(ModeInfo * mi)
425 {
426         if (braids != NULL) {
427                 (void) free((void *) braids);
428                 braids = NULL;
429         }
430 }
431
432 void
433 refresh_braid(ModeInfo * mi)
434 {
435         /* Do nothing, it will refresh by itself */
436 }