http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.04.2.tar.gz
[xscreensaver] / hacks / glx / rotator.c
1 /* xscreensaver, Copyright (c) 1998-2002 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include "config.h"
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <math.h>
16 #include "rotator.h"
17 #include "yarandom.h"
18
19 struct rotator {
20
21   double spin_x_speed, spin_y_speed, spin_z_speed;
22   double wander_speed;
23
24   double rotx, roty, rotz;         /* current object rotation */
25   double dx, dy, dz;               /* current rotational velocity */
26   double ddx, ddy, ddz;            /* current rotational acceleration */
27   double d_max;                    /* max rotational velocity */
28
29   int wander_frame;                /* position in the wander cycle */
30
31 };
32
33
34 #undef ABS
35 #define ABS(x) ((x)<0?-(x):(x))
36
37 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
38 #define RANDSIGN() ((random() & 1) ? 1 : -1)
39
40 static void
41 rotate_1 (double *pos, double *v, double *dv, double speed, double max_v)
42 {
43   double ppos = *pos;
44
45   if (speed == 0) return;
46
47   /* tick position */
48   if (ppos < 0)
49     ppos = -(ppos + *v);
50   else
51     ppos += *v;
52
53   if (ppos > 1.0)
54     ppos -= 1.0;
55   else if (ppos < 0)
56     ppos += 1.0;
57
58   if (ppos < 0) abort();
59   if (ppos > 1.0) abort();
60   *pos = (*pos > 0 ? ppos : -ppos);
61
62   /* accelerate */
63   *v += *dv;
64
65   /* clamp velocity */
66   if (*v > max_v || *v < -max_v)
67     {
68       *dv = -*dv;
69     }
70   /* If it stops, start it going in the other direction. */
71   else if (*v < 0)
72     {
73       if (random() % 4)
74         {
75           *v = 0;
76
77           /* keep going in the same direction */
78           if (random() % 2)
79             *dv = 0;
80           else if (*dv < 0)
81             *dv = -*dv;
82         }
83       else
84         {
85           /* reverse gears */
86           *v = -*v;
87           *dv = -*dv;
88           *pos = -*pos;
89         }
90     }
91
92   /* Alter direction of rotational acceleration randomly. */
93   if (! (random() % 120))
94     *dv = -*dv;
95
96   /* Change acceleration very occasionally. */
97   if (! (random() % 200))
98     {
99       if (*dv == 0)
100         *dv = 0.00001;
101       else if (random() & 1)
102         *dv *= 1.2;
103       else
104         *dv *= 0.8;
105     }
106 }
107
108
109 /* Returns a rotator object, which encapsulates rotation and motion state.
110
111    spin_[xyz]_speed indicates the relative speed of rotation.
112    Specify 0 if you don't want any rotation around that axis.
113
114    spin_accel specifies a scaling factor for the acceleration that is
115    randomly applied to spin: if you want the speed to change faster,
116    make this > 1.
117
118    wander_speed indicates the relative speed through space.
119
120    If randomize_initial_state_p is true, then the initial position and
121    rotation will be randomized (even if the spin speeds are 0.)  If it
122    is false, then all values will be initially zeroed.
123  */
124 rotator *
125 make_rotator (double spin_x_speed,
126               double spin_y_speed,
127               double spin_z_speed,
128               double spin_accel,
129               double wander_speed,
130               int randomize_initial_state_p)
131 {
132   rotator *r = (rotator *) calloc (1, sizeof(*r));
133   double d, dd;
134
135   if (!r) return 0;
136
137   if (spin_x_speed < 0 || spin_y_speed < 0 || spin_z_speed < 0 ||
138       wander_speed < 0)
139     abort();
140
141   r->spin_x_speed = spin_x_speed;
142   r->spin_y_speed = spin_y_speed;
143   r->spin_z_speed = spin_z_speed;
144   r->wander_speed = wander_speed;
145
146   if (randomize_initial_state_p)
147     {
148       r->rotx = frand(1.0) * RANDSIGN();
149       r->roty = frand(1.0) * RANDSIGN();
150       r->rotz = frand(1.0) * RANDSIGN();
151
152       r->wander_frame = random() % 0xFFFF;
153     }
154   else
155     {
156       r->rotx = r->roty = r->rotz = 0;
157       r->wander_frame = 0;
158     }
159
160   d  = 0.006;
161   dd = 0.00006;
162
163   r->dx = BELLRAND(d * r->spin_x_speed);
164   r->dy = BELLRAND(d * r->spin_y_speed);
165   r->dz = BELLRAND(d * r->spin_z_speed);
166
167   r->d_max = r->dx * 2;
168
169   r->ddx = (dd + frand(dd+dd)) * r->spin_x_speed * spin_accel;
170   r->ddy = (dd + frand(dd+dd)) * r->spin_y_speed * spin_accel;
171   r->ddz = (dd + frand(dd+dd)) * r->spin_z_speed * spin_accel;
172
173 # if 0
174   fprintf (stderr, "rotator:\n");
175   fprintf (stderr, "   wander: %3d %6.2f\n", r->wander_frame, r->wander_speed);
176   fprintf (stderr, "    speed: %6.2f %6.2f %6.2f\n",
177            r->spin_x_speed, r->spin_y_speed, r->spin_z_speed);
178   fprintf (stderr, "      rot: %6.2f %6.2f %6.2f\n",
179            r->rotx, r->roty, r->rotz);
180   fprintf (stderr, "        d: %6.2f %6.2f %6.2f, %6.2f\n",
181            r->dx, r->dy, r->dz,
182            r->d_max);
183   fprintf (stderr, "       dd: %6.2f %6.2f %6.2f\n",
184            r->ddx, r->ddy, r->ddz);
185 # endif
186
187   return r;
188 }
189
190
191 void
192 free_rotator (rotator *r)
193 {
194   free (r);
195 }
196
197 void
198 get_rotation (rotator *rot, double *x_ret, double *y_ret, double *z_ret,
199               int update_p)
200 {
201   double x, y, z;
202
203   if (update_p) {
204     rotate_1 (&rot->rotx, &rot->dx, &rot->ddx, rot->spin_x_speed, rot->d_max);
205     rotate_1 (&rot->roty, &rot->dy, &rot->ddy, rot->spin_y_speed, rot->d_max);
206     rotate_1 (&rot->rotz, &rot->dz, &rot->ddz, rot->spin_z_speed, rot->d_max);
207   }
208
209   x = rot->rotx;
210   y = rot->roty;
211   z = rot->rotz;
212   if (x < 0) x = 1 - (x + 1);
213   if (y < 0) y = 1 - (y + 1);
214   if (z < 0) z = 1 - (z + 1);
215
216   if (x_ret) *x_ret = x;
217   if (y_ret) *y_ret = y;
218   if (z_ret) *z_ret = z;
219 }
220
221
222 void
223 get_position (rotator *rot, double *x_ret, double *y_ret, double *z_ret,
224               int update_p)
225 {
226   double x = 0.5, y = 0.5, z = 0.5;
227
228   if (rot->wander_speed != 0)
229     {
230       if (update_p)
231         rot->wander_frame++;
232
233 # define SINOID(F) ((1 + sin((rot->wander_frame * (F)) / 2 * M_PI)) / 2.0)
234       x = SINOID (0.71 * rot->wander_speed);
235       y = SINOID (0.53 * rot->wander_speed);
236       z = SINOID (0.37 * rot->wander_speed);
237 # undef SINOID
238     }
239
240   if (x_ret) *x_ret = x;
241   if (y_ret) *y_ret = y;
242   if (z_ret) *z_ret = z;
243 }