From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / galaxy.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* galaxy --- spinning galaxies */
3 /* #include<math.h>*/
4 #if 0
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  * 26-Aug-00: robert.nagtegaal@phil.uu.nl and roland@tschai.demon.nl:
28  *            various improvements
29  * 10-May-97: jwz@jwz.org: turned into a standalone program.
30  * 18-Apr-97: Memory leak fixed by Tom Schmidt <tschmidt@micron.com>
31  * 07-Apr-97: Modified by Dave Mitchell <davem@magnet.com>
32  * 23-Oct-94: Modified by David Bagley <bagleyd@bigfoot.com>
33  *  random star sizes
34  *  colors change depending on velocity
35  * 10-Oct-94: Add colors by Hubert Feyer
36  * 30-Sep-94: Initial port by Hubert Feyer
37  * 09-Mar-94: VMS can generate a random number 0.0 which results in a
38  *            division by zero, corrected by Jouk Jansen
39  *            <joukj@crys.chem.uva.nl>
40  */
41
42 #ifdef STANDALONE
43 # define DEFAULTS       "*delay:  20000  \n"   \
44                                         "*count:  -5     \n"   \
45                                         "*cycles:  250   \n"   \
46                                         "*ncolors:  64   \n" \
47                                         "*fpsSolid:  true   \n" \
48                                         "*ignoreRotation: True \n" \
49
50 # define UNIFORM_COLORS
51 # define galaxy_handle_event 0
52 # include "xlockmore.h"    /* from the xscreensaver distribution */
53 #else  /* !STANDALONE */
54 # include "xlock.h"     /* from the xlockmore distribution */
55 #endif /* !STANDALONE */
56
57 static Bool tracks;
58 static Bool spin;
59 static Bool dbufp;
60
61 #define DEF_TRACKS "True"
62 #define DEF_SPIN   "True"
63 #define DEF_DBUF   "True"
64
65 static XrmOptionDescRec opts[] =
66 {
67  {"-tracks", ".galaxy.tracks", XrmoptionNoArg, "on"},
68  {"+tracks", ".galaxy.tracks", XrmoptionNoArg, "off"},
69  {"-spin",   ".galaxy.spin",   XrmoptionNoArg, "on"},
70  {"+spin",   ".galaxy.spin",   XrmoptionNoArg, "off"},
71  {"-dbuf",   ".galaxy.dbuf",   XrmoptionNoArg, "on"},
72  {"+dbuf",   ".galaxy.dbuf",   XrmoptionNoArg, "off"},
73 };
74
75 static argtype vars[] =
76 {
77  {&tracks, "tracks", "Tracks", DEF_TRACKS, t_Bool},
78  {&spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
79  {&dbufp,  "dbuf",   "Dbuf",   DEF_DBUF,   t_Bool}, 
80 };
81
82 static OptionStruct desc[] =
83 {
84  {"-/+tracks", "turn on/off star tracks"},
85  {"-/+spin",   "do/don't spin viewpoint"},
86  {"-/+dbuf",   "turn on/off double buffering."},
87 };
88
89 ENTRYPOINT ModeSpecOpt galaxy_opts =
90 {sizeof opts / sizeof opts[0], opts,
91  sizeof vars / sizeof vars[0], vars, desc};
92
93
94 #define FLOATRAND ((double) LRAND() / ((double) MAXRAND))
95
96 #if 0
97 #define WRAP       1  /* Warp around edges */
98 #define BOUNCE     1  /* Bounce from borders */
99 #endif
100
101 #define MINSIZE       1
102 #define MINGALAXIES    2
103 #define MAX_STARS    3000
104 #define MAX_IDELTAT    50
105 /* These come originally from the Cluster-version */
106 #define DEFAULT_GALAXIES  3
107 #define DEFAULT_STARS    1000
108 #define DEFAULT_HITITERATIONS  7500
109 #define DEFAULT_IDELTAT    200 /* 0.02 */
110 #define EPSILON 0.00000001
111
112 #define sqrt_EPSILON 0.0001
113
114 #define DELTAT (MAX_IDELTAT * 0.0001)
115
116 #define GALAXYRANGESIZE  0.1
117 #define GALAXYMINSIZE  0.15
118 #define QCONS    0.001
119
120
121 #define COLORBASE  16
122 /* colors per galaxy */
123 /* #define COLORSTEP  (NUMCOLORS/COLORBASE) */
124 # define COLORSTEP (MI_NCOLORS(mi)/COLORBASE)
125
126
127 typedef struct {
128  double      pos[3], vel[3];
129 } Star;
130
131
132 typedef struct {
133  int         mass;
134  int         nstars;
135  Star       *stars;
136  XPoint     *oldpoints;
137  XPoint     *newpoints;
138  double      pos[3], vel[3];
139  int         galcol;
140 } Galaxy;
141
142 typedef struct {
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  double      rot_y; /* rotation of eye around center of universe, around
154 y-axis*/
155  double      rot_x; /* rotation of eye around center of universe, around
156 x-axis */
157 } unistruct;
158
159 static unistruct *universes = NULL;
160
161 static void
162 free_galaxies(unistruct * gp)
163 {
164  if (gp->galaxies != NULL) {
165   int         i;
166
167   for (i = 0; i < gp->ngalaxies; i++) {
168    Galaxy     *gt = &gp->galaxies[i];
169
170    if (gt->stars != NULL)
171     (void) free((void *) gt->stars);
172    if (gt->oldpoints != NULL)
173        (void) free((void *) gt->oldpoints);
174    if (gt->newpoints != NULL)
175        (void) free((void *) gt->newpoints);
176   }
177   (void) free((void *) gp->galaxies);
178   gp->galaxies = NULL;
179  }
180 }
181
182 static void
183 startover(ModeInfo * mi)
184 {
185  unistruct  *gp = &universes[MI_SCREEN(mi)];
186  int         i, j; /* more tmp */
187  double      w1, w2; /* more tmp */
188  double      d, v, w, h; /* yet more tmp */
189
190  gp->step = 0;
191  gp->rot_y = 0;
192  gp->rot_x = 0;
193
194  if (MI_BATCHCOUNT(mi) < -MINGALAXIES)
195   free_galaxies(gp);
196  gp->ngalaxies = MI_BATCHCOUNT(mi);
197  if (gp->ngalaxies < -MINGALAXIES)
198   gp->ngalaxies = NRAND(-gp->ngalaxies - MINGALAXIES + 1) + MINGALAXIES;
199
200  else if (gp->ngalaxies < MINGALAXIES)
201   gp->ngalaxies = MINGALAXIES;
202  if (gp->galaxies == NULL)
203   gp->galaxies = (Galaxy *) calloc(gp->ngalaxies, sizeof (Galaxy));
204
205  for (i = 0; i < gp->ngalaxies; ++i) {
206   Galaxy     *gt = &gp->galaxies[i];
207   double      sinw1, sinw2, cosw1, cosw2;
208
209   gt->galcol = NRAND(COLORBASE - 2);
210   if (gt->galcol > 1)
211    gt->galcol += 2; /* Mult 8; 16..31 no green stars */
212   /* Galaxies still may have some green stars but are not all green. */
213
214   if (gt->stars != NULL) {
215    (void) free((void *) gt->stars);
216    gt->stars = NULL;
217   }
218   gt->nstars = (NRAND(MAX_STARS / 2)) + MAX_STARS / 2;
219   gt->stars = (Star *) malloc(gt->nstars * sizeof (Star));
220   gt->oldpoints = (XPoint *) malloc(gt->nstars * sizeof (XPoint));
221   gt->newpoints = (XPoint *) malloc(gt->nstars * sizeof (XPoint));
222
223   w1 = 2.0 * M_PI * FLOATRAND;
224   w2 = 2.0 * M_PI * FLOATRAND;
225   sinw1 = SINF(w1);
226   sinw2 = SINF(w2);
227   cosw1 = COSF(w1);
228   cosw2 = COSF(w2);
229
230   gp->mat[0][0] = cosw2;
231   gp->mat[0][1] = -sinw1 * sinw2;
232   gp->mat[0][2] = cosw1 * sinw2;
233   gp->mat[1][0] = 0.0;
234   gp->mat[1][1] = cosw1;
235   gp->mat[1][2] = sinw1;
236   gp->mat[2][0] = -sinw2;
237   gp->mat[2][1] = -sinw1 * cosw2;
238   gp->mat[2][2] = cosw1 * cosw2;
239
240   gt->vel[0] = FLOATRAND * 2.0 - 1.0;
241   gt->vel[1] = FLOATRAND * 2.0 - 1.0;
242   gt->vel[2] = FLOATRAND * 2.0 - 1.0;
243   gt->pos[0] = -gt->vel[0] * DELTAT * gp->f_hititerations + FLOATRAND -
244 0.5;
245   gt->pos[1] = -gt->vel[1] * DELTAT * gp->f_hititerations + FLOATRAND -
246 0.5;
247   gt->pos[2] = -gt->vel[2] * DELTAT * gp->f_hititerations + FLOATRAND -
248 0.5;
249
250   gt->mass = (int) (FLOATRAND * 1000.0) + 1;
251
252   gp->size = GALAXYRANGESIZE * FLOATRAND + GALAXYMINSIZE;
253
254   for (j = 0; j < gt->nstars; ++j) {
255    Star       *st = &gt->stars[j];
256    XPoint     *oldp = &gt->oldpoints[j];
257    XPoint     *newp = &gt->newpoints[j];
258
259    double      sinw, cosw;
260
261    w = 2.0 * M_PI * FLOATRAND;
262    sinw = SINF(w);
263    cosw = COSF(w);
264    d = FLOATRAND * gp->size;
265    h = FLOATRAND * exp(-2.0 * (d / gp->size)) / 5.0 * gp->size;
266    if (FLOATRAND < 0.5)
267     h = -h;
268    st->pos[0] = gp->mat[0][0] * d * cosw + gp->mat[1][0] * d * sinw +
269 gp->mat[2][0] * h + gt->pos[0];
270    st->pos[1] = gp->mat[0][1] * d * cosw + gp->mat[1][1] * d * sinw +
271 gp->mat[2][1] * h + gt->pos[1];
272    st->pos[2] = gp->mat[0][2] * d * cosw + gp->mat[1][2] * d * sinw +
273 gp->mat[2][2] * h + gt->pos[2];
274
275    v = sqrt(gt->mass * QCONS / sqrt(d * d + h * h));
276    st->vel[0] = -gp->mat[0][0] * v * sinw + gp->mat[1][0] * v * cosw +
277 gt->vel[0];
278    st->vel[1] = -gp->mat[0][1] * v * sinw + gp->mat[1][1] * v * cosw +
279 gt->vel[1];
280    st->vel[2] = -gp->mat[0][2] * v * sinw + gp->mat[1][2] * v * cosw +
281 gt->vel[2];
282
283    st->vel[0] *= DELTAT;
284    st->vel[1] *= DELTAT;
285    st->vel[2] *= DELTAT;
286
287    oldp->x = 0;
288    oldp->y = 0;
289    newp->x = 0;
290    newp->y = 0;
291   }
292
293  }
294
295  XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
296
297 #if 0
298  (void) printf("ngalaxies=%d, f_hititerations=%d\n", gp->ngalaxies,
299 gp->f_hititerations);
300  (void) printf("f_deltat=%g\n", DELTAT);
301  (void) printf("Screen: ");
302 #endif /*0 */
303 }
304
305 ENTRYPOINT void
306 init_galaxy(ModeInfo * mi)
307 {
308  unistruct  *gp;
309
310  if (universes == NULL) {
311   if ((universes = (unistruct *) calloc(MI_NUM_SCREENS(mi),
312       sizeof (unistruct))) == NULL)
313    return;
314  }
315  gp = &universes[MI_SCREEN(mi)];
316
317 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
318   dbufp = False;
319 # endif
320
321  gp->f_hititerations = MI_CYCLES(mi);
322
323  gp->scale = (double) (MI_WIN_WIDTH(mi) + MI_WIN_HEIGHT(mi)) / 8.0;
324  gp->midx =  MI_WIN_WIDTH(mi)  / 2;
325  gp->midy =  MI_WIN_HEIGHT(mi) / 2;
326  startover(mi);
327 }
328
329 ENTRYPOINT void
330 draw_galaxy(ModeInfo * mi)
331 {
332   Display    *display = MI_DISPLAY(mi);
333   Window      window = MI_WINDOW(mi);
334   GC          gc = MI_GC(mi);
335   unistruct  *gp = &universes[MI_SCREEN(mi)];
336   double      d, eps, cox, six, cor, sir;  /* tmp */
337   int         i, j, k; /* more tmp */
338   XPoint    *dummy = NULL;
339
340   if (! dbufp)
341     XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
342
343   if(spin){
344     gp->rot_y += 0.01;
345     gp->rot_x += 0.004;
346   }
347
348   cox = COSF(gp->rot_y);
349   six = SINF(gp->rot_y);
350   cor = COSF(gp->rot_x);
351   sir = SINF(gp->rot_x);
352
353   eps = 1/(EPSILON * sqrt_EPSILON * DELTAT * DELTAT * QCONS);
354
355   for (i = 0; i < gp->ngalaxies; ++i) {
356     Galaxy     *gt = &gp->galaxies[i];
357
358     for (j = 0; j < gp->galaxies[i].nstars; ++j) {
359       Star       *st = &gt->stars[j];
360       XPoint     *newp = &gt->newpoints[j];
361       double      v0 = st->vel[0];
362       double      v1 = st->vel[1];
363       double      v2 = st->vel[2];
364
365       for (k = 0; k < gp->ngalaxies; ++k) {
366         Galaxy     *gtk = &gp->galaxies[k];
367         double      d0 = gtk->pos[0] - st->pos[0];
368         double      d1 = gtk->pos[1] - st->pos[1];
369         double      d2 = gtk->pos[2] - st->pos[2];
370
371         d = d0 * d0 + d1 * d1 + d2 * d2;
372         if (d > EPSILON)
373           d = gtk->mass / (d * sqrt(d)) * DELTAT * DELTAT * QCONS;
374         else
375           d = gtk->mass / (eps * sqrt(eps));
376         v0 += d0 * d;
377         v1 += d1 * d;
378         v2 += d2 * d;
379       }
380
381       st->vel[0] = v0;
382       st->vel[1] = v1;
383       st->vel[2] = v2;
384
385       st->pos[0] += v0;
386       st->pos[1] += v1;
387       st->pos[2] += v2;
388
389       newp->x = (short) (((cox * st->pos[0]) - (six * st->pos[2])) *
390                          gp->scale) + gp->midx;
391       newp->y = (short) (((cor * st->pos[1]) - (sir * ((six * st->pos[0]) +
392                                                        (cox * st->pos[2]))))
393                          * gp->scale) + gp->midy;
394
395     }
396
397     for (k = i + 1; k < gp->ngalaxies; ++k) {
398       Galaxy     *gtk = &gp->galaxies[k];
399       double      d0 = gtk->pos[0] - gt->pos[0];
400       double      d1 = gtk->pos[1] - gt->pos[1];
401       double      d2 = gtk->pos[2] - gt->pos[2];
402
403       d = d0 * d0 + d1 * d1 + d2 * d2;
404       if (d > EPSILON)
405         d = 1 / (d * sqrt(d)) * DELTAT * QCONS;
406       else
407         d = 1 / (EPSILON * sqrt_EPSILON) * DELTAT * QCONS;
408
409       d0 *= d;
410       d1 *= d;
411       d2 *= d;
412       gt->vel[0] += d0 * gtk->mass;
413       gt->vel[1] += d1 * gtk->mass;
414       gt->vel[2] += d2 * gtk->mass;
415       gtk->vel[0] -= d0 * gt->mass;
416       gtk->vel[1] -= d1 * gt->mass;
417       gtk->vel[2] -= d2 * gt->mass;
418     }
419
420     gt->pos[0] += gt->vel[0] * DELTAT;
421     gt->pos[1] += gt->vel[1] * DELTAT;
422     gt->pos[2] += gt->vel[2] * DELTAT;
423
424     if (dbufp) {
425       XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
426       XDrawPoints(display, window, gc, gt->oldpoints, gt->nstars,
427                   CoordModeOrigin);
428     }
429     XSetForeground(display, gc, MI_PIXEL(mi, COLORSTEP * gt->galcol));
430     XDrawPoints(display, window, gc, gt->newpoints, gt->nstars,
431                 CoordModeOrigin);
432
433     dummy = gt->oldpoints;
434     gt->oldpoints = gt->newpoints;
435     gt->newpoints = dummy;
436   }
437
438   gp->step++;
439   if (gp->step > gp->f_hititerations * 4)
440     startover(mi);
441 }
442
443 ENTRYPOINT void
444 reshape_galaxy(ModeInfo * mi, int width, int height)
445 {
446   XClearWindow (MI_DISPLAY (mi), MI_WINDOW(mi));
447   init_galaxy (mi);
448 }
449
450 ENTRYPOINT void
451 release_galaxy(ModeInfo * mi)
452 {
453  if (universes != NULL) {
454   int         screen;
455
456   for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
457    free_galaxies(&universes[screen]);
458   (void) free((void *) universes);
459   universes = NULL;
460  }
461 }
462
463 ENTRYPOINT void
464 refresh_galaxy(ModeInfo * mi)
465 {
466  /* Do nothing, it will refresh by itself */
467 }
468
469 XSCREENSAVER_MODULE ("Galaxy", galaxy)