244 lines
5.9 KiB
C
244 lines
5.9 KiB
C
|
/* xscreensaver, Copyright (c) 1998-2002 Jamie Zawinski <jwz@jwz.org>
|
||
|
*
|
||
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
||
|
* documentation for any purpose is hereby granted without fee, provided that
|
||
|
* the above copyright notice appear in all copies and that both that
|
||
|
* copyright notice and this permission notice appear in supporting
|
||
|
* documentation. No representations are made about the suitability of this
|
||
|
* software for any purpose. It is provided "as is" without express or
|
||
|
* implied warranty.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <math.h>
|
||
|
#include "rotator.h"
|
||
|
#include "random.h"
|
||
|
|
||
|
struct rotator {
|
||
|
|
||
|
double spin_x_speed, spin_y_speed, spin_z_speed;
|
||
|
double wander_speed;
|
||
|
|
||
|
double rotx, roty, rotz; /* current object rotation */
|
||
|
double dx, dy, dz; /* current rotational velocity */
|
||
|
double ddx, ddy, ddz; /* current rotational acceleration */
|
||
|
double d_max; /* max rotational velocity */
|
||
|
|
||
|
int wander_frame; /* position in the wander cycle */
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
#undef ABS
|
||
|
#define ABS(x) ((x)<0?-(x):(x))
|
||
|
|
||
|
#define FLOATRAND(a) (((double)LRAND() / (double)MAXRAND) * a)
|
||
|
#define BELLRAND(n) ((FLOATRAND((n)) + FLOATRAND((n)) + FLOATRAND((n))) / 3)
|
||
|
#define RANDSIGN() ((LRAND() & 1) ? 1 : -1)
|
||
|
|
||
|
static void
|
||
|
rotate_1 (double *pos, double *v, double *dv, double speed, double max_v)
|
||
|
{
|
||
|
double ppos = *pos;
|
||
|
|
||
|
if (speed == 0) return;
|
||
|
|
||
|
/* tick position */
|
||
|
if (ppos < 0)
|
||
|
ppos = -(ppos + *v);
|
||
|
else
|
||
|
ppos += *v;
|
||
|
|
||
|
if (ppos > 1.0)
|
||
|
ppos -= 1.0;
|
||
|
else if (ppos < 0)
|
||
|
ppos += 1.0;
|
||
|
|
||
|
if (ppos < 0) abort();
|
||
|
if (ppos > 1.0) abort();
|
||
|
*pos = (*pos > 0 ? ppos : -ppos);
|
||
|
|
||
|
/* accelerate */
|
||
|
*v += *dv;
|
||
|
|
||
|
/* clamp velocity */
|
||
|
if (*v > max_v || *v < -max_v)
|
||
|
{
|
||
|
*dv = -*dv;
|
||
|
}
|
||
|
/* If it stops, start it going in the other direction. */
|
||
|
else if (*v < 0)
|
||
|
{
|
||
|
if (LRAND() % 4)
|
||
|
{
|
||
|
*v = 0;
|
||
|
|
||
|
/* keep going in the same direction */
|
||
|
if (LRAND() % 2)
|
||
|
*dv = 0;
|
||
|
else if (*dv < 0)
|
||
|
*dv = -*dv;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* reverse gears */
|
||
|
*v = -*v;
|
||
|
*dv = -*dv;
|
||
|
*pos = -*pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Alter direction of rotational acceleration randomly. */
|
||
|
if (! (LRAND() % 120))
|
||
|
*dv = -*dv;
|
||
|
|
||
|
/* Change acceleration very occasionally. */
|
||
|
if (! (LRAND() % 200))
|
||
|
{
|
||
|
if (*dv == 0)
|
||
|
*dv = 0.00001;
|
||
|
else if (LRAND() & 1)
|
||
|
*dv *= 1.2;
|
||
|
else
|
||
|
*dv *= 0.8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Returns a rotator object, which encapsulates rotation and motion state.
|
||
|
|
||
|
spin_[xyz]_speed indicates the relative speed of rotation.
|
||
|
Specify 0 if you don't want any rotation around that axis.
|
||
|
|
||
|
spin_accel specifies a scaling factor for the acceleration that is
|
||
|
randomly applied to spin: if you want the speed to change faster,
|
||
|
make this > 1.
|
||
|
|
||
|
wander_speed indicates the relative speed through space.
|
||
|
|
||
|
If randomize_initial_state_p is true, then the initial position and
|
||
|
rotation will be randomized (even if the spin speeds are 0.) If it
|
||
|
is false, then all values will be initially zeroed.
|
||
|
*/
|
||
|
rotator *
|
||
|
make_rotator (double spin_x_speed,
|
||
|
double spin_y_speed,
|
||
|
double spin_z_speed,
|
||
|
double spin_accel,
|
||
|
double wander_speed,
|
||
|
int randomize_initial_state_p)
|
||
|
{
|
||
|
rotator *r = (rotator *) calloc (1, sizeof(*r));
|
||
|
double d, dd;
|
||
|
|
||
|
if (!r) return 0;
|
||
|
|
||
|
if (spin_x_speed < 0 || spin_y_speed < 0 || spin_z_speed < 0 ||
|
||
|
wander_speed < 0)
|
||
|
abort();
|
||
|
|
||
|
r->spin_x_speed = spin_x_speed;
|
||
|
r->spin_y_speed = spin_y_speed;
|
||
|
r->spin_z_speed = spin_z_speed;
|
||
|
r->wander_speed = wander_speed;
|
||
|
|
||
|
if (randomize_initial_state_p)
|
||
|
{
|
||
|
r->rotx = FLOATRAND(1.0) * RANDSIGN();
|
||
|
r->roty = FLOATRAND(1.0) * RANDSIGN();
|
||
|
r->rotz = FLOATRAND(1.0) * RANDSIGN();
|
||
|
|
||
|
r->wander_frame = LRAND() % 0xFFFF;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
r->rotx = r->roty = r->rotz = 0;
|
||
|
r->wander_frame = 0;
|
||
|
}
|
||
|
|
||
|
d = 0.006;
|
||
|
dd = 0.00006;
|
||
|
|
||
|
r->dx = BELLRAND(d * r->spin_x_speed);
|
||
|
r->dy = BELLRAND(d * r->spin_y_speed);
|
||
|
r->dz = BELLRAND(d * r->spin_z_speed);
|
||
|
|
||
|
r->d_max = r->dx * 2;
|
||
|
|
||
|
r->ddx = (dd + FLOATRAND(dd+dd)) * r->spin_x_speed * spin_accel;
|
||
|
r->ddy = (dd + FLOATRAND(dd+dd)) * r->spin_y_speed * spin_accel;
|
||
|
r->ddz = (dd + FLOATRAND(dd+dd)) * r->spin_z_speed * spin_accel;
|
||
|
|
||
|
# if 0
|
||
|
fprintf (stderr, "rotator:\n");
|
||
|
fprintf (stderr, " wander: %3d %6.2f\n", r->wander_frame, r->wander_speed);
|
||
|
fprintf (stderr, " speed: %6.2f %6.2f %6.2f\n",
|
||
|
r->spin_x_speed, r->spin_y_speed, r->spin_z_speed);
|
||
|
fprintf (stderr, " rot: %6.2f %6.2f %6.2f\n",
|
||
|
r->rotx, r->roty, r->rotz);
|
||
|
fprintf (stderr, " d: %6.2f %6.2f %6.2f, %6.2f\n",
|
||
|
r->dx, r->dy, r->dz,
|
||
|
r->d_max);
|
||
|
fprintf (stderr, " dd: %6.2f %6.2f %6.2f\n",
|
||
|
r->ddx, r->ddy, r->ddz);
|
||
|
# endif
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
free_rotator (rotator *r)
|
||
|
{
|
||
|
free (r);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
get_rotation (rotator *rot, double *x_ret, double *y_ret, double *z_ret,
|
||
|
int update_p)
|
||
|
{
|
||
|
double x, y, z;
|
||
|
|
||
|
if (update_p) {
|
||
|
rotate_1 (&rot->rotx, &rot->dx, &rot->ddx, rot->spin_x_speed, rot->d_max);
|
||
|
rotate_1 (&rot->roty, &rot->dy, &rot->ddy, rot->spin_y_speed, rot->d_max);
|
||
|
rotate_1 (&rot->rotz, &rot->dz, &rot->ddz, rot->spin_z_speed, rot->d_max);
|
||
|
}
|
||
|
|
||
|
x = rot->rotx;
|
||
|
y = rot->roty;
|
||
|
z = rot->rotz;
|
||
|
if (x < 0) x = 1 - (x + 1);
|
||
|
if (y < 0) y = 1 - (y + 1);
|
||
|
if (z < 0) z = 1 - (z + 1);
|
||
|
|
||
|
if (x_ret) *x_ret = x;
|
||
|
if (y_ret) *y_ret = y;
|
||
|
if (z_ret) *z_ret = z;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
get_position (rotator *rot, double *x_ret, double *y_ret, double *z_ret,
|
||
|
int update_p)
|
||
|
{
|
||
|
double x = 0.5, y = 0.5, z = 0.5;
|
||
|
|
||
|
if (rot->wander_speed != 0)
|
||
|
{
|
||
|
if (update_p)
|
||
|
rot->wander_frame++;
|
||
|
|
||
|
# define SINOID(F) ((1 + sin((rot->wander_frame * (F)) / 2 * M_PI)) / 2.0)
|
||
|
x = SINOID (0.71 * rot->wander_speed);
|
||
|
y = SINOID (0.53 * rot->wander_speed);
|
||
|
z = SINOID (0.37 * rot->wander_speed);
|
||
|
# undef SINOID
|
||
|
}
|
||
|
|
||
|
if (x_ret) *x_ret = x;
|
||
|
if (y_ret) *y_ret = y;
|
||
|
if (z_ret) *z_ret = z;
|
||
|
}
|