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