Spinshift
SpinShift (ST Actorname) [to] (ve #4 rotation) (ve #4 translation);
The syntax of this command is:
Spinshift "myActor" to quat(Angle, [n1,n2,n3]) [x,y,z,1];
If you have not done so already, you might like to read the discussion of the Shift Command and Spin Command before proceeding with this section, since much of the terminology is developed in those sections. Also useful for examples is the section on Moving Actors.
An important form of animation manipulates the actors in a scene while leaving the scene (and camera) coordinates fixed. This is why the named actors have names, so that scripts can manipulate them. The commands that we use for this are: SpinShift, ShiftSpin, Shift, Spin, Push, DoShift, DoSpinShift, and DoShiftSpin. These commands are used to move individual actors in two ways. The simplest and most straightforward way is to apply the command to an actor to move it discretely to a new location, perhaps with a new orientation.
In Mathwright32, each actor is, ultimately, only a collection of vertices V in global (scene) coordinates, together with the "geometry" that binds the vertices together (the lines, triangles, quadrilaterals, etc. ) Now, after the actor vertices are defined, the actor is positioned in a scene by actively transforming these vertices to scene coordinates (usually using Translate() and Rotate() in the Actor Script). The fact that these transformations commute with the navigation transformations (Move and Turn) means that these initial positions are independent of viewpoint. That is natural.
Note: In all of this discussion, we assume that Scale() transformations are not used in actor scripts. They are not orthogonal, hence do not belong in general, to the Euclidean group. All of the actors you may create or import in Mathwright32 may be scaled initially as you like to fit the scene without using the Scale() transformation in any case.
The transformations in the Actor Script define the initial position and orientation of the actor, the way it appears when selected into the scene, in terms of the global coordinate system. Together, they may be viewed as a rigid (Euclidean) transformation, I, from R3 to R3. We have already observed that each rigid transformation from R3 to R3 has unique representation as the composition of a rotation about the origin followed by a translation. This is a SpinShift. It also has a (usually different) unique representation as the composition of a translation first, followed by a rotation about the origin. This is a ShiftSpin
. We find it convenient sometimes to use one such representation, sometimes the other, because while, in principle, we can always translate from one to the other, the algebra is sometimes messy.
Each actor therefore has, attached to it two things: A set of vertices, V, referred to the global coordinate system, and a rigid transformation of the global coordinate system to itself that actively carries those vertices to the initial position, defined by its script. Call this initial transformation of R3: I. It is a permanent property of the actor (like the vertices V) as long as the actor script is not changed. "I" will also be called the "Initial Stage Transformation."
Before this initial stage transformation is applied, the actor is simply defined by its vertex coordinates, V. While you will only see an actor in this state before you script it, it will be important to be aware of what that state, V, is when you script motion for an actor. The important thing to understand is that, in order to move any given actor, we must define a Euclidean transformation of all of R3 -- a combination of a spin and a shift. That transformation will not affect other actors, but it will carry the vertices V of the current actor to new vertices, and we will see the effect of that transformation. The Initial Stage transformation, I, defined by each actor's script does just that, for each actor, individually.
This is all well and good. When navigation commands are issued, they affect all actors in the same way, maintaining all relative spatial relations. Now the question is: How do we change the position of a single actor, leaving the others alone? To do this, we define a new Euclidean stage transformation for the actor, one that replaces the Initial Stage transformation, and transforms V to its new destination.
Each of the commands: SpinShift, ShiftSpin, Spin, and Shift defines for the actor a new Stage Transformation. These do not accumulate. Each new one simply replaces the previous one. The actor is simply wherever the last command put it. Thus at any time, the actor's vertices are the image of V under the last SpinShift, ShiftSpin, Spin, or Shift that was called on the actor. In this way, your program "knows" at any time what the position and orientation of each actor is. This is obviously useful and important.
This is why it is important to remember what the vertex set V looks like for each actor. Every motion will transform that vertex set to the desired position and orientation.
If the Stage transformation is the initial one, I, the actor appears as the script initially dictated. If not, it appears -- and remains under all navigation commands -- in the new position and orientation given by the last stage transformation. Whenever the screen is Reset, using the Centering Button, the scene is restored to its original state and all Stage Transformations are restored to the original ones.
Therefore, each actor carries with it, first, the initial coordinates, V, defined by its vertices. Its script defines the initial transformation, I, into the global scene. This defines the initial position and orientation of the actor.
Probably the most useful rigid motion in Mathwright32 is the SpinShift. It is the composition: t o r (where r, a rotation about the origin, is done first, and it is followed by t, a translation).
The syntax of this command is:
Spinshift "myActor" to quat(Angle, [n1,n2,n3]) [x,y,z,1];
Notice that the rotation (spin) is always the first argument in a SpinShift, and the translation (shift) is the second. Also, it is important that the last component of the translation vector be 1.
The effect of this command will be to apply first the rotation quat(Angle, [n1,n2,n3])
to the vertices, V, of "myActor" and then to apply the translation by (x,y,z) to the result.
You will see, of course, a single motion.
You may accumulate rotations in your script as products of quaternions, and accumulate translations as sums.
Note: The quaternion product u*v applied to an actor means v first, then u, just as in function composition.
For example, the loop:
do
step := 0
current := quat(0, [1,0,0])
'current is first the identity rotation'
inc := quat(5, [1,1,1])
until step >= 360 {
Spinshift "myActor" to current, [step/36,0,0,1];
current := inc*current;
'This updates the rotation'
step := step + 5;
}
causes "myActor" to be revolved about the normalized [1,1,1] vector through 360 degrees in steps of 5 degrees, each time being pushed along the x-axis. The effect is a continuous spinning and shifting motion.
Now if the original vertex set V for "myActor" places the "center of mass" of "myActor" at the origin, this will indeed appear to be a natural motion. That is because each spin will then be about the center of mass of "myActor" and so "myActor" will appear, in the motion, to revolve about its center of mass. But if the center of mass of "myActor" as defined by V is not at the origin, then "myActor" may appear to wobble. So it is important to be familiar with the appearance of "myActor" as defined by V.
Fortunately, we may determine the disposition of "myActor" under V when we create V. This is true even for actors imported from .x files. For the surface and geometric actors, it is obviously so. But there will be other motions for which it will not be appropriate to place the center of mass at the origin. For example, a swinging pendulum should probably place the origin at the pivot, or point of support. In these cases, some calculation may be required to give the right motion. And sometimes, we'll want to use ShiftSpin, rather than SpinShift to obtain the right effect.
Another illustration may be helpful.
Imagine, for example, what is involved in causing a rocket to move in a line at a uniform rate, and to rotate about the line of the trajectory of its center of mass. If the center of mass lies at the origin, this is not difficult to do. Simply choose a unit vector N = [n1, n2, n3] pointing in the direction of its trajectory, and call successively something like:
SpinShift "rocket" to quat(step, [n1,n2,n3]) [step*n1, step*n2, step*n3,1];
Each time called, it will first rotate the rocket about N -- step degrees in this case -- and then shift it along a length equal to step.
But if the center of mass is not on the line through the origin, it is more difficult. This is because the rotation is always a rotation about the origin,. The sequence of commands above would cause the rocket to move in the right direction, but to "orbit" in a spiral a certain line through the origin. The following script launches a space shuttle and rotates it one half turn through 10 degree steps, about the trajectory of its center, which is the line x = 2.5, z = 0.
program launch (ex step) {
do
i := 0
until i > 180 {
spinshift "shuttle" to quat(i, [0,1,0]), [(2.5*(1-cos(radian(i)))), i/36, 2.5*sin(radian(i)),1];
i := i+step;
}
}
(See Moving Actors for a picture.)
Here, the rotation quat(i, [0,1,0]) is done first, causing a revolution about the y-axis through i-degrees. After that revolution, the translation:
[(2.5*(1-cos(radian(i)))),i/36-10, 2.5*sin(radian(i)),1]
moves the shuttle back to the line: x = 2.5, z = 0 with the correct (rotational) orientation, and translates it vertically (in the y-direction) through i/36 units.
It was important for this simulation to do the full motion at once, and not to rotate first, draw, then translate, and draw at each step. The latter would give a "jerky" appearance to the launch. In principle, any motion desired can be obtained using SpinShift in this way. But some Geometry is required to discover the right transformations.
Notice again that you may enter a quaternion as a 4-tuple. If you execute
quat( a, [x,y,z])
on the command line, the system prints the quaternion value. And the quaternion product u*v applied to an actor means v first, then u, just as in function composition.
See Also