1 /* xscreensaver, Copyright (c) 1998-2016 Jamie Zawinski <jwz@jwz.org>
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
25 double spin_x_speed, spin_y_speed, spin_z_speed; /* scaling factors >= 0. */
28 double rotx, roty, rotz; /* current object rotation, -1 to +1.
29 Sign indicates direction of motion.
30 0.25 means +90 deg, positive velocity.
31 -0.25 means +90 deg, negative velocity
32 (not +270 deg or -90 deg!)
35 double dx, dy, dz; /* current rotational velocity, >= 0. */
36 double ddx, ddy, ddz; /* current rotational acceleration, +/-. */
37 double d_max; /* max rotational velocity, > 0. */
39 int wander_frame; /* position in the wander cycle, >= 0. */
44 #define ABS(x) ((x)<0?-(x):(x))
46 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
47 #define RANDSIGN() ((random() & 1) ? 1 : -1)
49 /* Stay in the range [0-1).
53 #define CLAMP(i) do { \
54 while ((i) < 0) (i)++; \
55 while ((i) >= 1) (i)--; \
59 #define EPSILON 0.000001
63 rotate_1 (double *pos, double *v, double *dv, double speed, double max_v)
65 /* Sign of *pos is direction of motion.
66 Sign of *v is always positive.
67 It would make way more sense for *v to indicate direction of motion.
73 if (speed == 0) return;
77 /* Ignore but preserve the sign on ppos. It's kind of like:
78 ppos = old_sign * (abs(ppos) + (v * old_sign))
79 This is why it would make more sense for that sign bit to be on v.
86 *pos = (*pos > 0 ? ppos : -ppos); /* preserve old sign bit on pos. */
92 if (*v > max_v || *v < -max_v)
96 /* If it stops, start it going in the other direction. */
97 /* Since *v is always positive, <= 0 means stopped. */
102 *v = 0; /* don't let velocity be negative */
104 if (random() % 2) /* stay stopped, and kill acceleration */
106 else if (*dv < 0) /* was decelerating, accelerate instead */
111 *v = -*v; /* switch to tiny positive velocity, or zero */
112 *dv = -*dv; /* toggle acceleration */
113 *pos = -*pos; /* reverse direction of motion */
117 /* Alter direction of rotational acceleration randomly. */
118 if (! (random() % 120))
121 /* Change acceleration very occasionally. */
122 if (! (random() % 200))
124 #if 0 /* this might make more sense: */
125 if (*dv > -EPSILON && *dv < EPSILON)
126 *dv += 10 * (*dv < 0 ? -EPSILON : EPSILON);
131 else if (random() & 1)
139 /* Returns a rotator object, which encapsulates rotation and motion state.
141 spin_[xyz]_speed indicates the relative speed of rotation.
142 Specify 0 if you don't want any rotation around that axis.
144 spin_accel specifies a scaling factor for the acceleration that is
145 randomly applied to spin: if you want the speed to change faster,
148 wander_speed indicates the relative speed through space.
150 If randomize_initial_state_p is true, then the initial position and
151 rotation will be randomized (even if the spin speeds are 0.) If it
152 is false, then all values will be initially zeroed.
155 make_rotator (double spin_x_speed,
160 int randomize_initial_state_p)
162 rotator *r = (rotator *) calloc (1, sizeof(*r));
167 if (spin_x_speed < 0 || spin_y_speed < 0 || spin_z_speed < 0 ||
171 r->spin_x_speed = spin_x_speed;
172 r->spin_y_speed = spin_y_speed;
173 r->spin_z_speed = spin_z_speed;
174 r->wander_speed = wander_speed;
176 if (randomize_initial_state_p)
178 /* Sign on position is direction of travel. Stripped before returned. */
179 r->rotx = frand(1.0) * RANDSIGN();
180 r->roty = frand(1.0) * RANDSIGN();
181 r->rotz = frand(1.0) * RANDSIGN();
183 r->wander_frame = random() % 0xFFFF;
187 r->rotx = r->roty = r->rotz = 0;
194 r->dx = BELLRAND(d * r->spin_x_speed);
195 r->dy = BELLRAND(d * r->spin_y_speed);
196 r->dz = BELLRAND(d * r->spin_z_speed);
198 r->d_max = r->dx * 2;
200 r->ddx = (dd + frand(dd+dd)) * r->spin_x_speed * spin_accel;
201 r->ddy = (dd + frand(dd+dd)) * r->spin_y_speed * spin_accel;
202 r->ddz = (dd + frand(dd+dd)) * r->spin_z_speed * spin_accel;
205 fprintf (stderr, "rotator:\n");
206 fprintf (stderr, " wander: %3d %6.2f\n", r->wander_frame, r->wander_speed);
207 fprintf (stderr, " speed: %6.2f %6.2f %6.2f\n",
208 r->spin_x_speed, r->spin_y_speed, r->spin_z_speed);
209 fprintf (stderr, " rot: %6.2f %6.2f %6.2f\n",
210 r->rotx, r->roty, r->rotz);
211 fprintf (stderr, " d: %6.2f %6.2f %6.2f, %6.2f\n",
214 fprintf (stderr, " dd: %6.2f %6.2f %6.2f\n",
215 r->ddx, r->ddy, r->ddz);
223 free_rotator (rotator *r)
229 get_rotation (rotator *rot, double *x_ret, double *y_ret, double *z_ret,
235 rotate_1 (&rot->rotx, &rot->dx, &rot->ddx, rot->spin_x_speed, rot->d_max);
236 rotate_1 (&rot->roty, &rot->dy, &rot->ddy, rot->spin_y_speed, rot->d_max);
237 rotate_1 (&rot->rotz, &rot->dz, &rot->ddz, rot->spin_z_speed, rot->d_max);
247 if (x_ret) *x_ret = x;
248 if (y_ret) *y_ret = y;
249 if (z_ret) *z_ret = z;
254 get_position (rotator *rot, double *x_ret, double *y_ret, double *z_ret,
257 double x = 0.5, y = 0.5, z = 0.5;
259 if (rot->wander_speed != 0)
264 # define SINOID(F) ((1 + sin((rot->wander_frame * (F)) / 2 * M_PI)) / 2.0)
265 x = SINOID (0.71 * rot->wander_speed);
266 y = SINOID (0.53 * rot->wander_speed);
267 z = SINOID (0.37 * rot->wander_speed);
271 if (x_ret) *x_ret = x;
272 if (y_ret) *y_ret = y;
273 if (z_ret) *z_ret = z;