1 /* -*- Mode: C; tab-width: 4 -*-
2 * galaxy --- draw spinning, colliding galaxies.
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)galaxy.c 4.02 97/04/01 xlockmore";
8 /* Originally done by Uli Siegmund (uli@wombat.okapi.sub.org) on Amiga
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.
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)
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>
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>
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" \
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 */
55 #define FLOATRAND ((double) LRAND() / ((double) MAXRAND))
58 #define WRAP 1 /* Warp around edges */
59 #define BOUNCE 1 /* Bounce from borders */
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
73 #define GALAXYRANGESIZE 0.1
74 #define GALAXYMINSIZE 0.1
78 /* Colors for stars start here */
79 #define COLORSTEP (NUMCOLORS/COLORBASE) /* 8 colors per galaxy */
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)
87 #define DEF_TRACKS "False"
89 static XrmOptionDescRec opts[] =
91 {"-tracks", ".galaxy.tracks", XrmoptionNoArg, (caddr_t) "on"},
92 {"+tracks", ".galaxy.tracks", XrmoptionNoArg, (caddr_t) "off"}
95 static argtype vars[] =
97 {(caddr_t *) & tracks, "tracks", "Tracks", DEF_TRACKS, t_Bool}
100 static OptionStruct desc[] =
102 {"-/+tracks", "turn on/off star tracks"}
105 ModeSpecOpt galaxy_opts = { 2, opts, 1, vars, desc };
109 double pos[3], vel[3];
119 double pos[3], vel[3];
125 int left; /* x minimum */
126 int right; /* x maximum */
127 int top; /* y minimum */
128 int bottom; /* y maximum */
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 */
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 */
143 static unistruct *universes = NULL;
146 free_galaxies(unistruct * gp)
148 if (gp->galaxies != NULL) {
151 for (i = 0; i < gp->ngalaxies; i++) {
152 Galaxy *gt = &gp->galaxies[i];
154 if (gt->stars != NULL)
155 (void) free((void *) gt->stars);
157 (void) free((void *) gp->galaxies);
163 startover(ModeInfo * mi)
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 */
173 if (MI_BATCHCOUNT(mi) < -MINGALAXIES)
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));
183 for (i = 0; i < gp->ngalaxies; ++i) {
184 Galaxy *gt = &gp->galaxies[i];
185 double sinw1, sinw2, cosw1, cosw2;
187 gt->galcol = NRAND(COLORBASE - 2);
189 gt->galcol += 2; /* Mult 8; 16..31 no green stars */
190 /* Galaxies still may have some green stars but are not all green. */
192 if (gt->stars != NULL) {
193 (void) free((void *) gt->stars);
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;
205 gp->mat[0][0] = cosw2;
206 gp->mat[0][1] = -sinw1 * sinw2;
207 gp->mat[0][2] = cosw1 * sinw2;
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;
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;
225 gt->mass = (int) (FLOATRAND * 1000.0) + 1;
227 gp->size = GALAXYRANGESIZE * FLOATRAND + GALAXYMINSIZE;
229 for (j = 0; j < gt->nstars; ++j) {
230 Star *st = >->stars[j];
233 w = 2.0 * M_PI * FLOATRAND;
236 d = FLOATRAND * gp->size;
237 h = FLOATRAND * exp(-2.0 * (d / gp->size)) / 5.0 * gp->size;
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];
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 +
250 st->vel[1] = -gp->mat[0][1] * v * sinw + gp->mat[1][1] * v * cosw +
252 st->vel[2] = -gp->mat[0][2] * v * sinw + gp->mat[1][2] * v * cosw +
259 st->size = NRAND(-size - MINSIZE + 1) + MINSIZE;
260 else if (size < MINSIZE)
267 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
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);
281 init_galaxy(ModeInfo * mi)
285 if (universes == NULL) {
286 if ((universes = (unistruct *) calloc(MI_NUM_SCREENS(mi),
287 sizeof (unistruct))) == NULL)
290 gp = &universes[MI_SCREEN(mi)];
292 gp->f_hititerations = MI_CYCLES(mi);
293 gp->f_deltat = ((double) MAX_IDELTAT) / 10000.0;
297 gp->clip.right = MI_WIN_WIDTH(mi);
298 gp->clip.bottom = MI_WIN_HEIGHT(mi);
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;
307 draw_galaxy(ModeInfo * mi)
309 Display *display = MI_DISPLAY(mi);
310 Window window = MI_WINDOW(mi);
312 unistruct *gp = &universes[MI_SCREEN(mi)];
314 int i, j, k; /* more tmp */
316 for (i = 0; i < gp->ngalaxies; ++i) {
317 Galaxy *gt = &gp->galaxies[i];
319 for (j = 0; j < gp->galaxies[i].nstars; ++j) {
320 Star *st = >->stars[j];
322 for (k = 0; k < gp->ngalaxies; ++k) {
323 Galaxy *gtk = &gp->galaxies[k];
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];
332 d = gt->mass / (d * sqrt(d)) * gp->f_deltat * QCONS;
336 st->vel[0] += gp->diff[0];
337 st->vel[1] += gp->diff[1];
338 st->vel[2] += gp->diff[2];
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;
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;
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);
355 st->px = (int) (st->pos[0] * gp->scale) + gp->midx;
356 st->py = (int) (st->pos[1] * gp->scale) + gp->midy;
360 if (st->px < gp->clip.left) {
361 (void) printf("wrap l -> r\n");
362 st->px = gp->clip.right;
364 if (st->px > gp->clip.right) {
365 (void) printf("wrap r -> l\n");
366 st->px = gp->clip.left;
368 if (st->py > gp->clip.bottom) {
369 (void) printf("wrap b -> t\n");
370 st->py = gp->clip.top;
372 if (st->py < gp->clip.top) {
373 (void) printf("wrap t -> b\n");
374 st->py = gp->clip.bottom;
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));
386 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
388 drawStar(st->px + 1, st->py, st->size);
390 drawStar(st->px, st->py, st->size);
394 for (k = i + 1; k < gp->ngalaxies; ++k) {
395 Galaxy *gtk = &gp->galaxies[k];
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];
404 d = gt->mass * gt->mass / (d * sqrt(d)) * gp->f_deltat * QCONS;
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;
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;
421 if (gp->step > gp->f_hititerations * 4)
426 release_galaxy(ModeInfo * mi)
428 if (universes != NULL) {
431 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
432 free_galaxies(&universes[screen]);
433 (void) free((void *) universes);
439 refresh_galaxy(ModeInfo * mi)
441 /* Do nothing, it will refresh by itself */