http://ftp.x.org/contrib/applications/xscreensaver-3.07.tar.gz
[xscreensaver] / hacks / bouboule.c
1 /* -*- Mode: C; tab-width: 4 -*-
2    Ported from xlockmore 4.03a12 to be a standalone program and thus usable
3    with xscreensaver by Jamie Zawinski <jwz@jwz.org> on 15-May-97.
4
5    Original copyright notice from xlock.c:
6
7     * Copyright (c) 1988-91 by Patrick J. Naughton.
8     *
9     * Permission to use, copy, modify, and distribute this software and its
10     * documentation for any purpose and without fee is hereby granted,
11     * provided that the above copyright notice appear in all copies and that
12     * both that copyright notice and this permission notice appear in
13     * supporting documentation.
14     *
15     * This file is provided AS IS with no warranties of any kind.  The author
16     * shall have no liability with respect to the infringement of copyrights,
17     * trade secrets or any patents by this file or any part thereof.  In no
18     * event will the author be liable for any lost revenue or profits or
19     * other special, indirect and consequential damages.
20  */
21
22 #if !defined( lint ) && !defined( SABER )
23 static const char sccsid[] = "@(#)bouboule.c    4.00 97/01/01 xlockmore";
24
25 #endif
26
27 /*-
28  * bouboule.c (bouboule mode for xlockmore)
29  *
30  * Sort of starfield for xlockmore. I found that making a starfield for
31  * a 3D engine and thought it could be a nice lock mode. For a real starfield,
32  * I only scale the sort of sphere you see to the whole sky and clip the stars
33  * to the camera screen.
34  *
35  *   Code Copyright 1996 by Jeremie PETIT (jeremie_petit@geocities.com)
36  *
37  *   Use: batchcount is the number of stars.
38  *        cycles is the maximum size for a star
39  *
40  * 15-May-97: jwz@jwz.org: turned into a standalone program.
41  * 04-Sep-96: Added 3d support (Henrik Theiling, theiling@coli-uni-sb.de)
42  * 20-Feb-96: Added tests so that already malloced objects are not
43  *            malloced twice, thanks to the report from <mccomb@interport.net>
44  * 01-Feb-96: Patched by Jouk Jansen <joukj@alpha.chem.uva.nl> for VMS
45  *            Patched by <bagleyd@bigfoot.com> for TrueColor displays
46  * 30-Jan-96: Wrote all that I wanted to.
47  *
48  * DONE: Build up a XArc list and Draw everything once with XFillArcs
49  *       That idea came from looking at swarm code.
50  * DONE: Add an old arcs list for erasing.
51  * DONE: Make center of starfield SinVariable.
52  * DONE: Add some random in the sinvary() function.
53  * DONE: check time for erasing the stars with the two methods and use the
54  *       better one. Note that sometimes the time difference between
55  *       beginning of erasing and its end is negative! I check this, and
56  *       do not use this result when it occurs. If all values are negative,
57  *       the erasing will continue being done in the currently tested mode.
58  * DONE: Allow stars size customization.
59  * DONE: Make sizey be no less than half sizex or no bigger than twice sizex.
60  *
61  * IDEA: A simple check can be performed to know which stars are "behind"
62  *       and which are "in front". So is possible to very simply change
63  *       the drawing mode for these two sorts of stars. BUT: this would lead
64  *       to a rewrite of the XArc list code because drawing should be done
65  *       in two steps: "behind" stars then "in front" stars. Also, what could
66  *       be the difference between the rendering of these two types of stars?
67  * IDEA: Calculate the distance of each star to the "viewer" and render the
68  *       star accordingly to this distance. Same remarks as for previous
69  *       ideas can be pointed out. This would even lead to reget the old stars
70  *       drawing code, that has been replaced by the XFillArcs. On another
71  *       hand, this would allow particular stars (own color, shape...), as
72  *       far as they would be individually drawn. One should be careful to
73  *       draw them according to their distance, that is not drawing a far
74  *       star after a close one.
75  */
76
77 #ifdef STANDALONE
78 # define PROGCLASS                                      "Bouboule"
79 # define HACK_INIT                                      init_bouboule
80 # define HACK_DRAW                                      draw_bouboule
81 # define bouboule_opts                          xlockmore_opts
82 # define DEFAULTS       "*count:                100     \n"                     \
83                                         "*size:                 15      \n"                     \
84                                         "*delay:                5000    \n"                     \
85                                         "*ncolors:              64      \n"                     \
86                                         "*use3d:                False   \n"                     \
87                                         "*delta3d:              1.5             \n"                     \
88                                         "*right3d:              red             \n"                     \
89                                         "*left3d:               blue    \n"                     \
90                                         "*both3d:               magenta \n"                     \
91                                         "*none3d:               black   \n"
92
93 # define SMOOTH_COLORS
94 # include "xlockmore.h"                         /* from the xscreensaver distribution */
95 #else  /* !STANDALONE */
96 # include "xlock.h"                                     /* from the xlockmore distribution */
97 #endif /* !STANDALONE */
98
99 ModeSpecOpt bouboule_opts = {
100   0, NULL, 0, NULL, NULL };
101
102 #define USEOLDXARCS  1          /* If 1, we use old xarcs list for erasing.
103                                    * else we just roughly erase the window.
104                                    * This mainly depends on the number of stars,
105                                    * because when they are many, it is faster to
106                                    * erase the whole window than to erase each star
107                                  */
108
109 #if HAVE_GETTIMEOFDAY
110 #define ADAPT_ERASE  1          /* If 1, then we try ADAPT_CHECKS black XFillArcs,
111                                    * and after, ADAPT_CHECKS XFillRectangle.
112                                    * We check which method seems better, knowing that
113                                    * XFillArcs is generally visually better. So we
114                                    * consider that XFillArcs is still better if its time
115                                    * is about XFillRectangle * ADAPT_ARC_PREFERED
116                                    * We need gettimeofday
117                                    * for this... Does it exist on other systems ? Do we
118                                    * have to use another function for others ?
119                                    * This value overrides USEOLDXARCS.
120                                  */
121
122 #ifdef USE_XVMSUTILS
123 # if 0
124 #  include "../xvmsutils/unix_time.h"
125 # else
126 #  include <X11/unix_time.h>
127 # endif
128 #endif
129
130 #include <sys/time.h>
131
132 #define ADAPT_CHECKS 50
133 #define ADAPT_ARC_PREFERED 150  /* Maybe the value that is the most important
134                                    * for adapting to a system */
135 #endif
136
137 #define dtor(x)    (((x) * M_PI) / 180.0)       /* Degrees to radians */
138
139 #define MINSTARS      1
140 #define MINSIZE       1
141 /* jwz: I think slower color changes look better */
142 #define COLOR_CHANGES 50                /* How often we change colors (1 = always)
143                                    * This value should be tuned accordingly to
144                                    * the number of stars */
145 #define MAX_SIZEX_SIZEY 2.      /* This controls whether the sphere can be very
146                                    * very large and have a small height (or the
147                                    * opposite) or no. */
148
149 #define THETACANRAND  80        /* percentage of changes for the speed of
150                                    * change of the 3 theta values */
151 #define SIZECANRAND   80        /* percentage of changes for the speed of
152                                    * change of the sizex and sizey values */
153 #define POSCANRAND    80        /* percentage of changes for the speed of
154                                    * change of the x and y values */
155 /* Note that these XXXCANRAND values can be 0, that is no rand acceleration *
156    variation. */
157
158 #define VARRANDALPHA (NRAND((int) (M_PI * 1000.0))/1000.0)
159 #define VARRANDSTEP  (M_PI/(NRAND(100)+100.0))
160 #define VARRANDMIN   (-70.0)
161 #define VARRANDMAX   70.0
162
163 #define MINZVAL   100           /* stars can come this close */
164 #define SCREENZ  2000           /* this is where the screen is */
165 #define MAXZVAL 10000           /* stars can go this far away */
166
167 #define GETZDIFF(z) ((MI_DELTA3D(mi))*20.0*(1.0-(SCREENZ)/(z+1000)))
168 #define MAXDIFF  MAX(-GETZDIFF(MINZVAL),GETZDIFF(MAXZVAL))
169
170 /* These values are the variation parameters of the acceleration variation *
171    of the SinVariables that are randomized. */
172
173 /******************************/
174 typedef struct SinVariableStruct
175 /******************************/
176 {
177         double      alpha;      /* 
178                                  * Alpha is the current state of the sinvariable
179                                  * alpha should be initialized to a value between
180                                  * 0.0 and 2 * M_PI
181                                  */
182         double      step;       /*
183                                  * Speed of evolution of alpha. It should be a reasonable
184                                  * fraction of 2 * M_PI. This value directly influence
185                                  * the variable speed of variation.
186                                  */
187         double      minimum;    /* Minimum value for the variable */
188         double      maximum;    /* Maximum value for the variable */
189         double      value;      /* Current value */
190         int         mayrand;    /* Flag for knowing whether some randomization can be
191                                  * applied to the variable */
192         struct SinVariableStruct *varrand;      /* Evolving Variable: the variation of
193                                                    * alpha */
194 } SinVariable;
195
196 /***********************/
197 typedef struct StarStruct
198 /***********************/
199 {
200         double      x, y, z;    /* Position of the star */
201         short       size;       /* Try to guess */
202 } Star;
203
204 /****************************/
205 typedef struct StarFieldStruct
206 /****************************/
207 {
208         short       width, height;      /* width and height of the starfield window */
209         short       max_star_size;      /* Maximum radius for stars. stars radius will
210                                          * vary from 1 to MAX_STAR_SIZE */
211         SinVariable x;          /* Evolving variables:               */
212         SinVariable y;          /* Center of the field on the screen */
213         SinVariable z;
214         SinVariable sizex;      /* Evolving variable: half width  of the field */
215         SinVariable sizey;      /* Evolving variable: half height of the field */
216         SinVariable thetax;     /* Evolving Variables:              */
217         SinVariable thetay;     /* rotation angles of the starfield */
218         SinVariable thetaz;     /* around x, y and z local axis     */
219         Star       *star;       /* List of stars */
220         XArc       *xarc;       /* Current List of arcs */
221         XArc       *xarcleft;   /* additional list for the left arcs */
222 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
223         XArc       *oldxarc;    /* Old list of arcs */
224         XArc       *oldxarcleft;
225 #endif
226         unsigned long color;    /* Current color of the starfield */
227         int         colorp;     /* Pointer to color of the starfield */
228         int         NbStars;    /* Number of stars */
229         short       colorchange;        /* Counter for the color change */
230 #if (ADAPT_ERASE == 1)
231         short       hasbeenchecked;
232         long        rect_time;
233         long        xarc_time;
234 #endif
235 } StarField;
236
237 static StarField *starfield = NULL;
238
239 /*********/
240 static void
241 sinvary(SinVariable * v)
242 /*********/
243
244 {
245         v->value = v->minimum +
246                 (v->maximum - v->minimum) * (sin(v->alpha) + 1.0) / 2.0;
247
248         if (v->mayrand == 0)
249                 v->alpha += v->step;
250         else {
251                 int         vaval = NRAND(100);
252
253                 if (vaval <= v->mayrand)
254                         sinvary(v->varrand);
255                 v->alpha += (100.0 + (v->varrand->value)) * v->step / 100.0;
256         }
257
258         if (v->alpha > 2 * M_PI)
259                 v->alpha -= 2 * M_PI;
260 }
261
262 /*************************************************/
263 static void
264 sininit(SinVariable * v,
265         double alpha, double step, double minimum, double maximum,
266         short int mayrand)
267 {
268         v->alpha = alpha;
269         v->step = step;
270         v->minimum = minimum;
271         v->maximum = maximum;
272         v->mayrand = mayrand;
273         if (mayrand != 0) {
274                 if (v->varrand == NULL)
275                         v->varrand = (SinVariable *) calloc(1, sizeof (SinVariable));
276                 sininit(v->varrand,
277                         VARRANDALPHA,
278                         VARRANDSTEP,
279                         VARRANDMIN,
280                         VARRANDMAX,
281                         0);
282                 sinvary(v->varrand);
283         }
284         /* We calculate the values at least once for initialization */
285         sinvary(v);
286 }
287
288 static void
289 sinfree(SinVariable * point)
290 {
291         SinVariable *temp, *next;
292
293         next = point->varrand;
294         while (next) {
295                 temp = next;
296                 next = temp->varrand;
297                 (void) free((void *) temp);
298         }
299 }
300
301 /***************/
302 void
303 init_bouboule(ModeInfo * mi)
304 /***************/
305
306 /*-
307  *  The stars init part was first inspirated from the net3d game starfield
308  * code.  But net3d starfield is not really 3d starfield, and I needed real 3d,
309  * so only remains the net3d starfield initialization main idea, that is
310  * the stars distribution on a sphere (theta and omega computing)
311  */
312 {
313         StarField  *sp;
314         int         size = MI_SIZE(mi);
315         int         i;
316         double      theta, omega;
317
318         if (starfield == NULL) {
319                 if ((starfield = (StarField *) calloc(MI_NUM_SCREENS(mi),
320                                                 sizeof (StarField))) == NULL)
321                         return;
322         }
323         sp = &starfield[MI_SCREEN(mi)];
324
325         sp->width = MI_WIN_WIDTH(mi);
326         sp->height = MI_WIN_HEIGHT(mi);
327
328         /* use the right `black' pixel values: */
329         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi)) {
330                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_NONE_COLOR(mi));
331                 XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
332                                0, 0, sp->width, sp->height);
333         } else
334                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
335
336         if (size < -MINSIZE)
337                 sp->max_star_size = NRAND(-size - MINSIZE + 1) + MINSIZE;
338         else if (size < MINSIZE)
339                 sp->max_star_size = MINSIZE;
340         else
341                 sp->max_star_size = size;
342
343         sp->NbStars = MI_BATCHCOUNT(mi);
344         if (sp->NbStars < -MINSTARS) {
345                 if (sp->star) {
346                         (void) free((void *) sp->star);
347                         sp->star = NULL;
348                 }
349                 if (sp->xarc) {
350                         (void) free((void *) sp->xarc);
351                         sp->xarc = NULL;
352                 }
353                 if (sp->xarcleft) {
354                         (void) free((void *) sp->xarcleft);
355                         sp->xarcleft = NULL;
356                 }
357 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
358                 if (sp->oldxarc) {
359                         (void) free((void *) sp->oldxarc);
360                         sp->oldxarc = NULL;
361                 }
362                 if (sp->oldxarcleft) {
363                         (void) free((void *) sp->oldxarcleft);
364                         sp->oldxarcleft = NULL;
365                 }
366 #endif
367                 sp->NbStars = NRAND(-sp->NbStars - MINSTARS + 1) + MINSTARS;
368         } else if (sp->NbStars < MINSTARS)
369                 sp->NbStars = MINSTARS;
370
371         /* We get memory for lists of objects */
372         if (sp->star == NULL)
373                 sp->star = (Star *) malloc(sp->NbStars * sizeof (Star));
374         if (sp->xarc == NULL)
375                 sp->xarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
376         if (MI_WIN_IS_USE3D(mi) && sp->xarcleft == NULL)
377                 sp->xarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
378 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
379         if (sp->oldxarc == NULL)
380                 sp->oldxarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
381         if (MI_WIN_IS_USE3D(mi) && sp->oldxarcleft == NULL)
382                 sp->oldxarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
383 #endif
384
385         {
386                 /* We initialize evolving variables */
387                 sininit(&sp->x,
388                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
389                         ((double) sp->width) / 4.0,
390                         3.0 * ((double) sp->width) / 4.0,
391                         POSCANRAND);
392                 sininit(&sp->y,
393                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
394                         ((double) sp->height) / 4.0,
395                         3.0 * ((double) sp->height) / 4.0,
396                         POSCANRAND);
397
398                 /* for z, we have to ensure that the bouboule does not get behind */
399                 /* the eyes of the viewer.  His/Her eyes are at 0.  Because the */
400                 /* bouboule uses the x-radius for the z-radius, too, we have to */
401                 /* use the x-values. */
402                 sininit(&sp->z,
403                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
404                         ((double) sp->width / 2.0 + MINZVAL),
405                         ((double) sp->width / 2.0 + MAXZVAL),
406                         POSCANRAND);
407
408
409                 sininit(&sp->sizex,
410                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
411                         MIN(((double) sp->width) - sp->x.value,
412                             sp->x.value) / 5.0,
413                         MIN(((double) sp->width) - sp->x.value,
414                             sp->x.value),
415                         SIZECANRAND);
416
417                 sininit(&sp->sizey,
418                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
419                         MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
420                             sp->sizey.maximum / 5.0),
421                         MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
422                             MIN(((double) sp->height) -
423                                 sp->y.value,
424                                 sp->y.value)),
425                         SIZECANRAND);
426
427                 sininit(&sp->thetax,
428                         NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
429                         -M_PI, M_PI,
430                         THETACANRAND);
431                 sininit(&sp->thetay,
432                         NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
433                         -M_PI, M_PI,
434                         THETACANRAND);
435                 sininit(&sp->thetaz,
436                         NRAND(3142) / 1000.0, M_PI / (NRAND(400) + 400.0),
437                         -M_PI, M_PI,
438                         THETACANRAND);
439         }
440         for (i = 0; i < sp->NbStars; i++) {
441                 Star       *star;
442                 XArc       *arc = NULL, *arcleft = NULL;
443 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
444         XArc       *oarc = NULL, *oarcleft = NULL;
445 #endif
446
447                 star = &(sp->star[i]);
448                 arc = &(sp->xarc[i]);
449                 if (MI_WIN_IS_USE3D(mi))
450                         arcleft = &(sp->xarcleft[i]);
451 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
452                 oarc = &(sp->oldxarc[i]);
453                 if (MI_WIN_IS_USE3D(mi))
454                         oarcleft = &(sp->oldxarcleft[i]);
455 #endif
456                 /* Elevation and bearing of the star */
457                 theta = dtor((NRAND(1800)) / 10.0 - 90.0);
458                 omega = dtor((NRAND(3600)) / 10.0 - 180.0);
459
460                 /* Stars coordinates in a 3D space */
461                 star->x = cos(theta) * sin(omega);
462                 star->y = sin(omega) * sin(theta);
463                 star->z = cos(omega);
464
465                 /* We set the stars size */
466                 star->size = NRAND(2 * sp->max_star_size);
467                 if (star->size < sp->max_star_size)
468                         star->size = 0;
469                 else
470                         star->size -= sp->max_star_size;
471
472                 /* We set default values for the XArc lists elements */
473                 arc->x = arc->y = 0;
474                 if (MI_WIN_IS_USE3D(mi)) {
475                         arcleft->x = arcleft->y = 0;
476                 }
477 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
478                 oarc->x = oarc->y = 0;
479                 if (MI_WIN_IS_USE3D(mi)) {
480                         oarcleft->x = oarcleft->y = 0;
481                 }
482 #endif
483                 arc->width = 2 + star->size;
484                 arc->height = 2 + star->size;
485                 if (MI_WIN_IS_USE3D(mi)) {
486                         arcleft->width = 2 + star->size;
487                         arcleft->height = 2 + star->size;
488                 }
489 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
490                 oarc->width = 2 + star->size;
491                 oarc->height = 2 + star->size;
492                 if (MI_WIN_IS_USE3D(mi)) {
493                         oarcleft->width = 2 + star->size;
494                         oarcleft->height = 2 + star->size;
495                 }
496 #endif
497
498                 arc->angle1 = 0;
499                 arc->angle2 = 360 * 64;
500                 if (MI_WIN_IS_USE3D(mi)) {
501                         arcleft->angle1 = 0;
502                         arcleft->angle2 = 360 * 64;
503                 }
504 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
505                 oarc->angle1 = 0;
506                 oarc->angle2 = 360 * 64;        /* ie. we draw whole disks:
507                                                  * from 0 to 360 degrees */
508                 if (MI_WIN_IS_USE3D(mi)) {
509                         oarcleft->angle1 = 0;
510                         oarcleft->angle2 = 360 * 64;
511                 }
512 #endif
513         }
514
515         if (MI_NPIXELS(mi) > 2)
516                 sp->colorp = NRAND(MI_NPIXELS(mi));
517         /* We set up the starfield color */
518         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2)
519                 sp->color = MI_PIXEL(mi, sp->colorp);
520         else
521                 sp->color = MI_WIN_WHITE_PIXEL(mi);
522
523 #if (ADAPT_ERASE == 1)
524         /* We initialize the adaptation code for screen erasing */
525         sp->hasbeenchecked = ADAPT_CHECKS * 2;
526         sp->rect_time = 0;
527         sp->xarc_time = 0;
528 #endif
529 }
530
531 /****************/
532 void
533 draw_bouboule(ModeInfo * mi)
534 /****************/
535
536 {
537         Display    *display = MI_DISPLAY(mi);
538         Window      window = MI_WINDOW(mi);
539         GC          gc = MI_GC(mi);
540         StarField  *sp = &starfield[MI_SCREEN(mi)];
541         int         i, diff = 0;
542         double      CX, CY, CZ, SX, SY, SZ;
543         Star       *star;
544         XArc       *arc = NULL, *arcleft = NULL;
545
546 #if (ADAPT_ERASE == 1)
547         struct timeval tv1;
548         struct timeval tv2;
549
550 #endif
551
552 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
553         short       x_1, y_1, x_2, y_2;
554
555         /* bounding rectangle around the old starfield,
556          * for erasing with the smallest rectangle
557          * instead of filling the whole screen */
558         int         maxdiff = 0;        /* maximal distance between left and right */
559
560         /* star in 3d mode, otherwise 0 */
561 #endif
562
563 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
564         if (MI_WIN_IS_USE3D(mi)) {
565                 maxdiff = (int) MAXDIFF;
566         }
567         x_1 = (int) sp->x.value - (int) sp->sizex.value -
568                 sp->max_star_size - maxdiff;
569         y_1 = (int) sp->y.value - (int) sp->sizey.value -
570                 sp->max_star_size;
571         x_2 = 2 * ((int) sp->sizex.value + sp->max_star_size + maxdiff);
572         y_2 = 2 * ((int) sp->sizey.value + sp->max_star_size);
573 #endif
574         /* We make variables vary. */
575         sinvary(&sp->thetax);
576         sinvary(&sp->thetay);
577         sinvary(&sp->thetaz);
578
579         sinvary(&sp->x);
580         sinvary(&sp->y);
581         if (MI_WIN_IS_USE3D(mi))
582                 sinvary(&sp->z);
583
584         /* A little trick to prevent the bouboule from being
585          * bigger than the screen */
586         sp->sizex.maximum =
587                 MIN(((double) sp->width) - sp->x.value,
588                     sp->x.value);
589         sp->sizex.minimum = sp->sizex.maximum / 3.0;
590
591         /* Another trick to make the ball not too flat */
592         sp->sizey.minimum =
593                 MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
594                     sp->sizey.maximum / 3.0);
595         sp->sizey.maximum =
596                 MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
597                     MIN(((double) sp->height) - sp->y.value,
598                         sp->y.value));
599
600         sinvary(&sp->sizex);
601         sinvary(&sp->sizey);
602
603         /*
604          * We calculate the rotation matrix values. We just make the
605          * rotation on the fly, without using a matrix.
606          * Star positions are recorded as unit vectors pointing in various
607          * directions. We just make them all rotate.
608          */
609         CX = cos(sp->thetax.value);
610         SX = sin(sp->thetax.value);
611         CY = cos(sp->thetay.value);
612         SY = sin(sp->thetay.value);
613         CZ = cos(sp->thetaz.value);
614         SZ = sin(sp->thetaz.value);
615
616         for (i = 0; i < sp->NbStars; i++) {
617                 star = &(sp->star[i]);
618                 arc = &(sp->xarc[i]);
619                 if (MI_WIN_IS_USE3D(mi)) {
620                         arcleft = &(sp->xarcleft[i]);
621                         /* to help the eyes, the starfield is always as wide as */
622                         /* deep, so .sizex.value can be used. */
623                         diff = (int) GETZDIFF(sp->sizex.value *
624                                       ((SY * CX) * star->x + (SX) * star->y +
625                                        (CX * CY) * star->z) + sp->z.value);
626                 }
627                 arc->x = (short) ((sp->sizex.value *
628                                    ((CY * CZ - SX * SY * SZ) * star->x +
629                                     (-CX * SZ) * star->y +
630                                     (SY * CZ + SZ * SX * CY) * star->z) +
631                                    sp->x.value));
632                 arc->y = (short) ((sp->sizey.value *
633                                    ((CY * SZ + SX * SY * CZ) * star->x +
634                                     (CX * CZ) * star->y +
635                                     (SY * SZ - SX * CY * CZ) * star->z) +
636                                    sp->y.value));
637
638                 if (MI_WIN_IS_USE3D(mi)) {
639                         arcleft->x = (short) ((sp->sizex.value *
640                                         ((CY * CZ - SX * SY * SZ) * star->x +
641                                          (-CX * SZ) * star->y +
642                                          (SY * CZ + SZ * SX * CY) * star->z) +
643                                                sp->x.value));
644                         arcleft->y = (short) ((sp->sizey.value *
645                                         ((CY * SZ + SX * SY * CZ) * star->x +
646                                          (CX * CZ) * star->y +
647                                          (SY * SZ - SX * CY * CZ) * star->z) +
648                                                sp->y.value));
649                         arc->x += diff;
650                         arcleft->x -= diff;
651                 }
652                 if (star->size != 0) {
653                         arc->x -= star->size;
654                         arc->y -= star->size;
655                         if (MI_WIN_IS_USE3D(mi)) {
656                                 arcleft->x -= star->size;
657                                 arcleft->y -= star->size;
658                         }
659                 }
660         }
661
662         /* First, we erase the previous starfield */
663         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi))
664                 XSetForeground(display, gc, MI_NONE_COLOR(mi));
665         else
666                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
667
668 #if (ADAPT_ERASE == 1)
669         if (sp->hasbeenchecked == 0) {
670                 /* We just calculate which method is the faster and eventually free
671                  * the oldxarc list */
672                 if (sp->xarc_time >
673                     ADAPT_ARC_PREFERED * sp->rect_time) {
674                         sp->hasbeenchecked = -2;        /* XFillRectangle mode */
675                         (void) free((void *) sp->oldxarc);
676                         sp->oldxarc = NULL;
677                         if (MI_WIN_IS_USE3D(mi)) {
678                                 (void) free((void *) sp->oldxarcleft);
679                           sp->oldxarcleft = NULL;
680                         }
681                 } else {
682                         sp->hasbeenchecked = -1;        /* XFillArcs mode */
683                 }
684         }
685         if (sp->hasbeenchecked == -2) {
686                 /* Erasing is done with XFillRectangle */
687                 XFillRectangle(display, window, gc,
688                                x_1, y_1, x_2, y_2);
689         } else if (sp->hasbeenchecked == -1) {
690                 /* Erasing is done with XFillArcs */
691                 XFillArcs(display, window, gc,
692                           sp->oldxarc, sp->NbStars);
693                 if (MI_WIN_IS_USE3D(mi))
694                         XFillArcs(display, window, gc,
695                                   sp->oldxarcleft, sp->NbStars);
696         } else {
697                 long        usec;
698
699                 if (sp->hasbeenchecked > ADAPT_CHECKS) {
700 #ifdef GETTIMEOFDAY_TWO_ARGS
701                         (void) gettimeofday(&tv1, NULL);
702 #else
703                         (void) gettimeofday(&tv1);
704 #endif
705                         XFillRectangle(display, window, gc,
706                                        x_1, y_1, x_2, y_2);
707 #ifdef GETTIMEOFDAY_TWO_ARGS
708                         (void) gettimeofday(&tv2, NULL);
709 #else
710                         (void) gettimeofday(&tv2);
711 #endif
712                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
713                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
714                                 sp->rect_time += usec + tv2.tv_usec - tv1.tv_usec;
715                                 sp->hasbeenchecked--;
716                         }
717                 } else {
718 #ifdef GETTIMEOFDAY_TWO_ARGS
719                         (void) gettimeofday(&tv1, NULL);
720 #else
721                         (void) gettimeofday(&tv1);
722 #endif
723                         XFillArcs(display, window, gc,
724                                   sp->oldxarc, sp->NbStars);
725                         if (MI_WIN_IS_USE3D(mi))
726                                 XFillArcs(display, window, gc,
727                                           sp->oldxarcleft, sp->NbStars);
728 #ifdef GETTIMEOFDAY_TWO_ARGS
729                         (void) gettimeofday(&tv2, NULL);
730 #else
731                         (void) gettimeofday(&tv2);
732 #endif
733                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
734                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
735                                 sp->xarc_time += usec + tv2.tv_usec - tv1.tv_usec;
736                                 sp->hasbeenchecked--;
737                         }
738                 }
739         }
740 #else
741 #if (USEOLDXARCS == 1)
742         XFillArcs(display, window, gc,
743                   sp->oldxarc, sp->NbStars);
744         if (MI_WIN_IS_USE3D(mi))
745                 XFillArcs(display, window, gc,
746                           sp->oldxarcleft, sp->NbStars);
747 #else
748         XFillRectangle(display, window, gc,
749                        x_1, y_1, x_2, y_2);
750 #endif
751 #endif
752
753         /* Then we draw the new one */
754         if (MI_WIN_IS_USE3D(mi)) {
755                 if (MI_WIN_IS_INSTALL(mi))
756                         XSetFunction(display, gc, GXor);
757                 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
758                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
759                 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
760                 XFillArcs(display, window, gc, sp->xarcleft, sp->NbStars);
761                 if (MI_WIN_IS_INSTALL(mi))
762                         XSetFunction(display, gc, GXcopy);
763         } else {
764                 XSetForeground(display, gc, sp->color);
765                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
766         }
767
768 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
769 #if (ADAPT_ERASE == 1)
770         if (sp->hasbeenchecked >= -1) {
771                 arc = sp->xarc;
772                 sp->xarc = sp->oldxarc;
773                 sp->oldxarc = arc;
774                 if (MI_WIN_IS_USE3D(mi)) {
775                         arcleft = sp->xarcleft;
776                         sp->xarcleft = sp->oldxarcleft;
777                         sp->oldxarcleft = arcleft;
778                 }
779         }
780 #else
781         arc = sp->xarc;
782         sp->xarc = sp->oldxarc;
783         sp->oldxarc = arc;
784         if (MI_WIN_IS_USE3D(mi)) {
785                 arcleft = sp->xarcleft;
786                 sp->xarcleft = sp->oldxarcleft;
787                 sp->oldxarcleft = arcleft;
788         }
789 #endif
790 #endif
791
792         /* We set up the color for the next drawing */
793         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2 &&
794             (++sp->colorchange >= COLOR_CHANGES)) {
795                 sp->colorchange = 0;
796                 if (++sp->colorp >= MI_NPIXELS(mi))
797                         sp->colorp = 0;
798                 sp->color = MI_PIXEL(mi, sp->colorp);
799         }
800 }
801
802 void
803 release_bouboule(ModeInfo * mi)
804 {
805         if (starfield != NULL) {
806                 int         screen;
807
808                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
809                         StarField  *sp = &starfield[screen];
810
811                         if (sp->star)
812                                 (void) free((void *) sp->star);
813                         if (sp->xarc)
814                                 (void) free((void *) sp->xarc);
815                         if (sp->xarcleft)
816                                 (void) free((void *) sp->xarcleft);
817 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
818                         if (sp->oldxarc)
819                                 (void) free((void *) sp->oldxarc);
820                         if (sp->oldxarcleft)
821                                 (void) free((void *) sp->oldxarcleft);
822 #endif
823                         sinfree(&(sp->x));
824                         sinfree(&(sp->y));
825                         sinfree(&(sp->z));
826                         sinfree(&(sp->sizex));
827                         sinfree(&(sp->sizey));
828                         sinfree(&(sp->thetax));
829                         sinfree(&(sp->thetay));
830                         sinfree(&(sp->thetaz));
831                 }
832                 (void) free((void *) starfield);
833                 starfield = NULL;
834         }
835 }
836
837 void
838 refresh_bouboule(ModeInfo * mi)
839 {
840         /* Do nothing, it will refresh by itself */
841 }