From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / rotor.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* rotor --- a swirly rotor */
3
4 #if 0
5 static const char sccsid[] = "@(#)rotor.c       5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1991 by Patrick J. Naughton.
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  * 01-Nov-2000: Allocation checks
25  * 10-May-1997: Compatible with xscreensaver
26  * 08-Mar-1995: CAT stuff for ## was tripping up some C compilers.  Removed.
27  * 01-Dec-1993: added patch for AIXV3 from Tom McConnell
28  *              <tmcconne@sedona.intel.com>
29  * 11-Nov-1990: put into xlock by Steve Zellers <zellers@sun.com>
30  * 16-Oct-1990: Received from Tom Lawrence (tcl@cs.brown.edu: 'flight'
31  *               simulator)
32  */
33
34 #ifdef STANDALONE
35 #define MODE_rotor
36 #define DEFAULTS        "*delay: 10000 \n" \
37                                         "*count: 4 \n" \
38                                         "*cycles: 20 \n" \
39                                         "*size: -6 \n" \
40                                         "*ncolors: 200 \n" \
41                                         "*fpsSolid: true \n" \
42
43 # define SMOOTH_COLORS
44 # define reshape_rotor 0
45 # define rotor_handle_event 0
46 # include "xlockmore.h"         /* in xscreensaver distribution */
47 #else /* STANDALONE */
48 # include "xlock.h"             /* in xlockmore distribution */
49 #endif /* STANDALONE */
50
51 #ifdef MODE_rotor
52
53 ENTRYPOINT ModeSpecOpt rotor_opts =
54 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
55
56 #ifdef USE_MODULES
57 ModStruct   rotor_description =
58 {"rotor", "init_rotor", "draw_rotor", "release_rotor",
59  "refresh_rotor", "init_rotor", (char *) NULL, &rotor_opts,
60  100, 4, 100, -6, 64, 0.3, "",
61  "Shows Tom's Roto-Rooter", 0, NULL};
62
63 #endif
64
65 /*-
66  * A 'batchcount' of 3 or 4 works best!
67  */
68
69 #define MAXANGLE        3000.0  /* irrectangular  (was 10000.0) */
70
71 /* How many segments to draw per cycle when redrawing */
72 #define REDRAWSTEP 3
73
74 typedef struct {
75         float       angle;
76         float       radius;
77         float       start_radius;
78         float       end_radius;
79         float       radius_drift_max;
80         float       radius_drift_now;
81
82         float       ratio;
83         float       start_ratio;
84         float       end_ratio;
85         float       ratio_drift_max;
86         float       ratio_drift_now;
87 } elem;
88
89 typedef struct {
90         int         pix;
91         int         lastx, lasty;
92         int         num, rotor, prev;
93         int         nsave;
94         float       angle;
95         int         centerx, centery;
96         int         prevcenterx, prevcentery;
97         unsigned char firsttime;
98         unsigned char iconifiedscreen;  /* for iconified view */
99         unsigned char forward;
100         unsigned char unused;
101         elem       *elements;
102         XPoint     *save;
103         int         redrawing, redrawpos;
104         int         linewidth;
105 } rotorstruct;
106
107 static rotorstruct *rotors = (rotorstruct *) NULL;
108
109 static void
110 free_rotor(rotorstruct *rp)
111 {
112         if (rp->elements != NULL) {
113                 (void) free((void *) rp->elements);
114                 rp->elements = (elem *) NULL;
115         }
116         if (rp->save != NULL) {
117                 (void) free((void *) rp->save);
118                 rp->save = (XPoint *) NULL;
119         }
120 }
121
122 ENTRYPOINT void
123 init_rotor (ModeInfo * mi)
124 {
125         int         x;
126         elem       *pelem;
127         unsigned char wasiconified;
128         rotorstruct *rp;
129
130         if (rotors == NULL) {
131                 if ((rotors = (rotorstruct *) calloc(MI_NUM_SCREENS(mi),
132                                               sizeof (rotorstruct))) == NULL)
133                         return;
134         }
135         rp = &rotors[MI_SCREEN(mi)];
136
137 #ifdef HAVE_JWXYZ
138     jwxyz_XSetAntiAliasing (MI_DISPLAY(mi), MI_GC(mi),  False);
139 #endif
140
141         rp->prevcenterx = rp->centerx;
142         rp->prevcentery = rp->centery;
143
144         rp->centerx = MI_WIDTH(mi) / 2;
145         rp->centery = MI_HEIGHT(mi) / 2;
146
147         rp->redrawing = 0;
148         /*
149          * sometimes, you go into iconified view, only to see a really whizzy pattern
150          * that you would like to look more closely at. Normally, clicking in the
151          * icon reinitializes everything - but I don't, cuz I'm that kind of guy.
152          * HENCE, the wasiconified stuff you see here.
153          */
154
155         wasiconified = rp->iconifiedscreen;
156         rp->iconifiedscreen = MI_IS_ICONIC(mi);
157
158         if (wasiconified && !rp->iconifiedscreen)
159                 rp->firsttime = True;
160         else {
161
162                 /* This is a fudge is needed since prevcenter may not be set when it comes
163                    from the the random mode and return is pressed (and its not the first
164                    mode that was running). This assumes that the size of the lock screen
165                    window / size of the icon window = 12 */
166                 if (!rp->prevcenterx)
167                         rp->prevcenterx = rp->centerx * 12;
168                 if (!rp->prevcentery)
169                         rp->prevcentery = rp->centery * 12;
170
171                 rp->num = MI_COUNT(mi);
172                 if (rp->num < 0) {
173                         rp->num = NRAND(-rp->num) + 1;
174                         if (rp->elements != NULL) {
175                                 (void) free((void *) rp->elements);
176                                 rp->elements = (elem *) NULL;
177                         }
178                 }
179                 if (rp->elements == NULL)
180                         if ((rp->elements = (elem *) calloc(rp->num,
181                                         sizeof (elem))) == NULL) {
182                                 free_rotor(rp);
183                                 return;
184                         }
185                 rp->nsave = MI_CYCLES(mi);
186                 if (rp->nsave <= 1)
187                         rp->nsave = 2;
188                 if (rp->save == NULL)
189                         if ((rp->save = (XPoint *) malloc(rp->nsave *
190                                         sizeof (XPoint))) == NULL) {
191                                 free_rotor(rp);
192                                 return;
193                         }
194                 for (x = 0; x < rp->nsave; x++) {
195                         rp->save[x].x = rp->centerx;
196                         rp->save[x].y = rp->centery;
197                 }
198
199                 pelem = rp->elements;
200
201                 for (x = rp->num; --x >= 0; pelem++) {
202                         pelem->radius_drift_max = 1.0;
203                         pelem->radius_drift_now = 1.0;
204
205                         pelem->end_radius = 100.0;
206
207                         pelem->ratio_drift_max = 1.0;
208                         pelem->ratio_drift_now = 1.0;
209                         pelem->end_ratio = 10.0;
210                 }
211                 if (MI_NPIXELS(mi) > 2)
212                         rp->pix = NRAND(MI_NPIXELS(mi));
213
214                 rp->rotor = 0;
215                 rp->prev = 1;
216                 rp->lastx = rp->centerx;
217                 rp->lasty = rp->centery;
218                 rp->angle = (float) NRAND((long) MAXANGLE) / 3.0;
219                 rp->forward = rp->firsttime = True;
220         }
221         rp->linewidth = MI_SIZE(mi);
222
223         if (rp->linewidth == 0)
224                 rp->linewidth = 1;
225         if (rp->linewidth < 0)
226                 rp->linewidth = NRAND(-rp->linewidth) + 1;
227
228         MI_CLEARWINDOW(mi);
229 }
230
231 ENTRYPOINT void
232 draw_rotor (ModeInfo * mi)
233 {
234         Display    *display = MI_DISPLAY(mi);
235         GC          gc = MI_GC(mi);
236         register elem *pelem;
237         int         thisx, thisy;
238         int         i;
239         int         x_1, y_1, x_2, y_2;
240         rotorstruct *rp;
241
242         if (rotors == NULL)
243                 return;
244         rp = &rotors[MI_SCREEN(mi)];
245         if (rp->elements == NULL)
246                 return;
247
248         MI_IS_DRAWN(mi) = True;
249         if (!rp->iconifiedscreen) {
250                 thisx = rp->centerx;
251                 thisy = rp->centery;
252         } else {
253                 thisx = rp->prevcenterx;
254                 thisy = rp->prevcentery;
255         }
256         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), rp->linewidth,
257                            LineSolid, CapButt, JoinMiter);
258         for (i = rp->num, pelem = rp->elements; --i >= 0; pelem++) {
259                 if (pelem->radius_drift_max <= pelem->radius_drift_now) {
260                         pelem->start_radius = pelem->end_radius;
261                         pelem->end_radius = (float) NRAND(40000) / 100.0 - 200.0;
262                         pelem->radius_drift_max = (float) NRAND(100000) + 10000.0;
263                         pelem->radius_drift_now = 0.0;
264                 }
265                 if (pelem->ratio_drift_max <= pelem->ratio_drift_now) {
266                         pelem->start_ratio = pelem->end_ratio;
267                         pelem->end_ratio = (float) NRAND(2000) / 100.0 - 10.0;
268                         pelem->ratio_drift_max = (float) NRAND(100000) + 10000.0;
269                         pelem->ratio_drift_now = 0.0;
270                 }
271                 pelem->ratio = pelem->start_ratio +
272                         (pelem->end_ratio - pelem->start_ratio) /
273                         pelem->ratio_drift_max * pelem->ratio_drift_now;
274                 pelem->angle = rp->angle * pelem->ratio;
275                 pelem->radius = pelem->start_radius +
276                         (pelem->end_radius - pelem->start_radius) /
277                         pelem->radius_drift_max * pelem->radius_drift_now;
278
279                 thisx += (int) (COSF(pelem->angle) * pelem->radius);
280                 thisy += (int) (SINF(pelem->angle) * pelem->radius);
281
282                 pelem->ratio_drift_now += 1.0;
283                 pelem->radius_drift_now += 1.0;
284         }
285         if (rp->firsttime)
286                 rp->firsttime = False;
287         else {
288                 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
289
290                 x_1 = (int) rp->save[rp->rotor].x;
291                 y_1 = (int) rp->save[rp->rotor].y;
292                 x_2 = (int) rp->save[rp->prev].x;
293                 y_2 = (int) rp->save[rp->prev].y;
294
295                 if (rp->iconifiedscreen) {
296                         x_1 = x_1 * rp->centerx / rp->prevcenterx;
297                         x_2 = x_2 * rp->centerx / rp->prevcenterx;
298                         y_1 = y_1 * rp->centery / rp->prevcentery;
299                         y_2 = y_2 * rp->centery / rp->prevcentery;
300                 }
301                 XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
302
303                 if (MI_NPIXELS(mi) > 2) {
304                         XSetForeground(display, gc, MI_PIXEL(mi, rp->pix));
305                         if (++rp->pix >= MI_NPIXELS(mi))
306                                 rp->pix = 0;
307                 } else
308                         XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
309
310                 x_1 = rp->lastx;
311                 y_1 = rp->lasty;
312                 x_2 = thisx;
313                 y_2 = thisy;
314
315                 if (rp->iconifiedscreen) {
316                         x_1 = x_1 * rp->centerx / rp->prevcenterx;
317                         x_2 = x_2 * rp->centerx / rp->prevcenterx;
318                         y_1 = y_1 * rp->centery / rp->prevcentery;
319                         y_2 = y_2 * rp->centery / rp->prevcentery;
320                 }
321                 XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
322         }
323         rp->save[rp->rotor].x = rp->lastx = thisx;
324         rp->save[rp->rotor].y = rp->lasty = thisy;
325
326         ++rp->rotor;
327         rp->rotor %= rp->nsave;
328         ++rp->prev;
329         rp->prev %= rp->nsave;
330         if (rp->forward) {
331                 rp->angle += 0.01;
332                 if (rp->angle >= MAXANGLE) {
333                         rp->angle = MAXANGLE;
334                         rp->forward = False;
335                 }
336         } else {
337                 rp->angle -= 0.1;
338                 if (rp->angle <= 0) {
339                         rp->angle = 0.0;
340                         rp->forward = True;
341                 }
342         }
343         if (rp->redrawing) {
344                 int         j;
345
346                 for (i = 0; i < REDRAWSTEP; i++) {
347                         j = (rp->rotor - rp->redrawpos + rp->nsave) % rp->nsave;
348
349                         x_1 = (int) rp->save[j].x;
350                         y_1 = (int) rp->save[j].y;
351                         x_2 = (int) rp->save[(j - 1 + rp->nsave) % rp->nsave].x;
352                         y_2 = (int) rp->save[(j - 1 + rp->nsave) % rp->nsave].y;
353
354                         if (rp->iconifiedscreen) {
355                                 x_1 = x_1 * rp->centerx / rp->prevcenterx;
356                                 x_2 = x_2 * rp->centerx / rp->prevcenterx;
357                                 y_1 = y_1 * rp->centery / rp->prevcentery;
358                                 y_2 = y_2 * rp->centery / rp->prevcentery;
359                         }
360                         XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
361
362                         if (++(rp->redrawpos) >= rp->nsave) {
363                                 rp->redrawing = 0;
364                                 break;
365                         }
366                 }
367         }
368         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
369                            LineSolid, CapButt, JoinMiter);
370 }
371
372 ENTRYPOINT void
373 release_rotor (ModeInfo * mi)
374 {
375         if (rotors != NULL) {
376                 int         screen;
377
378                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
379                         free_rotor(&rotors[screen]);
380                 (void) free((void *) rotors);
381                 rotors = (rotorstruct *) NULL;
382         }
383 }
384
385 ENTRYPOINT void
386 refresh_rotor (ModeInfo * mi)
387 {
388         rotorstruct *rp;
389
390         if (rotors == NULL)
391                 return;
392         rp = &rotors[MI_SCREEN(mi)];
393
394         MI_CLEARWINDOW(mi);
395         rp->redrawing = 1;
396         rp->redrawpos = 1;
397 }
398
399 XSCREENSAVER_MODULE ("Rotor", rotor)
400
401 #endif /* MODE_rotor */