From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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 # include "xlockmore.h"
45 # include "erase.h"
46 #else /* STANDALONE */
47 # include "xlock.h"
48 # define ENTRYPOINT /**/
49 #endif /* STANDALONE */
50
51 #ifdef MODE_braid
52
53 ENTRYPOINT ModeSpecOpt braid_opts = {0, NULL, 0, NULL, NULL};
54
55 #ifdef USE_MODULES
56 ModStruct   braid_description =
57 {"braid", "init_braid", "draw_braid", "release_braid",
58  "refresh_braid", "init_braid", (char *) NULL, &braid_opts,
59  1000, 15, 100, 1, 64, 1.0, "",
60  "Shows random braids and knots", 0, NULL};
61
62 #endif
63
64 #if defined( COLORROUND ) && defined( COLORCOMP )
65 #undef COLORROUND
66 #undef COLORCOMP
67 #endif
68
69 #if !defined( COLORROUND ) && !defined( COLORCOMP )
70 #if 0
71 /* to color in a circular pattern use COLORROUND */
72 #define COLORROUND
73 #else
74 /* to color by component use COLORCOMP */
75 #define COLORCOMP
76 #endif
77 #endif
78
79 #define MAXLENGTH  50           /* the maximum length of a braid word */
80 #define MINLENGTH  8            /* the minimum length of a braid word */
81 #define MAXSTRANDS  15          /* the maximum number of strands in the braid */
82 #define MINSTRANDS  3           /* the minimum number of strands in the braid */
83 #define SPINRATE  12.0          /* the rate at which the colors spin */
84
85 #define INTRAND(min,max) (NRAND((max+1)-(min))+(min))
86 #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min)))
87
88 typedef struct {
89         int         linewidth;
90         int         braidword[MAXLENGTH];
91         int         components[MAXSTRANDS];
92         int         startcomp[MAXLENGTH][MAXSTRANDS];
93         int         nstrands;
94         int         braidlength;
95         float       startcolor;
96         int         center_x;
97         int         center_y;
98         float       min_radius;
99         float       max_radius;
100         float       top, bottom, left, right;
101         int         age;
102         int         color_direction;
103 #ifdef STANDALONE
104   eraser_state *eraser;
105 #endif
106 } braidtype;
107
108 static braidtype *braids = (braidtype *) NULL;
109
110 static int
111 applyword(braidtype * braid, int string, int position)
112 {
113         int         i, c;
114
115         c = string;
116         for (i = position; i < braid->braidlength; i++) {
117                 if (c == ABS(braid->braidword[i]))
118                         c--;
119                 else if (c == ABS(braid->braidword[i]) - 1)
120                         c++;
121         }
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         return c;
129 }
130
131 #if 0
132 static int
133 applywordto(braidtype * braid, int string, int position)
134 {
135         int         i, c;
136
137         c = string;
138         for (i = 0; i < position; i++) {
139                 if (c == ABS(braid->braidword[i])) {
140                         c--;
141                 } else if (c == ABS(braid->braidword[i]) - 1) {
142                         c++;
143                 }
144         }
145         return c;
146 }
147 #endif
148
149 static int
150 applywordbackto(braidtype * braid, int string, int position)
151 {
152         int         i, c;
153
154         c = string;
155         for (i = position - 1; i >= 0; i--) {
156                 if (c == ABS(braid->braidword[i])) {
157                         c--;
158                 } else if (c == ABS(braid->braidword[i]) - 1) {
159                         c++;
160                 }
161         }
162         return c;
163 }
164
165 ENTRYPOINT void
166 init_braid(ModeInfo * mi)
167 {
168         braidtype  *braid;
169         int         used[MAXSTRANDS];
170         int         i, count, comp, c;
171         float       min_length;
172
173         if (braids == NULL) {
174                 if ((braids = (braidtype *) calloc(MI_NUM_SCREENS(mi),
175                                                 sizeof (braidtype))) == NULL)
176                         return;
177         }
178         braid = &braids[MI_SCREEN(mi)];
179
180         braid->center_x = MI_WIDTH(mi) / 2;
181         braid->center_y = MI_HEIGHT(mi) / 2;
182         braid->age = 0;
183
184         /* jwz: go in the other direction sometimes. */
185         braid->color_direction = ((LRAND() & 1) ? 1 : -1);
186
187 #ifndef STANDALONE
188         MI_CLEARWINDOW(mi);
189 #endif
190
191         min_length = (braid->center_x > braid->center_y) ?
192                 braid->center_y : braid->center_x;
193         braid->min_radius = min_length * 0.30;
194         braid->max_radius = min_length * 0.90;
195
196         if (MI_COUNT(mi) < MINSTRANDS)
197                 braid->nstrands = MINSTRANDS;
198         else
199                 braid->nstrands = INTRAND(MINSTRANDS,
200                                        MAX(MIN(MIN(MAXSTRANDS, MI_COUNT(mi)),
201                                                (int) ((braid->max_radius - braid->min_radius) / 5.0)), MINSTRANDS));
202         braid->braidlength = INTRAND(MINLENGTH, MIN(MAXLENGTH -1, braid->nstrands * 6));
203
204         for (i = 0; i < braid->braidlength; i++) {
205                 braid->braidword[i] =
206                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
207                 if (i > 0)
208                         while (braid->braidword[i] == -braid->braidword[i - 1])
209                                 braid->braidword[i] = INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
210         }
211
212         while (braid->braidword[0] == -braid->braidword[braid->braidlength - 1])
213                 braid->braidword[braid->braidlength - 1] =
214                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
215
216         do {
217                 (void) memset((char *) used, 0, sizeof (used));
218                 count = 0;
219                 for (i = 0; i < braid->braidlength; i++)
220                         used[ABS(braid->braidword[i])]++;
221                 for (i = 0; i < braid->nstrands; i++)
222                         count += (used[i] > 0) ? 1 : 0;
223                 if (count < braid->nstrands - 1) {
224                         braid->braidword[braid->braidlength] =
225                                 INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
226                         while (braid->braidword[braid->braidlength] ==
227                                -braid->braidword[braid->braidlength - 1] &&
228                                braid->braidword[0] == -braid->braidword[braid->braidlength])
229                                 braid->braidword[braid->braidlength] =
230                                         INTRAND(1, braid->nstrands - 1) * (INTRAND(1, 2) * 2 - 3);
231                         braid->braidlength++;
232                 }
233         } while (count < braid->nstrands - 1 && braid->braidlength < MAXLENGTH);
234
235         braid->startcolor = (MI_NPIXELS(mi) > 2) ?
236                 (float) NRAND(MI_NPIXELS(mi)) : 0.0;
237         /* XSetLineAttributes (display, MI_GC(mi), 2, LineSolid, CapRound,
238            JoinRound); */
239
240         (void) memset((char *) braid->components, 0, sizeof (braid->components));
241         c = 1;
242         comp = 0;
243         braid->components[0] = 1;
244         do {
245                 i = comp;
246                 do {
247                         i = applyword(braid, i, 0);
248                         braid->components[i] = braid->components[comp];
249                 } while (i != comp);
250                 count = 0;
251                 for (i = 0; i < braid->nstrands; i++)
252                         if (braid->components[i] == 0)
253                                 count++;
254                 if (count > 0) {
255                         for (comp = 0; braid->components[comp] != 0; comp++);
256                         braid->components[comp] = ++c;
257                 }
258         } while (count > 0);
259
260         braid->linewidth = MI_SIZE(mi);
261
262         if (braid->linewidth < 0)
263                 braid->linewidth = NRAND(-braid->linewidth) + 1;
264         if (braid->linewidth * braid->linewidth * 8 > MIN(MI_WIDTH(mi), MI_HEIGHT(mi)))
265        braid->linewidth = MIN(1, (int) sqrt((double) MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) / 8));
266         for (i = 0; i < braid->nstrands; i++)
267                 if (!(braid->components[i] & 1))
268                         braid->components[i] *= -1;
269 }
270
271 ENTRYPOINT void
272 draw_braid(ModeInfo * mi)
273 {
274         Display    *display = MI_DISPLAY(mi);
275         Window      window = MI_WINDOW(mi);
276         int         num_points = 500;
277         float       t_inc;
278         float       theta, psi;
279         float       t, r_diff;
280         int         i, s;
281         float       x_1, y_1, x_2, y_2, r1, r2;
282         float       color, color_use = 0.0, color_inc;
283         braidtype  *braid;
284
285         if (braids == NULL)
286                 return;
287         braid = &braids[MI_SCREEN(mi)];
288
289 #ifdef STANDALONE
290     if (braid->eraser) {
291       braid->eraser = erase_window (MI_DISPLAY(mi), MI_WINDOW(mi), braid->eraser);
292       return;
293     }
294 #endif
295
296         MI_IS_DRAWN(mi) = True;
297         XSetLineAttributes(display, MI_GC(mi), braid->linewidth,
298                            LineSolid,
299                            (braid->linewidth <= 3 ? CapButt : CapRound),
300                            JoinMiter);
301
302         theta = (2.0 * M_PI) / (float) (braid->braidlength);
303         t_inc = (2.0 * M_PI) / (float) num_points;
304         color_inc = (float) MI_NPIXELS(mi) * braid->color_direction /
305                 (float) num_points;
306         braid->startcolor += SPINRATE * color_inc;
307         if (((int) braid->startcolor) >= MI_NPIXELS(mi))
308                 braid->startcolor = 0.0;
309
310         r_diff = (braid->max_radius - braid->min_radius) / (float) (braid->nstrands);
311
312         color = braid->startcolor;
313         psi = 0.0;
314         for (i = 0; i < braid->braidlength; i++) {
315                 psi += theta;
316                 for (t = 0.0; t < theta; t += t_inc) {
317 #ifdef COLORROUND
318                         color += color_inc;
319                         if (((int) color) >= MI_NPIXELS(mi))
320                                 color = 0.0;
321                         color_use = color;
322 #endif
323                         for (s = 0; s < braid->nstrands; s++) {
324                                 if (ABS(braid->braidword[i]) == s)
325                                         continue;
326                                 if (ABS(braid->braidword[i]) - 1 == s) {
327                                         /* crosSINFg */
328 #ifdef COLORCOMP
329                                         if (MI_NPIXELS(mi) > 2) {
330                                                 color_use = color + SPINRATE *
331                                                         braid->components[applywordbackto(braid, s, i)] +
332                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
333                                                 while (((int) color_use) >= MI_NPIXELS(mi))
334                                                         color_use -= (float) MI_NPIXELS(mi);
335                                                 while (((int) color_use) < 0)
336                                                         color_use += (float) MI_NPIXELS(mi);
337                                         }
338 #endif
339 #ifdef COLORROUND
340                                         if (MI_NPIXELS(mi) > 2) {
341                                                 color_use += SPINRATE * color_inc;
342                                                 while (((int) color_use) >= MI_NPIXELS(mi))
343                                                         color_use -= (float) MI_NPIXELS(mi);
344                                         }
345 #endif
346                                         r1 = braid->min_radius + r_diff * (float) (s);
347                                         r2 = braid->min_radius + r_diff * (float) (s + 1);
348                                         if (braid->braidword[i] > 0 ||
349                                             (FABSF(t - theta / 2.0) > theta / 7.0)) {
350                                                 x_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r2 +
351                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r1)) *
352                                                         COSF(t + psi) + braid->center_x;
353                                                 y_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r2 +
354                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r1)) *
355                                                         SINF(t + psi) + braid->center_y;
356                                                 x_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r2 +
357                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r1)) *
358                                                         COSF(t + t_inc + psi) + braid->center_x;
359                                                 y_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r2 +
360                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r1)) *
361                                                         SINF(t + t_inc + psi) + braid->center_y;
362                                                 if (MI_NPIXELS(mi) > 2)
363                                                         XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
364                                                 else
365                                                         XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
366
367                                                 XDrawLine(display, window, MI_GC(mi),
368                                                           (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
369                                         }
370 #ifdef COLORCOMP
371                                         if (MI_NPIXELS(mi) > 2) {
372                                                 color_use = color + SPINRATE *
373                                                         braid->components[applywordbackto(braid, s + 1, i)] +
374                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
375                                                 while (((int) color_use) >= MI_NPIXELS(mi))
376                                                         color_use -= (float) MI_NPIXELS(mi);
377                                                 while (((int) color_use) < 0)
378                                                         color_use += (float) MI_NPIXELS(mi);
379                                         }
380 #endif
381                                         if (braid->braidword[i] < 0 ||
382                                             (FABSF(t - theta / 2.0) > theta / 7.0)) {
383                                                 x_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r1 +
384                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r2)) *
385                                                         COSF(t + psi) + braid->center_x;
386                                                 y_1 = ((0.5 * (1.0 + SINF(t / theta * M_PI - M_PI_2)) * r1 +
387                                                         0.5 * (1.0 + SINF((theta - t) / theta * M_PI - M_PI_2)) * r2)) *
388                                                         SINF(t + psi) + braid->center_y;
389                                                 x_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r1 +
390                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r2)) *
391                                                         COSF(t + t_inc + psi) + braid->center_x;
392                                                 y_2 = ((0.5 * (1.0 + SINF((t + t_inc) / theta * M_PI - M_PI_2)) * r1 +
393                                                         0.5 * (1.0 + SINF((theta - t - t_inc) / theta * M_PI - M_PI_2)) * r2)) *
394                                                         SINF(t + t_inc + psi) + braid->center_y;
395                                                 if (MI_NPIXELS(mi) > 2)
396                                                         XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
397                                                 else
398                                                         XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
399
400                                                 XDrawLine(display, window, MI_GC(mi),
401                                                           (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
402                                         }
403                                 } else {
404                                         /* no crosSINFg */
405 #ifdef COLORCOMP
406                                         if (MI_NPIXELS(mi) > 2) {
407                                                 color_use = color + SPINRATE *
408                                                         braid->components[applywordbackto(braid, s, i)] +
409                                                         (psi + t) / 2.0 / M_PI * (float) MI_NPIXELS(mi);
410                                                 while (((int) color_use) >= MI_NPIXELS(mi))
411                                                         color_use -= (float) MI_NPIXELS(mi);
412                                                 while (((int) color_use) < 0)
413                                                         color_use += (float) MI_NPIXELS(mi);
414                                         }
415 #endif
416 #ifdef COLORROUND
417                                         if (MI_NPIXELS(mi) > 2) {
418                                                 color_use += SPINRATE * color_inc;
419                                                 while (((int) color_use) >= MI_NPIXELS(mi))
420                                                         color_use -= (float) MI_NPIXELS(mi);
421                                         }
422 #endif
423                                         r1 = braid->min_radius + r_diff * (float) (s);
424                                         x_1 = r1 * COSF(t + psi) + braid->center_x;
425                                         y_1 = r1 * SINF(t + psi) + braid->center_y;
426                                         x_2 = r1 * COSF(t + t_inc + psi) + braid->center_x;
427                                         y_2 = r1 * SINF(t + t_inc + psi) + braid->center_y;
428                                         if (MI_NPIXELS(mi) > 2)
429                                                 XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, (int) color_use));
430                                         else
431                                                 XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
432
433                                         XDrawLine(display, window, MI_GC(mi),
434                                                   (int) (x_1), (int) (y_1), (int) (x_2), (int) (y_2));
435                                 }
436                         }
437                 }
438         }
439         XSetLineAttributes(display, MI_GC(mi), 1, LineSolid, CapNotLast, JoinRound);
440
441         if (++braid->age > MI_CYCLES(mi)) {
442 #ifdef STANDALONE
443       braid->eraser = erase_window (MI_DISPLAY(mi), MI_WINDOW(mi), braid->eraser);
444 #endif
445                 init_braid(mi);
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 release_braid(ModeInfo * mi)
470 {
471         if (braids != NULL) {
472                 (void) free((void *) braids);
473                 braids = (braidtype *) NULL;
474         }
475 }
476
477 ENTRYPOINT void
478 refresh_braid(ModeInfo * mi)
479 {
480         MI_CLEARWINDOW(mi);
481 }
482
483 XSCREENSAVER_MODULE ("Braid", braid)
484
485 #endif /* MODE_braid */