10d319a647fc29ac0f2df11f91e1b71c26641a12
[xscreensaver] / hacks / spiral.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * spiral --- low cpu screen design.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)spiral.c      4.00 97/01/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 1994 Darrick Brown.
9  *
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Idea based on a graphics demo I saw a *LONG* time ago.
23  *
24  * See xlock.c for copying information.
25  *
26  * Revision History:
27  * 10-May-97: jwz@jwz.org: turned into a standalone program.
28  * 24-Jul-95: Fix to allow cycles not to have an arbitrary value by
29  *            Peter Schmitzberger (schmitz@coma.sbg.ac.at).
30  * 06-Mar-95: Finished cleaning up and final testing.
31  * Copyright (c) 1994 by Darrick Brown.
32  *
33  * 03-Mar-95: Cleaned up code.
34  * 12-Jul-94: Written.
35  */
36
37 #ifdef STANDALONE
38 # define PROGCLASS                                      "Spiral"
39 # define HACK_INIT                                      init_spiral
40 # define HACK_DRAW                                      draw_spiral
41 # define spiral_opts                            xlockmore_opts
42 # define DEFAULTS       "*count:                40    \n"                       \
43                                         "*cycles:               350   \n"                       \
44                                         "*delay:                50000 \n"                       \
45                                         "*ncolors:              64   \n"
46 # define SMOOTH_COLORS
47 # include "xlockmore.h"                         /* from the xscreensaver distribution */
48 #else  /* !STANDALONE */
49 # include "xlock.h"                                     /* from the xlockmore distribution */
50 #endif /* !STANDALONE */
51
52 ModeSpecOpt spiral_opts = {
53   0, NULL, 0, NULL, NULL };
54
55 #define MAXTRAIL 512            /* The length of the trail */
56 #define MAXDOTS 40
57 #define MINDOTS 1
58 #define TWOPI (2.0*M_PI)        /* for convienence */
59 #define JAGGINESS 4             /* This sets the "Craziness" of the spiral (I like 4) */
60 #define SPEED 2.0
61
62 /* How many segments to draw per cycle when redrawing */
63 #define REDRAWSTEP 3
64
65 typedef struct {
66         float       hx, hy, ha, hr;
67 } Traildots;
68
69 typedef struct {
70         Traildots  *traildots;
71         float       cx, cy;
72         float       angle;
73         float       radius;
74         float       dr, da;
75         float       dx, dy;
76         int         erase;
77         int         inc;
78         float       colors;
79         int         width, height;
80         float       top, bottom, left, right;
81         int         dots, nlength;
82         int         redrawing, redrawpos;
83 } spiralstruct;
84
85 static spiralstruct *spirals = NULL;
86
87 static void draw_dots(ModeInfo * mi, int in);
88
89 #define TFX(sp,x) ((int)((x/sp->right)*(float)sp->width))
90 #define TFY(sp,y) ((int)((y/sp->top)*(float)sp->height))
91
92 static void
93 draw_dots(ModeInfo * mi, int in)
94 {
95
96         float       i, inc;
97         float       x, y;
98
99         spiralstruct *sp = &spirals[MI_SCREEN(mi)];
100
101         inc = TWOPI / (float) sp->dots;
102         for (i = 0.0; i < TWOPI; i += inc) {
103                 x = sp->traildots[in].hx + COSF(i + sp->traildots[in].ha) *
104                         sp->traildots[in].hr;
105                 y = sp->traildots[in].hy + SINF(i + sp->traildots[in].ha) *
106                         sp->traildots[in].hr;
107                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
108                            TFX(sp, x), TFY(sp, y));
109         }
110 }
111
112 void
113 init_spiral(ModeInfo * mi)
114 {
115         spiralstruct *sp;
116         int         i;
117
118         if (spirals == NULL) {
119                 if ((spirals = (spiralstruct *) calloc(MI_NUM_SCREENS(mi),
120                                              sizeof (spiralstruct))) == NULL)
121                         return;
122         }
123         sp = &spirals[MI_SCREEN(mi)];
124
125         sp->width = MI_WIN_WIDTH(mi);
126         sp->height = MI_WIN_HEIGHT(mi);
127
128         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
129
130         /* Init */
131         sp->nlength = MI_CYCLES(mi);
132
133         if (!sp->traildots)
134                 sp->traildots = (Traildots *) malloc(sp->nlength * sizeof (Traildots));
135
136         /* initialize the allocated array */
137         for (i = 0; i < sp->nlength; i++) {
138                 sp->traildots[i].hx = 0.0;
139                 sp->traildots[i].hy = 0.0;
140                 sp->traildots[i].ha = 0.0;
141                 sp->traildots[i].hr = 0.0;
142         }
143         sp->redrawing = 0;
144
145         /* keep the window parameters proportional */
146         sp->top = 10000.0;
147         sp->bottom = 0;
148         sp->right = (float) (sp->width) / (float) (sp->height) * (10000.0);
149         sp->left = 0;
150
151         /* assign the initial values */
152         sp->cx = (float) (5000.0 - NRAND(2000)) / 10000.0 * sp->right;
153         sp->cy = (float) (5000.0 - NRAND(2000));
154         sp->radius = (float) (NRAND(200) + 200);
155         sp->angle = 0.0;
156         sp->dx = (float) (10 - NRAND(20)) * SPEED;
157         sp->dy = (float) (10 - NRAND(20)) * SPEED;
158         sp->dr = (float) ((NRAND(10) + 4) * (1 - (LRAND() & 1) * 2));
159         sp->da = (float) NRAND(360) / 7200.0 + 0.01;
160         if (MI_NPIXELS(mi) > 2)
161                 sp->colors = (float) NRAND(MI_NPIXELS(mi));
162         sp->erase = 0;
163         sp->inc = 0;
164         sp->traildots[sp->inc].hx = sp->cx;
165         sp->traildots[sp->inc].hy = sp->cy;
166         sp->traildots[sp->inc].ha = sp->angle;
167         sp->traildots[sp->inc].hr = sp->radius;
168         sp->inc++;
169
170         sp->dots = MI_BATCHCOUNT(mi);
171         if (sp->dots < -MINDOTS)
172                 sp->dots = NRAND(sp->dots - MINDOTS + 1) + MINDOTS;
173         /* Absolute minimum */
174         if (sp->dots < MINDOTS)
175                 sp->dots = MINDOTS;
176 }
177
178 void
179 draw_spiral(ModeInfo * mi)
180 {
181         Display    *display = MI_DISPLAY(mi);
182         GC          gc = MI_GC(mi);
183         spiralstruct *sp = &spirals[MI_SCREEN(mi)];
184         int         i, j;
185
186         if (sp->erase == 1) {
187                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
188                 draw_dots(mi, sp->inc);
189         }
190         sp->cx += sp->dx;
191         sp->traildots[sp->inc].hx = sp->cx;
192
193         if ((sp->cx > 9000.0) || (sp->cx < 1000.0))
194                 sp->dx *= -1.0;
195
196         sp->cy += sp->dy;
197         sp->traildots[sp->inc].hy = sp->cy;
198
199         if ((sp->cy > 9000.0) || (sp->cy < 1000.0))
200                 sp->dy *= -1.0;
201
202         sp->radius += sp->dr;
203         sp->traildots[sp->inc].hr = sp->radius;
204
205         if ((sp->radius > 2500.0) && (sp->dr > 0.0))
206                 sp->dr *= -1.0;
207         else if ((sp->radius < 50.0) && (sp->radius < 0.0))
208                 sp->dr *= -1.0;
209
210         /* Randomly give some variations to:  */
211
212         /* spiral direction (if it is within the boundaries) */
213         if ((NRAND(3000) < 1 * JAGGINESS) &&
214             (((sp->cx > 2000.0) && (sp->cx < 8000.0)) &&
215              ((sp->cy > 2000.0) && (sp->cy < 8000.0)))) {
216                 sp->dx = (float) (10 - NRAND(20)) * SPEED;
217                 sp->dy = (float) (10 - NRAND(20)) * SPEED;
218         }
219         /* The speed of the change in size of the spiral */
220         if (NRAND(3000) < 1 * JAGGINESS) {
221                 if (LRAND() & 1)
222                         sp->dr += (float) (NRAND(3) + 1);
223                 else
224                         sp->dr -= (float) (NRAND(3) + 1);
225
226                 /* don't let it get too wild */
227                 if (sp->dr > 18.0)
228                         sp->dr = 18.0;
229                 else if (sp->dr < 4.0)
230                         sp->dr = 4.0;
231         }
232         /* The speed of rotation */
233         if (NRAND(3000) < 1 * JAGGINESS)
234                 sp->da = (float) NRAND(360) / 7200.0 + 0.01;
235
236         /* Reverse rotation */
237         if (NRAND(3000) < 1 * JAGGINESS)
238                 sp->da *= -1.0;
239
240         sp->angle += sp->da;
241         sp->traildots[sp->inc].ha = sp->angle;
242
243         if (sp->angle > TWOPI)
244                 sp->angle -= TWOPI;
245         else if (sp->angle < 0.0)
246                 sp->angle += TWOPI;
247
248         sp->colors += (float) MI_NPIXELS(mi) / ((float) (2 * sp->nlength));
249         if (sp->colors >= (float) MI_NPIXELS(mi))
250                 sp->colors = 0.0;
251
252         if (MI_NPIXELS(mi) > 2)
253                 XSetForeground(display, gc, MI_PIXEL(mi, (int) sp->colors));
254         else
255                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
256         draw_dots(mi, sp->inc);
257         sp->inc++;
258
259         if (sp->inc > sp->nlength - 1) {
260                 sp->inc -= sp->nlength;
261                 sp->erase = 1;
262         }
263         if (sp->redrawing) {
264                 for (i = 0; i < REDRAWSTEP; i++) {
265                         j = (sp->inc - sp->redrawpos + sp->nlength) % sp->nlength;
266                         draw_dots(mi, j);
267
268                         if (++(sp->redrawpos) >= sp->nlength) {
269                                 sp->redrawing = 0;
270                                 break;
271                         }
272                 }
273         }
274 }
275
276 void
277 release_spiral(ModeInfo * mi)
278 {
279         if (spirals != NULL) {
280                 int         screen;
281
282                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
283                         spiralstruct *sp = &spirals[screen];
284
285                         if (sp->traildots)
286                                 (void) free((void *) sp->traildots);
287                 }
288                 (void) free((void *) spirals);
289                 spirals = NULL;
290         }
291 }
292
293 void
294 refresh_spiral(ModeInfo * mi)
295 {
296         spiralstruct *sp = &spirals[MI_SCREEN(mi)];
297
298         sp->redrawing = 1;
299         sp->redrawpos = 0;
300 }