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