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