135159e36e40f5a5b562a2609d18a4c13602239f
[xscreensaver] / hacks / rotor.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* rotor --- a swirly rotor */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)rotor.c       4.04 97/07/28 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 1991 by Patrick J. Naughton.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 10-May-97:  Compatible with xscreensaver
26  * 08-Mar-95: CAT stuff for ## was tripping up some C compilers.  Removed.
27  * 01-Dec-93: added patch for AIXV3 from Tom McConnell
28  *            <tmcconne@sedona.intel.com>
29  * 11-Nov-90: put into xlock by Steve Zellers <zellers@sun.com>
30  * 16-Oct-90: Received from Tom Lawrence (tcl@cs.brown.edu: 'flight' simulator)
31  */
32
33 #ifdef STANDALONE
34 # define PROGCLASS "Rotor"
35 # define HACK_INIT init_rotor
36 # define HACK_DRAW draw_rotor
37 # define rotor_opts xlockmore_opts
38 # define DEFAULTS       "*delay:   10000 \n"    \
39                                         "*count:   4 \n"                \
40                                         "*size:   -6 \n"                \
41                                         "*cycles:  20 \n"               \
42                                         "*ncolors: 200 \n"
43 # define SMOOTH_COLORS
44 # include "xlockmore.h"         /* in xscreensaver distribution */
45 #else /* STANDALONE */
46 # include "xlock.h"                     /* in xlockmore distribution */
47 #endif /* STANDALONE */
48
49 ModeSpecOpt rotor_opts =
50 {0, NULL, 0, NULL, NULL};
51
52 /*-
53  * A 'batchcount' of 3 or 4 works best!
54  */
55
56 #define MAXANGLE        3000.0  /* irrectangular (jwz: was 10000.0) */
57
58 /* How many segments to draw per cycle when redrawing */
59 #define REDRAWSTEP 3
60
61 typedef struct {
62         float       angle;
63         float       radius;
64         float       start_radius;
65         float       end_radius;
66         float       radius_drift_max;
67         float       radius_drift_now;
68
69         float       ratio;
70         float       start_ratio;
71         float       end_ratio;
72         float       ratio_drift_max;
73         float       ratio_drift_now;
74 } elem;
75
76 typedef struct {
77         int         pix;
78         int         lastx, lasty;
79         int         num, rotor, prev;
80         int         nsave;
81         float       angle;
82         int         centerx, centery;
83         int         prevcenterx, prevcentery;
84         unsigned char firsttime;
85         unsigned char iconifiedscreen;  /* for iconified view */
86         unsigned char forward;
87         unsigned char unused;
88         elem       *elements;
89         XPoint     *save;
90         int         redrawing, redrawpos;
91 } rotorstruct;
92
93 static rotorstruct *rotors = NULL;
94
95 void
96 init_rotor(ModeInfo * mi)
97 {
98         rotorstruct *rp;
99         int         x;
100         elem       *pelem;
101         unsigned char wasiconified;
102
103         if (rotors == NULL) {
104                 if ((rotors = (rotorstruct *) calloc(MI_NUM_SCREENS(mi),
105                                               sizeof (rotorstruct))) == NULL)
106                         return;
107         }
108         rp = &rotors[MI_SCREEN(mi)];
109
110         rp->prevcenterx = rp->centerx;
111         rp->prevcentery = rp->centery;
112
113         rp->centerx = MI_WIN_WIDTH(mi) / 2;
114         rp->centery = MI_WIN_HEIGHT(mi) / 2;
115
116         rp->redrawing = 0;
117         /*
118          * sometimes, you go into iconified view, only to see a really whizzy pattern
119          * that you would like to look more closely at. Normally, clicking in the
120          * icon reinitializes everything - but I don't, cuz I'm that kind of guy.
121          * HENCE, the wasiconified stuff you see here.
122          */
123
124         wasiconified = rp->iconifiedscreen;
125         rp->iconifiedscreen = MI_WIN_IS_ICONIC(mi);
126
127         if (wasiconified && !rp->iconifiedscreen)
128                 rp->firsttime = True;
129         else {
130
131                 /* This is a fudge is needed since prevcenter may not be set when it comes
132                    from the the random mode and return is pressed (and its not the first
133                    mode that was running). This assumes that the size of the lock screen
134                    window / size of the icon window = 12 */
135                 if (!rp->prevcenterx)
136                         rp->prevcenterx = rp->centerx * 12;
137                 if (!rp->prevcentery)
138                         rp->prevcentery = rp->centery * 12;
139
140                 rp->num = MI_BATCHCOUNT(mi);
141                 if (rp->num < 0) {
142                         rp->num = NRAND(-rp->num) + 1;
143                         if (rp->elements != NULL) {
144                                 (void) free((void *) rp->elements);
145                                 rp->elements = NULL;
146                         }
147                 }
148                 if (rp->elements == NULL)
149                         rp->elements = (elem *) calloc(rp->num, sizeof (elem));
150                 rp->nsave = MI_CYCLES(mi);
151                 if (rp->nsave <= 1)
152                         rp->nsave = 2;
153                 if (rp->save == NULL)
154                         rp->save = (XPoint *) calloc(rp->nsave, sizeof (XPoint));
155
156                 pelem = rp->elements;
157
158                 for (x = rp->num; --x >= 0; pelem++) {
159                         pelem->radius_drift_max = 1.0;
160                         pelem->radius_drift_now = 1.0;
161
162                         pelem->end_radius = 100.0;
163
164                         pelem->ratio_drift_max = 1.0;
165                         pelem->ratio_drift_now = 1.0;
166                         pelem->end_ratio = 10.0;
167                 }
168                 if (MI_NPIXELS(mi) > 2)
169                         rp->pix = NRAND(MI_NPIXELS(mi));
170
171                 rp->rotor = 0;
172                 rp->prev = 1;
173                 rp->lastx = rp->centerx;
174                 rp->lasty = rp->centery;
175                 rp->angle = (float) NRAND((long) MAXANGLE) / 3.0;
176                 rp->forward = rp->firsttime = True;
177         }
178
179         {
180           int line_width = MI_SIZE(mi);
181           if (line_width == 0)
182                 line_width = -5;
183           if (line_width < 0)
184                 line_width = NRAND(-line_width)+1;
185           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), line_width,
186                                                  LineSolid, CapButt, JoinMiter);
187         }
188
189         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
190 }
191
192 void
193 draw_rotor(ModeInfo * mi)
194 {
195         Display    *display = MI_DISPLAY(mi);
196         GC          gc = MI_GC(mi);
197         register rotorstruct *rp = &rotors[MI_SCREEN(mi)];
198         register elem *pelem;
199         int         thisx, thisy;
200         int         i;
201         int         x_1, y_1, x_2, y_2;
202
203         if (!rp->iconifiedscreen) {
204                 thisx = rp->centerx;
205                 thisy = rp->centery;
206         } else {
207                 thisx = rp->prevcenterx;
208                 thisy = rp->prevcentery;
209         }
210         for (i = rp->num, pelem = rp->elements; --i >= 0; pelem++) {
211                 if (pelem->radius_drift_max <= pelem->radius_drift_now) {
212                         pelem->start_radius = pelem->end_radius;
213                         pelem->end_radius = (float) NRAND(40000) / 100.0 - 200.0;
214                         pelem->radius_drift_max = (float) NRAND(100000) + 10000.0;
215                         pelem->radius_drift_now = 0.0;
216                 }
217                 if (pelem->ratio_drift_max <= pelem->ratio_drift_now) {
218                         pelem->start_ratio = pelem->end_ratio;
219                         pelem->end_ratio = (float) NRAND(2000) / 100.0 - 10.0;
220                         pelem->ratio_drift_max = (float) NRAND(100000) + 10000.0;
221                         pelem->ratio_drift_now = 0.0;
222                 }
223                 pelem->ratio = pelem->start_ratio +
224                         (pelem->end_ratio - pelem->start_ratio) /
225                         pelem->ratio_drift_max * pelem->ratio_drift_now;
226                 pelem->angle = rp->angle * pelem->ratio;
227                 pelem->radius = pelem->start_radius +
228                         (pelem->end_radius - pelem->start_radius) /
229                         pelem->radius_drift_max * pelem->radius_drift_now;
230
231                 thisx += (int) (COSF(pelem->angle) * pelem->radius);
232                 thisy += (int) (SINF(pelem->angle) * pelem->radius);
233
234                 pelem->ratio_drift_now += 1.0;
235                 pelem->radius_drift_now += 1.0;
236         }
237         if (rp->firsttime)
238                 rp->firsttime = False;
239         else {
240                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
241
242                 x_1 = (int) rp->save[rp->rotor].x;
243                 y_1 = (int) rp->save[rp->rotor].y;
244                 x_2 = (int) rp->save[rp->prev].x;
245                 y_2 = (int) rp->save[rp->prev].y;
246
247                 if (rp->iconifiedscreen) {
248                         x_1 = x_1 * rp->centerx / rp->prevcenterx;
249                         x_2 = x_2 * rp->centerx / rp->prevcenterx;
250                         y_1 = y_1 * rp->centery / rp->prevcentery;
251                         y_2 = y_2 * rp->centery / rp->prevcentery;
252                 }
253                 XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
254
255                 if (MI_NPIXELS(mi) > 2) {
256                         XSetForeground(display, gc, MI_PIXEL(mi, rp->pix));
257                         if (++rp->pix >= MI_NPIXELS(mi))
258                                 rp->pix = 0;
259                 } else
260                         XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
261
262                 x_1 = rp->lastx;
263                 y_1 = rp->lasty;
264                 x_2 = thisx;
265                 y_2 = thisy;
266
267                 if (rp->iconifiedscreen) {
268                         x_1 = x_1 * rp->centerx / rp->prevcenterx;
269                         x_2 = x_2 * rp->centerx / rp->prevcenterx;
270                         y_1 = y_1 * rp->centery / rp->prevcentery;
271                         y_2 = y_2 * rp->centery / rp->prevcentery;
272                 }
273                 XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
274         }
275         rp->save[rp->rotor].x = rp->lastx = thisx;
276         rp->save[rp->rotor].y = rp->lasty = thisy;
277
278         ++rp->rotor;
279         rp->rotor %= rp->nsave;
280         ++rp->prev;
281         rp->prev %= rp->nsave;
282         if (rp->forward) {
283                 rp->angle += 0.01;
284                 if (rp->angle >= MAXANGLE) {
285                         rp->angle = MAXANGLE;
286                         rp->forward = False;
287                 }
288         } else {
289                 rp->angle -= 0.1;
290                 if (rp->angle <= 0) {
291                         rp->angle = 0.0;
292                         rp->forward = True;
293                 }
294         }
295         if (rp->redrawing) {
296                 int         j;
297
298                 for (i = 0; i < REDRAWSTEP; i++) {
299                         j = (rp->rotor - rp->redrawpos + rp->nsave) % rp->nsave;
300
301                         x_1 = (int) rp->save[j].x;
302                         y_1 = (int) rp->save[j].y;
303                         x_2 = (int) rp->save[(j - 1 + rp->nsave) % rp->nsave].x;
304                         y_2 = (int) rp->save[(j - 1 + rp->nsave) % rp->nsave].y;
305
306                         if (rp->iconifiedscreen) {
307                                 x_1 = x_1 * rp->centerx / rp->prevcenterx;
308                                 x_2 = x_2 * rp->centerx / rp->prevcenterx;
309                                 y_1 = y_1 * rp->centery / rp->prevcentery;
310                                 y_2 = y_2 * rp->centery / rp->prevcentery;
311                         }
312                         XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
313
314                         if (++(rp->redrawpos) >= rp->nsave) {
315                                 rp->redrawing = 0;
316                                 break;
317                         }
318                 }
319         }
320 }
321
322 void
323 release_rotor(ModeInfo * mi)
324 {
325         if (rotors != NULL) {
326                 int         screen;
327
328                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
329                         rotorstruct *rp = &rotors[screen];
330
331                         if (rp->elements != NULL)
332                                 (void) free((void *) rp->elements);
333                         if (rp->save != NULL)
334                                 (void) free((void *) rp->save);
335                 }
336                 (void) free((void *) rotors);
337                 rotors = NULL;
338         }
339 }
340
341 void
342 refresh_rotor(ModeInfo * mi)
343 {
344         rotorstruct *rp = &rotors[MI_SCREEN(mi)];
345
346         rp->redrawing = 1;
347         rp->redrawpos = 1;
348 }