From http://www.jwz.org/xscreensaver/xscreensaver-5.38.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 release_bouboule 0
92 # define bouboule_handle_event 0
93 # include "xlockmore.h"                         /* from the xscreensaver distribution */
94 #else  /* !STANDALONE */
95 # include "xlock.h"                                     /* from the xlockmore distribution */
96 # define ENTRYPOINT /**/
97 #endif /* !STANDALONE */
98
99 ENTRYPOINT 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 /***************/
303 ENTRYPOINT void
304 init_bouboule(ModeInfo * mi)
305 /***************/
306
307 /*-
308  *  The stars init part was first inspirated from the net3d game starfield
309  * code.  But net3d starfield is not really 3d starfield, and I needed real 3d,
310  * so only remains the net3d starfield initialization main idea, that is
311  * the stars distribution on a sphere (theta and omega computing)
312  */
313 {
314         StarField  *sp;
315         int         size = MI_SIZE(mi);
316         int         i;
317         double      theta, omega;
318
319         MI_INIT (mi, starfield);
320         sp = &starfield[MI_SCREEN(mi)];
321
322         sp->width = MI_WIN_WIDTH(mi);
323         sp->height = MI_WIN_HEIGHT(mi);
324
325         /* use the right `black' pixel values: */
326         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi)) {
327                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_NONE_COLOR(mi));
328                 XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
329                                0, 0, sp->width, sp->height);
330         } else
331                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
332
333         if (size < -MINSIZE)
334                 sp->max_star_size = NRAND(-size - MINSIZE + 1) + MINSIZE;
335         else if (size < MINSIZE)
336                 sp->max_star_size = MINSIZE;
337         else
338                 sp->max_star_size = size;
339
340         sp->NbStars = MI_BATCHCOUNT(mi);
341         if (sp->NbStars < -MINSTARS) {
342                 if (sp->star) {
343                         (void) free((void *) sp->star);
344                         sp->star = NULL;
345                 }
346                 if (sp->xarc) {
347                         (void) free((void *) sp->xarc);
348                         sp->xarc = NULL;
349                 }
350                 if (sp->xarcleft) {
351                         (void) free((void *) sp->xarcleft);
352                         sp->xarcleft = NULL;
353                 }
354 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
355                 if (sp->oldxarc) {
356                         (void) free((void *) sp->oldxarc);
357                         sp->oldxarc = NULL;
358                 }
359                 if (sp->oldxarcleft) {
360                         (void) free((void *) sp->oldxarcleft);
361                         sp->oldxarcleft = NULL;
362                 }
363 #endif
364                 sp->NbStars = NRAND(-sp->NbStars - MINSTARS + 1) + MINSTARS;
365         } else if (sp->NbStars < MINSTARS)
366                 sp->NbStars = MINSTARS;
367
368         /* We get memory for lists of objects */
369         if (sp->star == NULL)
370                 sp->star = (Star *) malloc(sp->NbStars * sizeof (Star));
371         if (sp->xarc == NULL)
372                 sp->xarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
373         if (MI_WIN_IS_USE3D(mi) && sp->xarcleft == NULL)
374                 sp->xarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
375 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
376         if (sp->oldxarc == NULL)
377                 sp->oldxarc = (XArc *) malloc(sp->NbStars * sizeof (XArc));
378         if (MI_WIN_IS_USE3D(mi) && sp->oldxarcleft == NULL)
379                 sp->oldxarcleft = (XArc *) malloc(sp->NbStars * sizeof (XArc));
380 #endif
381
382         {
383                 /* We initialize evolving variables */
384                 sininit(&sp->x,
385                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
386                         ((double) sp->width) / 4.0,
387                         3.0 * ((double) sp->width) / 4.0,
388                         POSCANRAND);
389                 sininit(&sp->y,
390                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
391                         ((double) sp->height) / 4.0,
392                         3.0 * ((double) sp->height) / 4.0,
393                         POSCANRAND);
394
395                 /* for z, we have to ensure that the bouboule does not get behind */
396                 /* the eyes of the viewer.  His/Her eyes are at 0.  Because the */
397                 /* bouboule uses the x-radius for the z-radius, too, we have to */
398                 /* use the x-values. */
399                 sininit(&sp->z,
400                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
401                         ((double) sp->width / 2.0 + MINZVAL),
402                         ((double) sp->width / 2.0 + MAXZVAL),
403                         POSCANRAND);
404
405
406                 sininit(&sp->sizex,
407                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
408                         MIN(((double) sp->width) - sp->x.value,
409                             sp->x.value) / 5.0,
410                         MIN(((double) sp->width) - sp->x.value,
411                             sp->x.value),
412                         SIZECANRAND);
413
414                 sininit(&sp->sizey,
415                         NRAND(3142) / 1000.0, M_PI / (NRAND(100) + 100.0),
416                         MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
417                             sp->sizey.maximum / 5.0),
418                         MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
419                             MIN(((double) sp->height) -
420                                 sp->y.value,
421                                 sp->y.value)),
422                         SIZECANRAND);
423
424                 sininit(&sp->thetax,
425                         NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
426                         -M_PI, M_PI,
427                         THETACANRAND);
428                 sininit(&sp->thetay,
429                         NRAND(3142) / 1000.0, M_PI / (NRAND(200) + 200.0),
430                         -M_PI, M_PI,
431                         THETACANRAND);
432                 sininit(&sp->thetaz,
433                         NRAND(3142) / 1000.0, M_PI / (NRAND(400) + 400.0),
434                         -M_PI, M_PI,
435                         THETACANRAND);
436         }
437         for (i = 0; i < sp->NbStars; i++) {
438                 Star       *star;
439                 XArc       *arc = NULL, *arcleft = NULL;
440 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
441         XArc       *oarc = NULL, *oarcleft = NULL;
442 #endif
443
444                 star = &(sp->star[i]);
445                 arc = &(sp->xarc[i]);
446                 if (MI_WIN_IS_USE3D(mi))
447                         arcleft = &(sp->xarcleft[i]);
448 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
449                 oarc = &(sp->oldxarc[i]);
450                 if (MI_WIN_IS_USE3D(mi))
451                         oarcleft = &(sp->oldxarcleft[i]);
452 #endif
453                 /* Elevation and bearing of the star */
454                 theta = dtor((NRAND(1800)) / 10.0 - 90.0);
455                 omega = dtor((NRAND(3600)) / 10.0 - 180.0);
456
457                 /* Stars coordinates in a 3D space */
458                 star->x = cos(theta) * sin(omega);
459                 star->y = sin(omega) * sin(theta);
460                 star->z = cos(omega);
461
462                 /* We set the stars size */
463                 star->size = NRAND(2 * sp->max_star_size);
464                 if (star->size < sp->max_star_size)
465                         star->size = 0;
466                 else
467                         star->size -= sp->max_star_size;
468
469                 /* We set default values for the XArc lists elements */
470                 arc->x = arc->y = 0;
471                 if (MI_WIN_IS_USE3D(mi)) {
472                         arcleft->x = arcleft->y = 0;
473                 }
474 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
475                 oarc->x = oarc->y = 0;
476                 if (MI_WIN_IS_USE3D(mi)) {
477                         oarcleft->x = oarcleft->y = 0;
478                 }
479 #endif
480                 arc->width = 2 + star->size;
481                 arc->height = 2 + star->size;
482                 if (MI_WIN_IS_USE3D(mi)) {
483                         arcleft->width = 2 + star->size;
484                         arcleft->height = 2 + star->size;
485                 }
486 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
487                 oarc->width = 2 + star->size;
488                 oarc->height = 2 + star->size;
489                 if (MI_WIN_IS_USE3D(mi)) {
490                         oarcleft->width = 2 + star->size;
491                         oarcleft->height = 2 + star->size;
492                 }
493 #endif
494
495                 arc->angle1 = 0;
496                 arc->angle2 = 360 * 64;
497                 if (MI_WIN_IS_USE3D(mi)) {
498                         arcleft->angle1 = 0;
499                         arcleft->angle2 = 360 * 64;
500                 }
501 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
502                 oarc->angle1 = 0;
503                 oarc->angle2 = 360 * 64;        /* ie. we draw whole disks:
504                                                  * from 0 to 360 degrees */
505                 if (MI_WIN_IS_USE3D(mi)) {
506                         oarcleft->angle1 = 0;
507                         oarcleft->angle2 = 360 * 64;
508                 }
509 #endif
510         }
511
512         if (MI_NPIXELS(mi) > 2)
513                 sp->colorp = NRAND(MI_NPIXELS(mi));
514         /* We set up the starfield color */
515         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2)
516                 sp->color = MI_PIXEL(mi, sp->colorp);
517         else
518                 sp->color = MI_WIN_WHITE_PIXEL(mi);
519
520 #if (ADAPT_ERASE == 1)
521         /* We initialize the adaptation code for screen erasing */
522         sp->hasbeenchecked = ADAPT_CHECKS * 2;
523         sp->rect_time = 0;
524         sp->xarc_time = 0;
525 #endif
526 }
527
528 /****************/
529 ENTRYPOINT void
530 draw_bouboule(ModeInfo * mi)
531 /****************/
532
533 {
534         Display    *display = MI_DISPLAY(mi);
535         Window      window = MI_WINDOW(mi);
536         GC          gc = MI_GC(mi);
537         StarField  *sp = &starfield[MI_SCREEN(mi)];
538         int         i, diff = 0;
539         double      CX, CY, CZ, SX, SY, SZ;
540         Star       *star;
541         XArc       *arc = NULL, *arcleft = NULL;
542
543 #ifdef HAVE_JWXYZ       /* Don't second-guess Quartz's double-buffering */
544     XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
545 #endif
546
547 #if (ADAPT_ERASE == 1)
548         struct timeval tv1;
549         struct timeval tv2;
550
551 #endif
552
553 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
554         short       x_1, y_1, x_2, y_2;
555
556         /* bounding rectangle around the old starfield,
557          * for erasing with the smallest rectangle
558          * instead of filling the whole screen */
559         int         maxdiff = 0;        /* maximal distance between left and right */
560
561         /* star in 3d mode, otherwise 0 */
562 #endif
563
564 #if ((USEOLDXARCS == 0) || (ADAPT_ERASE == 1))
565         if (MI_WIN_IS_USE3D(mi)) {
566                 maxdiff = (int) MAXDIFF;
567         }
568         x_1 = (int) sp->x.value - (int) sp->sizex.value -
569                 sp->max_star_size - maxdiff;
570         y_1 = (int) sp->y.value - (int) sp->sizey.value -
571                 sp->max_star_size;
572         x_2 = 2 * ((int) sp->sizex.value + sp->max_star_size + maxdiff);
573         y_2 = 2 * ((int) sp->sizey.value + sp->max_star_size);
574 #endif
575         /* We make variables vary. */
576         sinvary(&sp->thetax);
577         sinvary(&sp->thetay);
578         sinvary(&sp->thetaz);
579
580         sinvary(&sp->x);
581         sinvary(&sp->y);
582         if (MI_WIN_IS_USE3D(mi))
583                 sinvary(&sp->z);
584
585         /* A little trick to prevent the bouboule from being
586          * bigger than the screen */
587         sp->sizex.maximum =
588                 MIN(((double) sp->width) - sp->x.value,
589                     sp->x.value);
590         sp->sizex.minimum = sp->sizex.maximum / 3.0;
591
592         /* Another trick to make the ball not too flat */
593         sp->sizey.minimum =
594                 MAX(sp->sizex.value / MAX_SIZEX_SIZEY,
595                     sp->sizey.maximum / 3.0);
596         sp->sizey.maximum =
597                 MIN(sp->sizex.value * MAX_SIZEX_SIZEY,
598                     MIN(((double) sp->height) - sp->y.value,
599                         sp->y.value));
600
601         sinvary(&sp->sizex);
602         sinvary(&sp->sizey);
603
604         /*
605          * We calculate the rotation matrix values. We just make the
606          * rotation on the fly, without using a matrix.
607          * Star positions are recorded as unit vectors pointing in various
608          * directions. We just make them all rotate.
609          */
610         CX = cos(sp->thetax.value);
611         SX = sin(sp->thetax.value);
612         CY = cos(sp->thetay.value);
613         SY = sin(sp->thetay.value);
614         CZ = cos(sp->thetaz.value);
615         SZ = sin(sp->thetaz.value);
616
617         for (i = 0; i < sp->NbStars; i++) {
618                 star = &(sp->star[i]);
619                 arc = &(sp->xarc[i]);
620                 if (MI_WIN_IS_USE3D(mi)) {
621                         arcleft = &(sp->xarcleft[i]);
622                         /* to help the eyes, the starfield is always as wide as */
623                         /* deep, so .sizex.value can be used. */
624                         diff = (int) GETZDIFF(sp->sizex.value *
625                                       ((SY * CX) * star->x + (SX) * star->y +
626                                        (CX * CY) * star->z) + sp->z.value);
627                 }
628                 arc->x = (short) ((sp->sizex.value *
629                                    ((CY * CZ - SX * SY * SZ) * star->x +
630                                     (-CX * SZ) * star->y +
631                                     (SY * CZ + SZ * SX * CY) * star->z) +
632                                    sp->x.value));
633                 arc->y = (short) ((sp->sizey.value *
634                                    ((CY * SZ + SX * SY * CZ) * star->x +
635                                     (CX * CZ) * star->y +
636                                     (SY * SZ - SX * CY * CZ) * star->z) +
637                                    sp->y.value));
638
639                 if (MI_WIN_IS_USE3D(mi)) {
640                         arcleft->x = (short) ((sp->sizex.value *
641                                         ((CY * CZ - SX * SY * SZ) * star->x +
642                                          (-CX * SZ) * star->y +
643                                          (SY * CZ + SZ * SX * CY) * star->z) +
644                                                sp->x.value));
645                         arcleft->y = (short) ((sp->sizey.value *
646                                         ((CY * SZ + SX * SY * CZ) * star->x +
647                                          (CX * CZ) * star->y +
648                                          (SY * SZ - SX * CY * CZ) * star->z) +
649                                                sp->y.value));
650                         arc->x += diff;
651                         arcleft->x -= diff;
652                 }
653                 if (star->size != 0) {
654                         arc->x -= star->size;
655                         arc->y -= star->size;
656                         if (MI_WIN_IS_USE3D(mi)) {
657                                 arcleft->x -= star->size;
658                                 arcleft->y -= star->size;
659                         }
660                 }
661         }
662
663         /* First, we erase the previous starfield */
664         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi))
665                 XSetForeground(display, gc, MI_NONE_COLOR(mi));
666         else
667                 XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
668
669 #if (ADAPT_ERASE == 1)
670         if (sp->hasbeenchecked == 0) {
671                 /* We just calculate which method is the faster and eventually free
672                  * the oldxarc list */
673                 if (sp->xarc_time >
674                     ADAPT_ARC_PREFERED * sp->rect_time) {
675                         sp->hasbeenchecked = -2;        /* XFillRectangle mode */
676                         (void) free((void *) sp->oldxarc);
677                         sp->oldxarc = NULL;
678                         if (MI_WIN_IS_USE3D(mi)) {
679                                 (void) free((void *) sp->oldxarcleft);
680                           sp->oldxarcleft = NULL;
681                         }
682                 } else {
683                         sp->hasbeenchecked = -1;        /* XFillArcs mode */
684                 }
685         }
686         if (sp->hasbeenchecked == -2) {
687                 /* Erasing is done with XFillRectangle */
688                 XFillRectangle(display, window, gc,
689                                x_1, y_1, x_2, y_2);
690         } else if (sp->hasbeenchecked == -1) {
691                 /* Erasing is done with XFillArcs */
692                 XFillArcs(display, window, gc,
693                           sp->oldxarc, sp->NbStars);
694                 if (MI_WIN_IS_USE3D(mi))
695                         XFillArcs(display, window, gc,
696                                   sp->oldxarcleft, sp->NbStars);
697         } else {
698                 long        usec;
699
700                 if (sp->hasbeenchecked > ADAPT_CHECKS) {
701 #ifdef GETTIMEOFDAY_TWO_ARGS
702                         (void) gettimeofday(&tv1, NULL);
703 #else
704                         (void) gettimeofday(&tv1);
705 #endif
706                         XFillRectangle(display, window, gc,
707                                        x_1, y_1, x_2, y_2);
708 #ifdef GETTIMEOFDAY_TWO_ARGS
709                         (void) gettimeofday(&tv2, NULL);
710 #else
711                         (void) gettimeofday(&tv2);
712 #endif
713                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
714                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
715                                 sp->rect_time += usec + tv2.tv_usec - tv1.tv_usec;
716                                 sp->hasbeenchecked--;
717                         }
718                 } else {
719 #ifdef GETTIMEOFDAY_TWO_ARGS
720                         (void) gettimeofday(&tv1, NULL);
721 #else
722                         (void) gettimeofday(&tv1);
723 #endif
724                         XFillArcs(display, window, gc,
725                                   sp->oldxarc, sp->NbStars);
726                         if (MI_WIN_IS_USE3D(mi))
727                                 XFillArcs(display, window, gc,
728                                           sp->oldxarcleft, sp->NbStars);
729 #ifdef GETTIMEOFDAY_TWO_ARGS
730                         (void) gettimeofday(&tv2, NULL);
731 #else
732                         (void) gettimeofday(&tv2);
733 #endif
734                         usec = (tv2.tv_sec - tv1.tv_sec) * 1000000;
735                         if (usec + tv2.tv_usec - tv1.tv_usec > 0) {
736                                 sp->xarc_time += usec + tv2.tv_usec - tv1.tv_usec;
737                                 sp->hasbeenchecked--;
738                         }
739                 }
740         }
741 #else
742 #if (USEOLDXARCS == 1)
743         XFillArcs(display, window, gc,
744                   sp->oldxarc, sp->NbStars);
745         if (MI_WIN_IS_USE3D(mi))
746                 XFillArcs(display, window, gc,
747                           sp->oldxarcleft, sp->NbStars);
748 #else
749         XFillRectangle(display, window, gc,
750                        x_1, y_1, x_2, y_2);
751 #endif
752 #endif
753
754         /* Then we draw the new one */
755         if (MI_WIN_IS_USE3D(mi)) {
756                 if (MI_WIN_IS_INSTALL(mi))
757                         XSetFunction(display, gc, GXor);
758                 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
759                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
760                 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
761                 XFillArcs(display, window, gc, sp->xarcleft, sp->NbStars);
762                 if (MI_WIN_IS_INSTALL(mi))
763                         XSetFunction(display, gc, GXcopy);
764         } else {
765                 XSetForeground(display, gc, sp->color);
766                 XFillArcs(display, window, gc, sp->xarc, sp->NbStars);
767         }
768
769 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
770 #if (ADAPT_ERASE == 1)
771         if (sp->hasbeenchecked >= -1) {
772                 arc = sp->xarc;
773                 sp->xarc = sp->oldxarc;
774                 sp->oldxarc = arc;
775                 if (MI_WIN_IS_USE3D(mi)) {
776                         arcleft = sp->xarcleft;
777                         sp->xarcleft = sp->oldxarcleft;
778                         sp->oldxarcleft = arcleft;
779                 }
780         }
781 #else
782         arc = sp->xarc;
783         sp->xarc = sp->oldxarc;
784         sp->oldxarc = arc;
785         if (MI_WIN_IS_USE3D(mi)) {
786                 arcleft = sp->xarcleft;
787                 sp->xarcleft = sp->oldxarcleft;
788                 sp->oldxarcleft = arcleft;
789         }
790 #endif
791 #endif
792
793         /* We set up the color for the next drawing */
794         if (!MI_WIN_IS_USE3D(mi) && MI_NPIXELS(mi) > 2 &&
795             (++sp->colorchange >= COLOR_CHANGES)) {
796                 sp->colorchange = 0;
797                 if (++sp->colorp >= MI_NPIXELS(mi))
798                         sp->colorp = 0;
799                 sp->color = MI_PIXEL(mi, sp->colorp);
800         }
801 }
802
803 ENTRYPOINT void
804 free_bouboule(ModeInfo * mi)
805 {
806         StarField  *sp = &starfield[MI_SCREEN(mi)];
807
808         if (sp->star)
809                 (void) free((void *) sp->star);
810         if (sp->xarc)
811                 (void) free((void *) sp->xarc);
812         if (sp->xarcleft)
813                 (void) free((void *) sp->xarcleft);
814 #if ((USEOLDXARCS == 1) || (ADAPT_ERASE == 1))
815         if (sp->oldxarc)
816                 (void) free((void *) sp->oldxarc);
817         if (sp->oldxarcleft)
818                 (void) free((void *) sp->oldxarcleft);
819 #endif
820         sinfree(&(sp->x));
821         sinfree(&(sp->y));
822         sinfree(&(sp->z));
823         sinfree(&(sp->sizex));
824         sinfree(&(sp->sizey));
825         sinfree(&(sp->thetax));
826         sinfree(&(sp->thetay));
827         sinfree(&(sp->thetaz));
828 }
829
830 ENTRYPOINT void
831 reshape_bouboule(ModeInfo * mi, int width, int height)
832 {
833         StarField  *sp = &starfield[MI_SCREEN(mi)];
834         sp->width  = width;
835         sp->height = height;
836     sininit(&sp->x,
837         sp->x.alpha, sp->x.step,
838         ((double) sp->width) / 4.0,
839         3.0 * ((double) sp->width) / 4.0,
840         POSCANRAND);
841     sininit(&sp->y,
842         sp->y.alpha, sp->y.step,
843         ((double) sp->height) / 4.0,
844         3.0 * ((double) sp->height) / 4.0,
845         POSCANRAND);
846 }
847
848 #ifndef STANDALONE
849 ENTRYPOINT void
850 refresh_bouboule(ModeInfo * mi)
851 {
852         /* Do nothing, it will refresh by itself */
853 }
854 #endif
855
856 XSCREENSAVER_MODULE ("Bouboule", bouboule)