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