ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / grav.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* grav --- planets spinning around a pulsar */
3
4 #if 0
5 static const char sccsid[] = "@(#)grav.c        5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1993 by Greg Boewring <gb@pobox.com>
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  * 11-Jul-1994: color version
27  * 06-Oct-1993: Written by Greg Bowering <gb@pobox.com>
28  */
29
30 #ifdef STANDALONE
31 #define MODE_grav
32 #define PROGCLASS "Grav"
33 #define HACK_INIT init_grav
34 #define HACK_DRAW draw_grav
35 #define grav_opts xlockmore_opts
36 #define DEFAULTS "*delay: 10000 \n" \
37  "*count: 12 \n" \
38  "*ncolors: 64 \n"
39 #define BRIGHT_COLORS
40 #include "xlockmore.h"          /* in xscreensaver distribution */
41 #else /* STANDALONE */
42 #include "xlock.h"              /* in xlockmore distribution */
43
44 #endif /* STANDALONE */
45
46 #ifdef MODE_grav
47
48 #define DEF_DECAY "False"       /* Damping for decaying orbits */
49 #define DEF_TRAIL "False"       /* For trails (works good in mono only) */
50
51 static Bool decay;
52 static Bool trail;
53
54 static XrmOptionDescRec opts[] =
55 {
56         {"-decay", ".grav.decay", XrmoptionNoArg, "on"},
57         {"+decay", ".grav.decay", XrmoptionNoArg, "off"},
58         {"-trail", ".grav.trail", XrmoptionNoArg, "on"},
59         {"+trail", ".grav.trail", XrmoptionNoArg, "off"}
60 };
61 static argtype vars[] =
62 {
63         {&decay, "decay", "Decay", DEF_DECAY, t_Bool},
64         {&trail, "trail", "Trail", DEF_TRAIL, t_Bool}
65 };
66 static OptionStruct desc[] =
67 {
68         {"-/+decay", "turn on/off decaying orbits"},
69         {"-/+trail", "turn on/off trail dots"}
70 };
71
72 ModeSpecOpt grav_opts =
73 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
74
75 #ifdef USE_MODULES
76 ModStruct   grav_description =
77 {"grav", "init_grav", "draw_grav", "release_grav",
78  "refresh_grav", "init_grav", (char *) NULL, &grav_opts,
79  10000, -12, 1, 1, 64, 1.0, "",
80  "Shows orbiting planets", 0, NULL};
81
82 #endif
83
84 #define GRAV                    -0.02   /* Gravitational constant */
85 #define DIST                    16.0
86 #define COLLIDE                 0.0001
87 #define ALMOST                  15.99
88 #define HALF                    0.5
89 /* #define INTRINSIC_RADIUS     200.0 */
90 #define INTRINSIC_RADIUS        ((float) (gp->height/5))
91 #define STARRADIUS              (unsigned int)(gp->height/(2*DIST))
92 #define AVG_RADIUS              (INTRINSIC_RADIUS/DIST)
93 #define RADIUS                  (unsigned int)(INTRINSIC_RADIUS/(POS(Z)+DIST))
94
95 #define XR                      HALF*ALMOST
96 #define YR                      HALF*ALMOST
97 #define ZR                      HALF*ALMOST
98
99 #define VR                      0.04
100
101 #define DIMENSIONS              3
102 #define X                       0
103 #define Y                       1
104 #define Z                       2
105
106 #define DAMP                    0.999999
107 #define MaxA                    0.1     /* Maximum acceleration (w/ damping) */
108
109 #define POS(c) planet->P[c]
110 #define VEL(c) planet->V[c]
111 #define ACC(c) planet->A[c]
112
113 #define Planet(x,y)\
114   if ((x) >= 0 && (y) >= 0 && (x) <= gp->width && (y) <= gp->height) {\
115     if (planet->ri < 2)\
116      XDrawPoint(display, window, gc, (x), (y));\
117     else\
118      XFillArc(display, window, gc,\
119       (x) - planet->ri / 2, (y) - planet->ri / 2, planet->ri, planet->ri,\
120       0, 23040);\
121    }
122
123 #define FLOATRAND(min,max)      ((min)+(LRAND()/MAXRAND)*((max)-(min)))
124
125 typedef struct {
126         double      P[DIMENSIONS], V[DIMENSIONS], A[DIMENSIONS];
127         int         xi, yi, ri;
128         unsigned long colors;
129 } planetstruct;
130
131 typedef struct {
132         int         width, height;
133         int         x, y, sr, nplanets;
134         unsigned long starcolor;
135         planetstruct *planets;
136 } gravstruct;
137
138 static gravstruct *gravs = (gravstruct *) NULL;
139
140 static void
141 init_planet(ModeInfo * mi, planetstruct * planet)
142 {
143         Display    *display = MI_DISPLAY(mi);
144         Window      window = MI_WINDOW(mi);
145         GC          gc = MI_GC(mi);
146         gravstruct *gp = &gravs[MI_SCREEN(mi)];
147
148         if (MI_NPIXELS(mi) > 2)
149                 planet->colors = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
150         else
151                 planet->colors = MI_WHITE_PIXEL(mi);
152         /* Initialize positions */
153         POS(X) = FLOATRAND(-XR, XR);
154         POS(Y) = FLOATRAND(-YR, YR);
155         POS(Z) = FLOATRAND(-ZR, ZR);
156
157         if (POS(Z) > -ALMOST) {
158                 planet->xi = (int)
159                         ((double) gp->width * (HALF + POS(X) / (POS(Z) + DIST)));
160                 planet->yi = (int)
161                         ((double) gp->height * (HALF + POS(Y) / (POS(Z) + DIST)));
162         } else
163                 planet->xi = planet->yi = -1;
164         planet->ri = RADIUS;
165
166         /* Initialize velocities */
167         VEL(X) = FLOATRAND(-VR, VR);
168         VEL(Y) = FLOATRAND(-VR, VR);
169         VEL(Z) = FLOATRAND(-VR, VR);
170
171         /* Draw planets */
172         Planet(planet->xi, planet->yi);
173 }
174
175 static void
176 draw_planet(ModeInfo * mi, planetstruct * planet)
177 {
178         Display    *display = MI_DISPLAY(mi);
179         Window      window = MI_WINDOW(mi);
180         GC          gc = MI_GC(mi);
181         gravstruct *gp = &gravs[MI_SCREEN(mi)];
182         double      D;          /* A distance variable to work with */
183         register unsigned char cmpt;
184
185         D = POS(X) * POS(X) + POS(Y) * POS(Y) + POS(Z) * POS(Z);
186         if (D < COLLIDE)
187                 D = COLLIDE;
188         D = sqrt(D);
189         D = D * D * D;
190         for (cmpt = X; cmpt < DIMENSIONS; cmpt++) {
191                 ACC(cmpt) = POS(cmpt) * GRAV / D;
192                 if (decay) {
193                         if (ACC(cmpt) > MaxA)
194                                 ACC(cmpt) = MaxA;
195                         else if (ACC(cmpt) < -MaxA)
196                                 ACC(cmpt) = -MaxA;
197                         VEL(cmpt) = VEL(cmpt) + ACC(cmpt);
198                         VEL(cmpt) *= DAMP;
199                 } else {
200                         /* update velocity */
201                         VEL(cmpt) = VEL(cmpt) + ACC(cmpt);
202                 }
203                 /* update position */
204                 POS(cmpt) = POS(cmpt) + VEL(cmpt);
205         }
206
207         gp->x = planet->xi;
208         gp->y = planet->yi;
209
210         if (POS(Z) > -ALMOST) {
211                 planet->xi = (int)
212                         ((double) gp->width * (HALF + POS(X) / (POS(Z) + DIST)));
213                 planet->yi = (int)
214                         ((double) gp->height * (HALF + POS(Y) / (POS(Z) + DIST)));
215         } else
216                 planet->xi = planet->yi = -1;
217
218         /* Mask */
219         XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
220         Planet(gp->x, gp->y);
221         if (trail) {
222                 XSetForeground(display, gc, planet->colors);
223                 XDrawPoint(display, MI_WINDOW(mi), gc, gp->x, gp->y);
224         }
225         /* Move */
226         gp->x = planet->xi;
227         gp->y = planet->yi;
228         planet->ri = RADIUS;
229
230         /* Redraw */
231         XSetForeground(display, gc, planet->colors);
232         Planet(gp->x, gp->y);
233 }
234
235 void
236 init_grav(ModeInfo * mi)
237 {
238         Display    *display = MI_DISPLAY(mi);
239         GC          gc = MI_GC(mi);
240         unsigned char ball;
241         gravstruct *gp;
242
243         if (gravs == NULL) {
244                 if ((gravs = (gravstruct *) calloc(MI_NUM_SCREENS(mi),
245                                                sizeof (gravstruct))) == NULL)
246                         return;
247         }
248         gp = &gravs[MI_SCREEN(mi)];
249
250         gp->width = MI_WIDTH(mi);
251         gp->height = MI_HEIGHT(mi);
252
253         gp->sr = STARRADIUS;
254
255         gp->nplanets = MI_COUNT(mi);
256         if (gp->nplanets < 0) {
257                 if (gp->planets) {
258                         (void) free((void *) gp->planets);
259                         gp->planets = (planetstruct *) NULL;
260                 }
261                 gp->nplanets = NRAND(-gp->nplanets) + 1;        /* Add 1 so its not too boring */
262         }
263         if (gp->planets == NULL) {
264                 if ((gp->planets = (planetstruct *) calloc(gp->nplanets,
265                                 sizeof (planetstruct))) == NULL)
266                         return;
267         }
268
269         MI_CLEARWINDOW(mi);
270
271         if (MI_NPIXELS(mi) > 2)
272                 gp->starcolor = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
273         else
274                 gp->starcolor = MI_WHITE_PIXEL(mi);
275         for (ball = 0; ball < (unsigned char) gp->nplanets; ball++)
276                 init_planet(mi, &gp->planets[ball]);
277
278         /* Draw centrepoint */
279         XDrawArc(display, MI_WINDOW(mi), gc,
280                  gp->width / 2 - gp->sr / 2, gp->height / 2 - gp->sr / 2, gp->sr, gp->sr,
281                  0, 23040);
282 }
283
284 void
285 draw_grav(ModeInfo * mi)
286 {
287         Display    *display = MI_DISPLAY(mi);
288         Window      window = MI_WINDOW(mi);
289         GC          gc = MI_GC(mi);
290         register unsigned char ball;
291         gravstruct *gp;
292
293         if (gravs == NULL)
294                         return;
295         gp = &gravs[MI_SCREEN(mi)];
296         if (gp->planets == NULL)
297                 return;
298
299         MI_IS_DRAWN(mi) = True;
300         /* Mask centrepoint */
301         XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
302         XDrawArc(display, window, gc,
303                  gp->width / 2 - gp->sr / 2, gp->height / 2 - gp->sr / 2, gp->sr, gp->sr,
304                  0, 23040);
305
306         /* Resize centrepoint */
307         switch (NRAND(4)) {
308                 case 0:
309                         if (gp->sr < (int) STARRADIUS)
310                                 gp->sr++;
311                         break;
312                 case 1:
313                         if (gp->sr > 2)
314                                 gp->sr--;
315         }
316
317         /* Draw centrepoint */
318         XSetForeground(display, gc, gp->starcolor);
319         XDrawArc(display, window, gc,
320                  gp->width / 2 - gp->sr / 2, gp->height / 2 - gp->sr / 2, gp->sr, gp->sr,
321                  0, 23040);
322
323         for (ball = 0; ball < (unsigned char) gp->nplanets; ball++)
324                 draw_planet(mi, &gp->planets[ball]);
325 }
326
327 void
328 release_grav(ModeInfo * mi)
329 {
330         if (gravs != NULL) {
331                 int         screen;
332
333                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
334                         gravstruct *gp = &gravs[screen];
335
336                         if (gp->planets)
337                                 (void) free((void *) gp->planets);
338                 }
339                 (void) free((void *) gravs);
340                 gravs = (gravstruct *) NULL;
341         }
342 }
343
344 void
345 refresh_grav(ModeInfo * mi)
346 {
347         MI_CLEARWINDOW(mi);
348 }
349
350 #endif /* MODE_grav */