ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / hacks / galaxy.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * galaxy --- draw spinning, colliding galaxies.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)galaxy.c      4.02 97/04/01 xlockmore";
6 #endif
7
8 /* Originally done by Uli Siegmund (uli@wombat.okapi.sub.org) on Amiga
9  *   for EGS in Cluster
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  * Port to X11 and incorporation into xlockmore by Hubert Feyrer
23  *   (hubert.feyrer@rz.uni-regensburg.de)
24  *
25  * Revision History:
26  * 10-May-97: jwz@netscape.com: turned into a standalone program.
27  * 18-Apr-97: Memory leak fixed by Tom Schmidt <tschmidt@micron.com>
28  * 07-Apr-97: Modified by Dave Mitchell <davem@magnet.com>
29  * 23-Oct-94: Modified by David Bagley <bagleyd@bigfoot.com>
30  *              random star sizes
31  *              colors change depending on velocity
32  * 10-Oct-94: Add colors by Hubert Feyer
33  * 30-Sep-94: Initial port by Hubert Feyer
34  * 09-Mar-94: VMS can generate a random number 0.0 which results in a
35  *            division by zero, corrected by Jouk Jansen
36  *            <joukj@crys.chem.uva.nl>
37  */
38
39 #ifdef STANDALONE
40 # define PROGCLASS                                      "Galaxy"
41 # define HACK_INIT                                      init_galaxy
42 # define HACK_DRAW                                      draw_galaxy
43 # define galaxy_opts                            xlockmore_opts
44 # define DEFAULTS       "*count:                5    \n"                        \
45                                         "*cycles:               250  \n"                        \
46                                         "*delay:                100  \n"                        \
47                                         "*size:                 3    \n"                        \
48                                         "*ncolors:              64   \n"
49 # define UNIFORM_COLORS
50 # include "xlockmore.h"                         /* from the xscreensaver distribution */
51 #else  /* !STANDALONE */
52 # include "xlock.h"                                     /* from the xlockmore distribution */
53 #endif /* !STANDALONE */
54
55 #define FLOATRAND ((double) LRAND() / ((double) MAXRAND))
56
57 #if 0
58 #define WRAP       1            /* Warp around edges */
59 #define BOUNCE     1            /* Bounce from borders */
60 #endif
61
62 #define MINSIZE       1
63 #define MINGALAXIES    1
64 #define MAX_STARS    300
65 #define MAX_IDELTAT    50
66 /* These come originally from the Cluster-version */
67 #define DEFAULT_GALAXIES  2
68 #define DEFAULT_STARS    1000
69 #define DEFAULT_HITITERATIONS  7500
70 #define DEFAULT_IDELTAT    200  /* 0.02 */
71 #define EPSILON 0.0000001
72
73 #define GALAXYRANGESIZE  0.1
74 #define GALAXYMINSIZE  0.1
75 #define QCONS    0.001
76
77 #define COLORBASE  8
78   /* Colors for stars start here */
79 #define COLORSTEP  (NUMCOLORS/COLORBASE)        /* 8 colors per galaxy */
80
81 #define drawStar(x,y,size) if(size<=1) XDrawPoint(display,window,gc,x,y);\
82   else XFillArc(display,window,gc,x,y,size,size,0,23040)
83
84
85 static Bool tracks;
86
87 #define DEF_TRACKS "False"
88
89 static XrmOptionDescRec opts[] =
90 {
91         {"-tracks", ".galaxy.tracks", XrmoptionNoArg, (caddr_t) "on"},
92         {"+tracks", ".galaxy.tracks", XrmoptionNoArg, (caddr_t) "off"}
93 };
94
95 static argtype vars[] =
96 {
97         {(caddr_t *) & tracks, "tracks", "Tracks", DEF_TRACKS, t_Bool}
98 };
99
100 static OptionStruct desc[] =
101 {
102         {"-/+tracks", "turn on/off star tracks"}
103 };
104
105 ModeSpecOpt galaxy_opts = { 2, opts, 1, vars, desc };
106
107
108 typedef struct {
109         double      pos[3], vel[3];
110         int         px, py;
111         int         color;
112         int         size;
113 } Star;
114
115 typedef struct {
116         int         mass;
117         int         nstars;
118         Star       *stars;
119         double      pos[3], vel[3];
120         int         galcol;
121 } Galaxy;
122
123 typedef struct {
124         struct {
125                 int         left;       /* x minimum */
126                 int         right;      /* x maximum */
127                 int         top;        /* y minimum */
128                 int         bottom;     /* y maximum */
129         } clip;
130         double      mat[3][3];  /* Movement of stars(?) */
131         double      scale;      /* Scale */
132         int         midx;       /* Middle of screen, x */
133         int         midy;       /* Middle of screen, y */
134         double      size;       /* */
135         double      diff[3];    /* */
136         Galaxy     *galaxies;   /* the Whole Universe */
137         int         ngalaxies;  /* # galaxies */
138         double      f_deltat;   /* quality of calculation, calc'd by d_ideltat */
139         int         f_hititerations;    /* # iterations before restart */
140         int         step;       /* */
141 } unistruct;
142
143 static unistruct *universes = NULL;
144
145 static void
146 free_galaxies(unistruct * gp)
147 {
148         if (gp->galaxies != NULL) {
149                 int         i;
150
151                 for (i = 0; i < gp->ngalaxies; i++) {
152                         Galaxy     *gt = &gp->galaxies[i];
153
154                         if (gt->stars != NULL)
155                                 (void) free((void *) gt->stars);
156                 }
157                 (void) free((void *) gp->galaxies);
158                 gp->galaxies = NULL;
159         }
160 }
161
162 static void
163 startover(ModeInfo * mi)
164 {
165         unistruct  *gp = &universes[MI_SCREEN(mi)];
166         int         size = MI_SIZE(mi);
167         int         i, j;       /* more tmp */
168         double      w1, w2;     /* more tmp */
169         double      d, v, w, h; /* yet more tmp */
170
171         gp->step = 0;
172
173         if (MI_BATCHCOUNT(mi) < -MINGALAXIES)
174                 free_galaxies(gp);
175         gp->ngalaxies = MI_BATCHCOUNT(mi);
176         if (gp->ngalaxies < -MINGALAXIES)
177                 gp->ngalaxies = NRAND(-gp->ngalaxies - MINGALAXIES + 1) + MINGALAXIES;
178         else if (gp->ngalaxies < MINGALAXIES)
179                 gp->ngalaxies = MINGALAXIES;
180         if (gp->galaxies == NULL)
181                 gp->galaxies = (Galaxy *) calloc(gp->ngalaxies, sizeof (Galaxy));
182
183         for (i = 0; i < gp->ngalaxies; ++i) {
184                 Galaxy     *gt = &gp->galaxies[i];
185                 double      sinw1, sinw2, cosw1, cosw2;
186
187                 gt->galcol = NRAND(COLORBASE - 2);
188                 if (gt->galcol > 1)
189                         gt->galcol += 2;        /* Mult 8; 16..31 no green stars */
190                 /* Galaxies still may have some green stars but are not all green. */
191
192                 if (gt->stars != NULL) {
193                         (void) free((void *) gt->stars);
194                         gt->stars = NULL;
195                 }
196                 gt->nstars = (NRAND(MAX_STARS / 2)) + MAX_STARS / 2;
197                 gt->stars = (Star *) malloc(gt->nstars * sizeof (Star));
198                 w1 = 2.0 * M_PI * FLOATRAND;
199                 w2 = 2.0 * M_PI * FLOATRAND;
200                 sinw1 = SINF(w1);
201                 sinw2 = SINF(w2);
202                 cosw1 = COSF(w1);
203                 cosw2 = COSF(w2);
204
205                 gp->mat[0][0] = cosw2;
206                 gp->mat[0][1] = -sinw1 * sinw2;
207                 gp->mat[0][2] = cosw1 * sinw2;
208                 gp->mat[1][0] = 0.0;
209                 gp->mat[1][1] = cosw1;
210                 gp->mat[1][2] = sinw1;
211                 gp->mat[2][0] = -sinw2;
212                 gp->mat[2][1] = -sinw1 * cosw2;
213                 gp->mat[2][2] = cosw1 * cosw2;
214
215                 gt->vel[0] = FLOATRAND * 2.0 - 1.0;
216                 gt->vel[1] = FLOATRAND * 2.0 - 1.0;
217                 gt->vel[2] = FLOATRAND * 2.0 - 1.0;
218                 gt->pos[0] = -gt->vel[0] * gp->f_deltat *
219                         gp->f_hititerations + FLOATRAND - 0.5;
220                 gt->pos[1] = -gt->vel[1] * gp->f_deltat *
221                         gp->f_hititerations + FLOATRAND - 0.5;
222                 gt->pos[2] = -gt->vel[2] * gp->f_deltat *
223                         gp->f_hititerations + FLOATRAND - 0.5;
224
225                 gt->mass = (int) (FLOATRAND * 1000.0) + 1;
226
227                 gp->size = GALAXYRANGESIZE * FLOATRAND + GALAXYMINSIZE;
228
229                 for (j = 0; j < gt->nstars; ++j) {
230                         Star       *st = &gt->stars[j];
231                         double      sinw, cosw;
232
233                         w = 2.0 * M_PI * FLOATRAND;
234                         sinw = SINF(w);
235                         cosw = COSF(w);
236                         d = FLOATRAND * gp->size;
237                         h = FLOATRAND * exp(-2.0 * (d / gp->size)) / 5.0 * gp->size;
238                         if (FLOATRAND < 0.5)
239                                 h = -h;
240                         st->pos[0] = gp->mat[0][0] * d * cosw + gp->mat[1][0] * d * sinw +
241                                 gp->mat[2][0] * h + gt->pos[0];
242                         st->pos[1] = gp->mat[0][1] * d * cosw + gp->mat[1][1] * d * sinw +
243                                 gp->mat[2][1] * h + gt->pos[1];
244                         st->pos[2] = gp->mat[0][2] * d * cosw + gp->mat[1][2] * d * sinw +
245                                 gp->mat[2][2] * h + gt->pos[2];
246
247                         v = sqrt(gt->mass * QCONS / sqrt(d * d + h * h));
248                         st->vel[0] = -gp->mat[0][0] * v * sinw + gp->mat[1][0] * v * cosw +
249                                 gt->vel[0];
250                         st->vel[1] = -gp->mat[0][1] * v * sinw + gp->mat[1][1] * v * cosw +
251                                 gt->vel[1];
252                         st->vel[2] = -gp->mat[0][2] * v * sinw + gp->mat[1][2] * v * cosw +
253                                 gt->vel[2];
254
255                         st->px = 0;
256                         st->py = 0;
257
258                         if (size < -MINSIZE)
259                                 st->size = NRAND(-size - MINSIZE + 1) + MINSIZE;
260                         else if (size < MINSIZE)
261                                 st->size = MINSIZE;
262                         else
263                                 st->size = size;
264                 }
265         }
266
267         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
268
269 #if 0
270         (void) printf("ngalaxies=%d, f_hititerations=%d\n",
271                       gp->ngalaxies, gp->f_hititerations);
272         (void) printf("f_deltat=%g\n", gp->f_deltat);
273         (void) printf("Screen: ");
274         (void) printf("%dx%d pixel (%d-%d, %d-%d)\n",
275           (gp->clip.right - gp->clip.left), (gp->clip.bottom - gp->clip.top),
276                gp->clip.left, gp->clip.right, gp->clip.top, gp->clip.bottom);
277 #endif /*0 */
278 }
279
280 void
281 init_galaxy(ModeInfo * mi)
282 {
283         unistruct  *gp;
284
285         if (universes == NULL) {
286                 if ((universes = (unistruct *) calloc(MI_NUM_SCREENS(mi),
287                                                 sizeof (unistruct))) == NULL)
288                         return;
289         }
290         gp = &universes[MI_SCREEN(mi)];
291
292         gp->f_hititerations = MI_CYCLES(mi);
293         gp->f_deltat = ((double) MAX_IDELTAT) / 10000.0;
294
295         gp->clip.left = 0;
296         gp->clip.top = 0;
297         gp->clip.right = MI_WIN_WIDTH(mi);
298         gp->clip.bottom = MI_WIN_HEIGHT(mi);
299
300         gp->scale = (double) (gp->clip.right + gp->clip.bottom) / 8.0;
301         gp->midx = gp->clip.right / 2;
302         gp->midy = gp->clip.bottom / 2;
303         startover(mi);
304 }
305
306 void
307 draw_galaxy(ModeInfo * mi)
308 {
309         Display    *display = MI_DISPLAY(mi);
310         Window      window = MI_WINDOW(mi);
311         GC          gc = MI_GC(mi);
312         unistruct  *gp = &universes[MI_SCREEN(mi)];
313         double      d;          /* tmp */
314         int         i, j, k;    /* more tmp */
315
316         for (i = 0; i < gp->ngalaxies; ++i) {
317                 Galaxy     *gt = &gp->galaxies[i];
318
319                 for (j = 0; j < gp->galaxies[i].nstars; ++j) {
320                         Star       *st = &gt->stars[j];
321
322                         for (k = 0; k < gp->ngalaxies; ++k) {
323                                 Galaxy     *gtk = &gp->galaxies[k];
324
325                                 gp->diff[0] = gtk->pos[0] - st->pos[0];
326                                 gp->diff[1] = gtk->pos[1] - st->pos[1];
327                                 gp->diff[2] = gtk->pos[2] - st->pos[2];
328                                 d = gp->diff[0] * gp->diff[0] + gp->diff[1] * gp->diff[1] +
329                                         gp->diff[2] * gp->diff[2];
330                                 if (d < EPSILON)
331                                         d = EPSILON;
332                                 d = gt->mass / (d * sqrt(d)) * gp->f_deltat * QCONS;
333                                 gp->diff[0] *= d;
334                                 gp->diff[1] *= d;
335                                 gp->diff[2] *= d;
336                                 st->vel[0] += gp->diff[0];
337                                 st->vel[1] += gp->diff[1];
338                                 st->vel[2] += gp->diff[2];
339                         }
340
341                         st->color = COLORSTEP * gt->galcol + ((int) ((st->vel[0] * st->vel[0] +
342                                 st->vel[1] * st->vel[1] + st->vel[2] * st->vel[2]) / 3.0)) % COLORSTEP;
343
344                         st->pos[0] += st->vel[0] * gp->f_deltat;
345                         st->pos[1] += st->vel[1] * gp->f_deltat;
346                         st->pos[2] += st->vel[2] * gp->f_deltat;
347
348                         if (st->px >= gp->clip.left &&
349                             st->px <= gp->clip.right - st->size &&
350                             st->py >= gp->clip.top &&
351                             st->py <= gp->clip.bottom - st->size) {
352                                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
353                                 drawStar(st->px, st->py, st->size);
354                         }
355                         st->px = (int) (st->pos[0] * gp->scale) + gp->midx;
356                         st->py = (int) (st->pos[1] * gp->scale) + gp->midy;
357
358
359 #ifdef WRAP
360                         if (st->px < gp->clip.left) {
361                                 (void) printf("wrap l -> r\n");
362                                 st->px = gp->clip.right;
363                         }
364                         if (st->px > gp->clip.right) {
365                                 (void) printf("wrap r -> l\n");
366                                 st->px = gp->clip.left;
367                         }
368                         if (st->py > gp->clip.bottom) {
369                                 (void) printf("wrap b -> t\n");
370                                 st->py = gp->clip.top;
371                         }
372                         if (st->py < gp->clip.top) {
373                                 (void) printf("wrap t -> b\n");
374                                 st->py = gp->clip.bottom;
375                         }
376 #endif /*WRAP */
377
378
379                         if (st->px >= gp->clip.left &&
380                             st->px <= gp->clip.right - st->size &&
381                             st->py >= gp->clip.top &&
382                             st->py <= gp->clip.bottom - st->size) {
383                                 if (MI_NPIXELS(mi) > 2)
384                                         XSetForeground(display, gc, MI_PIXEL(mi, st->color));
385                                 else
386                                         XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
387                                 if (tracks)
388                                         drawStar(st->px + 1, st->py, st->size);
389                                 else
390                                         drawStar(st->px, st->py, st->size);
391                         }
392                 }
393
394                 for (k = i + 1; k < gp->ngalaxies; ++k) {
395                         Galaxy     *gtk = &gp->galaxies[k];
396
397                         gp->diff[0] = gtk->pos[0] - gt->pos[0];
398                         gp->diff[1] = gtk->pos[1] - gt->pos[1];
399                         gp->diff[2] = gtk->pos[2] - gt->pos[2];
400                         d = gp->diff[0] * gp->diff[0] + gp->diff[1] * gp->diff[1] +
401                                 gp->diff[2] * gp->diff[2];
402                         if (d < EPSILON)
403                                 d = EPSILON;
404                         d = gt->mass * gt->mass / (d * sqrt(d)) * gp->f_deltat * QCONS;
405                         gp->diff[0] *= d;
406                         gp->diff[1] *= d;
407                         gp->diff[2] *= d;
408                         gt->vel[0] += gp->diff[0] / gt->mass;
409                         gt->vel[1] += gp->diff[1] / gt->mass;
410                         gt->vel[2] += gp->diff[2] / gt->mass;
411                         gtk->vel[0] -= gp->diff[0] / gtk->mass;
412                         gtk->vel[1] -= gp->diff[1] / gtk->mass;
413                         gtk->vel[2] -= gp->diff[2] / gtk->mass;
414                 }
415                 gt->pos[0] += gt->vel[0] * gp->f_deltat;
416                 gt->pos[1] += gt->vel[1] * gp->f_deltat;
417                 gt->pos[2] += gt->vel[2] * gp->f_deltat;
418         }
419
420         gp->step++;
421         if (gp->step > gp->f_hititerations * 4)
422                 startover(mi);
423 }
424
425 void
426 release_galaxy(ModeInfo * mi)
427 {
428         if (universes != NULL) {
429                 int         screen;
430
431                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
432                         free_galaxies(&universes[screen]);
433                 (void) free((void *) universes);
434                 universes = NULL;
435         }
436 }
437
438 void
439 refresh_galaxy(ModeInfo * mi)
440 {
441         /* Do nothing, it will refresh by itself */
442 }