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