From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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 0
23 static const char sccsid[] = "@(#)bouboule.c    4.00 97/01/01 xlockmore";
24 #endif
25
26 /*-
27  * bouboule.c (bouboule mode for xlockmore)
28  *
29  * Sort of starfield for xlockmore. I found that making a starfield for
30  * a 3D engine and thought it could be a nice lock mode. For a real starfield,
31  * I only scale the sort of sphere you see to the whole sky and clip the stars
32  * to the camera screen.
33  *
34  *   Code Copyright 1996 by Jeremie PETIT (jeremie_petit@geocities.com)
35  *
36  *   Use: batchcount is the number of stars.
37  *        cycles is the maximum size for a star
38  *
39  * 15-May-97: jwz@jwz.org: turned into a standalone program.
40  * 04-Sep-96: Added 3d support (Henrik Theiling, theiling@coli-uni-sb.de)
41  * 20-Feb-96: Added tests so that already malloced objects are not
42  *            malloced twice, thanks to the report from <mccomb@interport.net>
43  * 01-Feb-96: Patched by Jouk Jansen <joukj@alpha.chem.uva.nl> for VMS
44  *            Patched by <bagleyd@bigfoot.com> for TrueColor displays
45  * 30-Jan-96: Wrote all that I wanted to.
46  *
47  * DONE: Build up a XArc list and Draw everything once with XFillArcs
48  *       That idea came from looking at swarm code.
49  * DONE: Add an old arcs list for erasing.
50  * DONE: Make center of starfield SinVariable.
51  * DONE: Add some random in the sinvary() function.
52  * DONE: check time for erasing the stars with the two methods and use the
53  *       better one. Note that sometimes the time difference between
54  *       beginning of erasing and its end is negative! I check this, and
55  *       do not use this result when it occurs. If all values are negative,
56  *       the erasing will continue being done in the currently tested mode.
57  * DONE: Allow stars size customization.
58  * DONE: Make sizey be no less than half sizex or no bigger than twice sizex.
59  *
60  * IDEA: A simple check can be performed to know which stars are "behind"
61  *       and which are "in front". So is possible to very simply change
62  *       the drawing mode for these two sorts of stars. BUT: this would lead
63  *       to a rewrite of the XArc list code because drawing should be done
64  *       in two steps: "behind" stars then "in front" stars. Also, what could
65  *       be the difference between the rendering of these two types of stars?
66  * IDEA: Calculate the distance of each star to the "viewer" and render the
67  *       star accordingly to this distance. Same remarks as for previous
68  *       ideas can be pointed out. This would even lead to reget the old stars
69  *       drawing code, that has been replaced by the XFillArcs. On another
70  *       hand, this would allow particular stars (own color, shape...), as
71  *       far as they would be individually drawn. One should be careful to
72  *       draw them according to their distance, that is not drawing a far
73  *       star after a close one.
74  */
75
76 #ifdef STANDALONE
77 # define DEFAULTS       "*count:                100     \n"                     \
78                                         "*size:                 15      \n"                     \
79                                         "*delay:                20000   \n"                     \
80                                         "*ncolors:              64      \n"                     \
81                                         "*use3d:                True    \n"                     \
82                                         "*delta3d:              1.5             \n"                     \
83                                         "*right3d:              red             \n"                     \
84                                         "*left3d:               blue    \n"                     \
85                                         "*both3d:               magenta \n"                     \
86                                         "*none3d:               black   \n"                     \
87                                         "*fpsSolid:             true    \n"                     \
88                                         "*ignoreRotation: True  \n"
89
90 # define SMOOTH_COLORS
91 # define bouboule_handle_event 0
92 # include "xlockmore.h"                         /* from the xscreensaver distribution */
93 #else  /* !STANDALONE */
94 # include "xlock.h"                                     /* from the xlockmore distribution */
95 # define ENTRYPOINT /**/
96 #endif /* !STANDALONE */
97
98 ENTRYPOINT ModeSpecOpt bouboule_opts = {
99   0, NULL, 0, NULL, NULL };
100
101 #define USEOLDXARCS  1          /* If 1, we use old xarcs list for erasing.
102                                    * else we just roughly erase the window.
103                                    * This mainly depends on the number of stars,
104                                    * because when they are many, it is faster to
105                                    * erase the whole window than to erase each star
106                                  */
107
108 #if HAVE_GETTIMEOFDAY
109 #define ADAPT_ERASE  1          /* If 1, then we try ADAPT_CHECKS black XFillArcs,
110                                    * and after, ADAPT_CHECKS XFillRectangle.
111                                    * We check which method seems better, knowing that
112                                    * XFillArcs is generally visually better. So we
113                                    * consider that XFillArcs is still better if its time
114                                    * is about XFillRectangle * ADAPT_ARC_PREFERED
115                                    * We need gettimeofday
116                                    * for this... Does it exist on other systems ? Do we
117                                    * have to use another function for others ?
118                                    * This value overrides USEOLDXARCS.
119                                  */
120
121 #ifdef USE_XVMSUTILS
122 # if 0
123 #  include "../xvmsutils/unix_time.h"
124 # else
125 #  include <X11/unix_time.h>
126 # endif
127 #endif
128
129 #include <sys/time.h>
130
131 #define ADAPT_CHECKS 50
132 #define ADAPT_ARC_PREFERED 150  /* Maybe the value that is the most important
133                                    * for adapting to a system */
134 #endif
135
136 #define dtor(x)    (((x) * M_PI) / 180.0)       /* Degrees to radians */
137
138 #define MINSTARS      1
139 #define MINSIZE       1
140 /* jwz: I think slower color changes look better */
141 #define COLOR_CHANGES 50                /* How often we change colors (1 = always)
142                                    * This value should be tuned accordingly to
143                                    * the number of stars */
144 #define MAX_SIZEX_SIZEY 2.      /* This controls whether the sphere can be very
145                                    * very large and have a small height (or the
146                                    * opposite) or no. */
147
148 #define THETACANRAND  80        /* percentage of changes for the speed of
149                                    * change of the 3 theta values */
150 #define SIZECANRAND   80        /* percentage of changes for the speed of
151                                    * change of the sizex and sizey values */
152 #define POSCANRAND    80        /* percentage of changes for the speed of
153                                    * change of the x and y values */
154 /* Note that these XXXCANRAND values can be 0, that is no rand acceleration *
155    variation. */
156
157 #define VARRANDALPHA (NRAND((int) (M_PI * 1000.0))/1000.0)
158 #define VARRANDSTEP  (M_PI/(NRAND(100)+100.0))
159 #define VARRANDMIN   (-70.0)
160 #define VARRANDMAX   70.0
161
162 #define MINZVAL   100           /* stars can come this close */
163 #define SCREENZ  2000           /* this is where the screen is */
164 #define MAXZVAL 10000           /* stars can go this far away */
165
166 #define GETZDIFF(z) ((MI_DELTA3D(mi))*20.0*(1.0-(SCREENZ)/(z+1000)))
167 #define MAXDIFF  MAX(-GETZDIFF(MINZVAL),GETZDIFF(MAXZVAL))
168
169 /* These values are the variation parameters of the acceleration variation *
170    of the SinVariables that are randomized. */
171
172 /******************************/
173 typedef struct SinVariableStruct
174 /******************************/
175 {
176         double      alpha;      /* 
177                                  * Alpha is the current state of the sinvariable
178                                  * alpha should be initialized to a value between
179                                  * 0.0 and 2 * M_PI
180                                  */
181         double      step;       /*
182                                  * Speed of evolution of alpha. It should be a reasonable
183                                  * fraction of 2 * M_PI. This value directly influence
184                                  * the variable speed of variation.
185                                  */
186         double      minimum;    /* Minimum value for the variable */
187         double      maximum;    /* Maximum value for the variable */
188         double      value;      /* Current value */
189         int         mayrand;    /* Flag for knowing whether some randomization can be
190                                  * applied to the variable */
191         struct SinVariableStruct *varrand;      /* Evolving Variable: the variation of
192                                                    * alpha */
193 } SinVariable;
194
195 /***********************/
196 typedef struct StarStruct
197 /***********************/
198 {
199         double      x, y, z;    /* Position of the star */
200         short       size;       /* Try to guess */
201 } Star;
202
203 /****************************/
204 typedef struct StarFieldStruct
205 /****************************/
206 {
207         short       width, height;      /* width and height of the starfield window */
208         short       max_star_size;      /* Maximum radius for stars. stars radius will
209                                          * vary from 1 to MAX_STAR_SIZE */
210         SinVariable x;          /* Evolving variables:               */
211         SinVariable y;          /* Center of the field on the screen */
212         SinVariable z;
213         SinVariable sizex;      /* Evolving variable: half width  of the field */
214         SinVariable sizey;      /* Evolving variable: half height of the field */
215         SinVariable thetax;     /* Evolving Variables:              */
216         SinVariable thetay;     /* rotation angles of the starfield */
217         SinVariable thetaz;     /* around x, y and z local axis     */
218         Star       *star;       /* List of stars */
219         XArc       *xarc;       /* Current List of arcs */
220         XArc       *xarcleft;   /* additional list for the left arcs */
221 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
222         XArc       *oldxarc;    /* Old list of arcs */
223         XArc       *oldxarcleft;
224 #endif
225         unsigned long color;    /* Current color of the starfield */
226         int         colorp;     /* Pointer to color of the starfield */
227         int         NbStars;    /* Number of stars */
228         short       colorchange;        /* Counter for the color change */
229 #if (ADAPT_ERASE == 1)
230         short       hasbeenchecked;
231         long        rect_time;
232         long        xarc_time;
233 #endif
234 } StarField;
235
236 static StarField *starfield = NULL;
237
238 /*********/
239 static void
240 sinvary(SinVariable * v)
241 /*********/
242
243 {
244         v->value = v->minimum +
245                 (v->maximum - v->minimum) * (sin(v->alpha) + 1.0) / 2.0;
246
247         if (v->mayrand == 0)
248                 v->alpha += v->step;
249         else {
250                 int         vaval = NRAND(100);
251
252                 if (vaval <= v->mayrand)
253                         sinvary(v->varrand);
254                 v->alpha += (100.0 + (v->varrand->value)) * v->step / 100.0;
255         }
256
257         if (v->alpha > 2 * M_PI)
258                 v->alpha -= 2 * M_PI;
259 }
260
261 /*************************************************/
262 static void
263 sininit(SinVariable * v,
264         double alpha, double step, double minimum, double maximum,
265         short int mayrand)
266 {
267         v->alpha = alpha;
268         v->step = step;
269         v->minimum = minimum;
270         v->maximum = maximum;
271         v->mayrand = mayrand;
272         if (mayrand != 0) {
273                 if (v->varrand == NULL)
274                         v->varrand = (SinVariable *) calloc(1, sizeof (SinVariable));
275                 sininit(v->varrand,
276                         VARRANDALPHA,
277                         VARRANDSTEP,
278                         VARRANDMIN,
279                         VARRANDMAX,
280                         0);
281                 sinvary(v->varrand);
282         }
283         /* We calculate the values at least once for initialization */
284         sinvary(v);
285 }
286
287 static void
288 sinfree(SinVariable * point)
289 {
290         SinVariable *temp, *next;
291
292         next = point->varrand;
293         while (next) {
294                 temp = next;
295                 next = temp->varrand;
296                 (void) free((void *) temp);
297         }
298 }
299
300
301 /***************/
302 ENTRYPOINT 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 ENTRYPOINT 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 #ifdef HAVE_COCOA       /* Don't second-guess Quartz's double-buffering */
547     XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
548 #endif
549
550 #if (ADAPT_ERASE == 1)
551         struct timeval tv1;
552         struct timeval tv2;
553
554 #endif
555
556 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
557         short       x_1, y_1, x_2, y_2;
558
559         /* bounding rectangle around the old starfield,
560          * for erasing with the smallest rectangle
561          * instead of filling the whole screen */
562         int         maxdiff = 0;        /* maximal distance between left and right */
563
564         /* star in 3d mode, otherwise 0 */
565 #endif
566
567 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
568         if (MI_WIN_IS_USE3D(mi)) {
569                 maxdiff = (int) MAXDIFF;
570         }
571         x_1 = (int) sp->x.value - (int) sp->sizex.value -
572                 sp->max_star_size - maxdiff;
573         y_1 = (int) sp->y.value - (int) sp->sizey.value -
574                 sp->max_star_size;
575         x_2 = 2 * ((int) sp->sizex.value + sp->max_star_size + maxdiff);
576         y_2 = 2 * ((int) sp->sizey.value + sp->max_star_size);
577 #endif
578         /* We make variables vary. */
579         sinvary(&sp->thetax);
580         sinvary(&sp->thetay);
581         sinvary(&sp->thetaz);
582
583         sinvary(&sp->x);
584         sinvary(&sp->y);
585         if (MI_WIN_IS_USE3D(mi))
586                 sinvary(&sp->z);
587
588         /* A little trick to prevent the bouboule from being
589          * bigger than the screen */
590         sp->sizex.maximum =
591                 MIN(((double) sp->width) - sp->x.value,
592                     sp->x.value);
593         sp->sizex.minimum = sp->sizex.maximum / 3.0;
594
595         /* Another trick to make the ball not too flat */
596         sp->sizey.minimum =
597                 MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
598                     sp->sizey.maximum / 3.0);
599         sp->sizey.maximum =
600                 MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
601                     MIN(((double) sp->height) - sp->y.value,
602                         sp->y.value));
603
604         sinvary(&sp->sizex);
605         sinvary(&sp->sizey);
606
607         /*
608          * We calculate the rotation matrix values. We just make the
609          * rotation on the fly, without using a matrix.
610          * Star positions are recorded as unit vectors pointing in various
611          * directions. We just make them all rotate.
612          */
613         CX = cos(sp->thetax.value);
614         SX = sin(sp->thetax.value);
615         CY = cos(sp->thetay.value);
616         SY = sin(sp->thetay.value);
617         CZ = cos(sp->thetaz.value);
618         SZ = sin(sp->thetaz.value);
619
620         for (i = 0; i < sp->NbStars; i++) {
621                 star = &(sp->star[i]);
622                 arc = &(sp->xarc[i]);
623                 if (MI_WIN_IS_USE3D(mi)) {
624                         arcleft = &(sp->xarcleft[i]);
625                         /* to help the eyes, the starfield is always as wide as */
626                         /* deep, so .sizex.value can be used. */
627                         diff = (int) GETZDIFF(sp->sizex.value *
628                                       ((SY * CX) * star->x + (SX) * star->y +
629                                        (CX * CY) * star->z) + sp->z.value);
630                 }
631                 arc->x = (short) ((sp->sizex.value *
632                                    ((CY * CZ - SX * SY * SZ) * star->x +
633                                     (-CX * SZ) * star->y +
634                                     (SY * CZ + SZ * SX * CY) * star->z) +
635                                    sp->x.value));
636                 arc->y = (short) ((sp->sizey.value *
637                                    ((CY * SZ + SX * SY * CZ) * star->x +
638                                     (CX * CZ) * star->y +
639                                     (SY * SZ - SX * CY * CZ) * star->z) +
640                                    sp->y.value));
641
642                 if (MI_WIN_IS_USE3D(mi)) {
643                         arcleft->x = (short) ((sp->sizex.value *
644                                         ((CY * CZ - SX * SY * SZ) * star->x +
645                                          (-CX * SZ) * star->y +
646                                          (SY * CZ + SZ * SX * CY) * star->z) +
647                                                sp->x.value));
648                         arcleft->y = (short) ((sp->sizey.value *
649                                         ((CY * SZ + SX * SY * CZ) * star->x +
650                                          (CX * CZ) * star->y +
651                                          (SY * SZ - SX * CY * CZ) * star->z) +
652                                                sp->y.value));
653                         arc->x += diff;
654                         arcleft->x -= diff;
655                 }
656                 if (star->size != 0) {
657                         arc->x -= star->size;
658                         arc->y -= star->size;
659                         if (MI_WIN_IS_USE3D(mi)) {
660                                 arcleft->x -= star->size;
661                                 arcleft->y -= star->size;
662                         }
663                 }
664         }
665
666         /* First, we erase the previous starfield */
667         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi))
668                 XSetForeground(display, gc, MI_NONE_COLOR(mi));
669         else
670                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
671
672 #if (ADAPT_ERASE == 1)
673         if (sp->hasbeenchecked == 0) {
674                 /* We just calculate which method is the faster and eventually free
675                  * the oldxarc list */
676                 if (sp->xarc_time >
677                     ADAPT_ARC_PREFERED * sp->rect_time) {
678                         sp->hasbeenchecked = -2;        /* XFillRectangle mode */
679                         (void) free((void *) sp->oldxarc);
680                         sp->oldxarc = NULL;
681                         if (MI_WIN_IS_USE3D(mi)) {
682                                 (void) free((void *) sp->oldxarcleft);
683                           sp->oldxarcleft = NULL;
684                         }
685                 } else {
686                         sp->hasbeenchecked = -1;        /* XFillArcs mode */
687                 }
688         }
689         if (sp->hasbeenchecked == -2) {
690                 /* Erasing is done with XFillRectangle */
691                 XFillRectangle(display, window, gc,
692                                x_1, y_1, x_2, y_2);
693         } else if (sp->hasbeenchecked == -1) {
694                 /* Erasing is done with XFillArcs */
695                 XFillArcs(display, window, gc,
696                           sp->oldxarc, sp->NbStars);
697                 if (MI_WIN_IS_USE3D(mi))
698                         XFillArcs(display, window, gc,
699                                   sp->oldxarcleft, sp->NbStars);
700         } else {
701                 long        usec;
702
703                 if (sp->hasbeenchecked > ADAPT_CHECKS) {
704 #ifdef GETTIMEOFDAY_TWO_ARGS
705                         (void) gettimeofday(&tv1, NULL);
706 #else
707                         (void) gettimeofday(&tv1);
708 #endif
709                         XFillRectangle(display, window, gc,
710                                        x_1, y_1, x_2, y_2);
711 #ifdef GETTIMEOFDAY_TWO_ARGS
712                         (void) gettimeofday(&tv2, NULL);
713 #else
714                         (void) gettimeofday(&tv2);
715 #endif
716                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
717                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
718                                 sp->rect_time += usec + tv2.tv_usec - tv1.tv_usec;
719                                 sp->hasbeenchecked--;
720                         }
721                 } else {
722 #ifdef GETTIMEOFDAY_TWO_ARGS
723                         (void) gettimeofday(&tv1, NULL);
724 #else
725                         (void) gettimeofday(&tv1);
726 #endif
727                         XFillArcs(display, window, gc,
728                                   sp->oldxarc, sp->NbStars);
729                         if (MI_WIN_IS_USE3D(mi))
730                                 XFillArcs(display, window, gc,
731                                           sp->oldxarcleft, sp->NbStars);
732 #ifdef GETTIMEOFDAY_TWO_ARGS
733                         (void) gettimeofday(&tv2, NULL);
734 #else
735                         (void) gettimeofday(&tv2);
736 #endif
737                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
738                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
739                                 sp->xarc_time += usec + tv2.tv_usec - tv1.tv_usec;
740                                 sp->hasbeenchecked--;
741                         }
742                 }
743         }
744 #else
745 #if (USEOLDXARCS == 1)
746         XFillArcs(display, window, gc,
747                   sp->oldxarc, sp->NbStars);
748         if (MI_WIN_IS_USE3D(mi))
749                 XFillArcs(display, window, gc,
750                           sp->oldxarcleft, sp->NbStars);
751 #else
752         XFillRectangle(display, window, gc,
753                        x_1, y_1, x_2, y_2);
754 #endif
755 #endif
756
757         /* Then we draw the new one */
758         if (MI_WIN_IS_USE3D(mi)) {
759                 if (MI_WIN_IS_INSTALL(mi))
760                         XSetFunction(display, gc, GXor);
761                 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
762                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
763                 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
764                 XFillArcs(display, window, gc, sp->xarcleft, sp->NbStars);
765                 if (MI_WIN_IS_INSTALL(mi))
766                         XSetFunction(display, gc, GXcopy);
767         } else {
768                 XSetForeground(display, gc, sp->color);
769                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
770         }
771
772 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
773 #if (ADAPT_ERASE == 1)
774         if (sp->hasbeenchecked >= -1) {
775                 arc = sp->xarc;
776                 sp->xarc = sp->oldxarc;
777                 sp->oldxarc = arc;
778                 if (MI_WIN_IS_USE3D(mi)) {
779                         arcleft = sp->xarcleft;
780                         sp->xarcleft = sp->oldxarcleft;
781                         sp->oldxarcleft = arcleft;
782                 }
783         }
784 #else
785         arc = sp->xarc;
786         sp->xarc = sp->oldxarc;
787         sp->oldxarc = arc;
788         if (MI_WIN_IS_USE3D(mi)) {
789                 arcleft = sp->xarcleft;
790                 sp->xarcleft = sp->oldxarcleft;
791                 sp->oldxarcleft = arcleft;
792         }
793 #endif
794 #endif
795
796         /* We set up the color for the next drawing */
797         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2 &&
798             (++sp->colorchange >= COLOR_CHANGES)) {
799                 sp->colorchange = 0;
800                 if (++sp->colorp >= MI_NPIXELS(mi))
801                         sp->colorp = 0;
802                 sp->color = MI_PIXEL(mi, sp->colorp);
803         }
804 }
805
806 ENTRYPOINT void
807 release_bouboule(ModeInfo * mi)
808 {
809         if (starfield != NULL) {
810                 int         screen;
811
812                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
813                         StarField  *sp = &starfield[screen];
814
815                         if (sp->star)
816                                 (void) free((void *) sp->star);
817                         if (sp->xarc)
818                                 (void) free((void *) sp->xarc);
819                         if (sp->xarcleft)
820                                 (void) free((void *) sp->xarcleft);
821 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
822                         if (sp->oldxarc)
823                                 (void) free((void *) sp->oldxarc);
824                         if (sp->oldxarcleft)
825                                 (void) free((void *) sp->oldxarcleft);
826 #endif
827                         sinfree(&(sp->x));
828                         sinfree(&(sp->y));
829                         sinfree(&(sp->z));
830                         sinfree(&(sp->sizex));
831                         sinfree(&(sp->sizey));
832                         sinfree(&(sp->thetax));
833                         sinfree(&(sp->thetay));
834                         sinfree(&(sp->thetaz));
835                 }
836                 (void) free((void *) starfield);
837                 starfield = NULL;
838         }
839 }
840
841 ENTRYPOINT void
842 reshape_bouboule(ModeInfo * mi, int width, int height)
843 {
844         StarField  *sp = &starfield[MI_SCREEN(mi)];
845         sp->width  = width;
846         sp->height = height;
847     sininit(&sp->x,
848         sp->x.alpha, sp->x.step,
849         ((double) sp->width) / 4.0,
850         3.0 * ((double) sp->width) / 4.0,
851         POSCANRAND);
852     sininit(&sp->y,
853         sp->y.alpha, sp->y.step,
854         ((double) sp->height) / 4.0,
855         3.0 * ((double) sp->height) / 4.0,
856         POSCANRAND);
857 }
858
859 ENTRYPOINT void
860 refresh_bouboule(ModeInfo * mi)
861 {
862         /* Do nothing, it will refresh by itself */
863 }
864
865 XSCREENSAVER_MODULE ("Bouboule", bouboule)