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