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