From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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 release_braid 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", (char *) NULL,
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         MI_INIT (mi, braids, 0);
175         braid = &braids[MI_SCREEN(mi)];
176
177         braid->center_x = MI_WIDTH(mi) / 2;
178         braid->center_y = MI_HEIGHT(mi) / 2;
179         braid->age = 0;
180
181         /* jwz: go in the other direction sometimes. */
182         braid->color_direction = ((LRAND() & 1) ? 1 : -1);
183
184 #ifndef STANDALONE
185         MI_CLEARWINDOW(mi);
186 #endif
187
188         min_length = (braid->center_x > braid->center_y) ?
189                 braid->center_y : braid->center_x;
190         braid->min_radius = min_length * 0.30;
191         braid->max_radius = min_length * 0.90;
192
193         if (MI_COUNT(mi) < MINSTRANDS)
194                 braid->nstrands = MINSTRANDS;
195         else
196                 braid->nstrands = INTRAND(MINSTRANDS,
197                                        MAX(MIN(MIN(MAXSTRANDS, MI_COUNT(mi)),
198                                                (int) ((braid->max_radius - braid->min_radius) / 5.0)), MINSTRANDS));
199         braid->braidlength = INTRAND(MINLENGTH, MIN(MAXLENGTH -1, braid->nstrands * 6));
200
201         for (i = 0; i < braid->braidlength; i++) {
202                 braid->braidword[i] =
203                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
204                 if (i > 0)
205                         while (braid->braidword[i] == -braid->braidword[i - 1])
206                                 braid->braidword[i] = INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
207         }
208
209         while (braid->braidword[0] == -braid->braidword[braid->braidlength - 1])
210                 braid->braidword[braid->braidlength - 1] =
211                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
212
213         do {
214                 (void) memset((char *) used, 0, sizeof (used));
215                 count = 0;
216                 for (i = 0; i < braid->braidlength; i++)
217                         used[ABS(braid->braidword[i])]++;
218                 for (i = 0; i < braid->nstrands; i++)
219                         count += (used[i] > 0) ? 1 : 0;
220                 if (count < braid->nstrands - 1) {
221                         braid->braidword[braid->braidlength] =
222                                 INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
223                         while (braid->braidword[braid->braidlength] ==
224                                -braid->braidword[braid->braidlength - 1] &&
225                                braid->braidword[0] == -braid->braidword[braid->braidlength])
226                                 braid->braidword[braid->braidlength] =
227                                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
228                         braid->braidlength++;
229                 }
230         } while (count < braid->nstrands - 1 && braid->braidlength < MAXLENGTH);
231
232         braid->startcolor = (MI_NPIXELS(mi) > 2) ?
233                 (float) NRAND(MI_NPIXELS(mi)) : 0.0;
234         /* XSetLineAttributes (display, MI_GC(mi), 2, LineSolid, CapRound,
235            JoinRound); */
236
237         (void) memset((char *) braid->components, 0, sizeof (braid->components));
238         c = 1;
239         comp = 0;
240         braid->components[0] = 1;
241         do {
242                 i = comp;
243                 do {
244                         i = applyword(braid, i, 0);
245                         braid->components[i] = braid->components[comp];
246                 } while (i != comp);
247                 count = 0;
248                 for (i = 0; i < braid->nstrands; i++)
249                         if (braid->components[i] == 0)
250                                 count++;
251                 if (count > 0) {
252                         for (comp = 0; braid->components[comp] != 0; comp++);
253                         braid->components[comp] = ++c;
254                 }
255         } while (count > 0);
256
257         braid->linewidth = MI_SIZE(mi);
258
259         if (braid->linewidth < 0)
260                 braid->linewidth = NRAND(-braid->linewidth) + 1;
261         if (braid->linewidth * braid->linewidth * 8 > MIN(MI_WIDTH(mi), MI_HEIGHT(mi)))
262        braid->linewidth = MIN(1, (int) sqrt((double) MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) / 8));
263         for (i = 0; i < braid->nstrands; i++)
264                 if (!(braid->components[i] & 1))
265                         braid->components[i] *= -1;
266 }
267
268 ENTRYPOINT void
269 draw_braid(ModeInfo * mi)
270 {
271         Display    *display = MI_DISPLAY(mi);
272         Window      window = MI_WINDOW(mi);
273         int         num_points = 500;
274         float       t_inc;
275         float       theta, psi;
276         float       t, r_diff;
277         int         i, s;
278         float       x_1, y_1, x_2, y_2, r1, r2;
279         float       color, color_use = 0.0, color_inc;
280         braidtype  *braid;
281
282         if (braids == NULL)
283                 return;
284         braid = &braids[MI_SCREEN(mi)];
285
286 #ifdef STANDALONE
287     if (braid->eraser) {
288       braid->eraser = erase_window (MI_DISPLAY(mi), MI_WINDOW(mi), braid->eraser);
289       if (!braid->eraser)
290         init_braid(mi);
291       return;
292     }
293 #endif
294
295         MI_IS_DRAWN(mi) = True;
296         XSetLineAttributes(display, MI_GC(mi), braid->linewidth,
297                            LineSolid,
298                            (braid->linewidth <= 3 ? CapButt : CapRound),
299                            JoinMiter);
300
301         theta = (2.0 * M_PI) / (float) (braid->braidlength);
302         t_inc = (2.0 * M_PI) / (float) num_points;
303         color_inc = (float) MI_NPIXELS(mi) * braid->color_direction /
304                 (float) num_points;
305         braid->startcolor += SPINRATE * color_inc;
306         if (((int) braid->startcolor) >= MI_NPIXELS(mi))
307                 braid->startcolor = 0.0;
308
309         r_diff = (braid->max_radius - braid->min_radius) / (float) (braid->nstrands);
310
311         color = braid->startcolor;
312         psi = 0.0;
313         for (i = 0; i < braid->braidlength; i++) {
314                 psi += theta;
315                 for (t = 0.0; t < theta; t += t_inc) {
316 #ifdef COLORROUND
317                         color += color_inc;
318                         if (((int) color) >= MI_NPIXELS(mi))
319                                 color = 0.0;
320                         color_use = color;
321 #endif
322                         for (s = 0; s < braid->nstrands; s++) {
323                                 if (ABS(braid->braidword[i]) == s)
324                                         continue;
325                                 if (ABS(braid->braidword[i]) - 1 == s) {
326                                         /* crosSINFg */
327 #ifdef COLORCOMP
328                                         if (MI_NPIXELS(mi) > 2) {
329                                                 color_use = color + SPINRATE *
330                                                         braid->components[applywordbackto(braid, s, i)] +
331                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
332                                                 while (((int) color_use) >= MI_NPIXELS(mi))
333                                                         color_use -= (float) MI_NPIXELS(mi);
334                                                 while (((int) color_use) < 0)
335                                                         color_use += (float) MI_NPIXELS(mi);
336                                         }
337 #endif
338 #ifdef COLORROUND
339                                         if (MI_NPIXELS(mi) > 2) {
340                                                 color_use += SPINRATE * color_inc;
341                                                 while (((int) color_use) >= MI_NPIXELS(mi))
342                                                         color_use -= (float) MI_NPIXELS(mi);
343                                         }
344 #endif
345                                         r1 = braid->min_radius + r_diff * (float) (s);
346                                         r2 = braid->min_radius + r_diff * (float) (s + 1);
347                                         if (braid->braidword[i] > 0 ||
348                                             (FABSF(t - theta / 2.0) > theta / 7.0)) {
349                                                 x_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r2 +
350                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r1)) *
351                                                         COSF(t + psi) + braid->center_x;
352                                                 y_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r2 +
353                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r1)) *
354                                                         SINF(t + psi) + braid->center_y;
355                                                 x_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r2 +
356                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r1)) *
357                                                         COSF(t + t_inc + psi) + braid->center_x;
358                                                 y_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r2 +
359                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r1)) *
360                                                         SINF(t + t_inc + psi) + braid->center_y;
361                                                 if (MI_NPIXELS(mi) > 2)
362                                                         XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
363                                                 else
364                                                         XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
365
366                                                 XDrawLine(display, window, MI_GC(mi),
367                                                           (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
368                                         }
369 #ifdef COLORCOMP
370                                         if (MI_NPIXELS(mi) > 2) {
371                                                 color_use = color + SPINRATE *
372                                                         braid->components[applywordbackto(braid, s + 1, i)] +
373                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
374                                                 while (((int) color_use) >= MI_NPIXELS(mi))
375                                                         color_use -= (float) MI_NPIXELS(mi);
376                                                 while (((int) color_use) < 0)
377                                                         color_use += (float) MI_NPIXELS(mi);
378                                         }
379 #endif
380                                         if (braid->braidword[i] < 0 ||
381                                             (FABSF(t - theta / 2.0) > theta / 7.0)) {
382                                                 x_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r1 +
383                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r2)) *
384                                                         COSF(t + psi) + braid->center_x;
385                                                 y_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r1 +
386                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r2)) *
387                                                         SINF(t + psi) + braid->center_y;
388                                                 x_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r1 +
389                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r2)) *
390                                                         COSF(t + t_inc + psi) + braid->center_x;
391                                                 y_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r1 +
392                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r2)) *
393                                                         SINF(t + t_inc + psi) + braid->center_y;
394                                                 if (MI_NPIXELS(mi) > 2)
395                                                         XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
396                                                 else
397                                                         XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
398
399                                                 XDrawLine(display, window, MI_GC(mi),
400                                                           (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
401                                         }
402                                 } else {
403                                         /* no crosSINFg */
404 #ifdef COLORCOMP
405                                         if (MI_NPIXELS(mi) > 2) {
406                                                 color_use = color + SPINRATE *
407                                                         braid->components[applywordbackto(braid, s, i)] +
408                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
409                                                 while (((int) color_use) >= MI_NPIXELS(mi))
410                                                         color_use -= (float) MI_NPIXELS(mi);
411                                                 while (((int) color_use) < 0)
412                                                         color_use += (float) MI_NPIXELS(mi);
413                                         }
414 #endif
415 #ifdef COLORROUND
416                                         if (MI_NPIXELS(mi) > 2) {
417                                                 color_use += SPINRATE * color_inc;
418                                                 while (((int) color_use) >= MI_NPIXELS(mi))
419                                                         color_use -= (float) MI_NPIXELS(mi);
420                                         }
421 #endif
422                                         r1 = braid->min_radius + r_diff * (float) (s);
423                                         x_1 = r1 * COSF(t + psi) + braid->center_x;
424                                         y_1 = r1 * SINF(t + psi) + braid->center_y;
425                                         x_2 = r1 * COSF(t + t_inc + psi) + braid->center_x;
426                                         y_2 = r1 * SINF(t + t_inc + psi) + braid->center_y;
427                                         if (MI_NPIXELS(mi) > 2)
428                                                 XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
429                                         else
430                                                 XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
431
432                                         XDrawLine(display, window, MI_GC(mi),
433                                                   (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
434                                 }
435                         }
436                 }
437         }
438         XSetLineAttributes(display, MI_GC(mi), 1, LineSolid, CapNotLast, JoinRound);
439
440         if (++braid->age > MI_CYCLES(mi)) {
441 #ifdef STANDALONE
442       braid->eraser = erase_window (MI_DISPLAY(mi), MI_WINDOW(mi), braid->eraser);
443 #else
444                 init_braid(mi);
445 #endif
446         }
447 }
448
449 ENTRYPOINT void
450 reshape_braid(ModeInfo * mi, int width, int height)
451 {
452   XClearWindow (MI_DISPLAY (mi), MI_WINDOW(mi));
453   init_braid (mi);
454 }
455
456 ENTRYPOINT Bool
457 braid_handle_event (ModeInfo *mi, XEvent *event)
458 {
459   if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
460     {
461       reshape_braid (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
462       return True;
463     }
464
465   return False;
466 }
467
468 ENTRYPOINT void
469 refresh_braid(ModeInfo * mi)
470 {
471         MI_CLEARWINDOW(mi);
472 }
473
474 XSCREENSAVER_MODULE ("Braid", braid)
475
476 #endif /* MODE_braid */