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