Welcome to Mathscript 3D.

Personal computers are rapidly becoming the portals to dynamic three dimensional worlds of color, sound, and stunning visual effects. They are heuristic machines, and they can bring virtual worlds of the imagination to life before our eyes. In this way, they invite a new quality of participation in, and a deeper understanding of the geometry and dynamics of our models of the world.

 With Mathscript 3D, you will be able to render geometric constructions and models of physical processes in a two or three-dimensional canvas that will endow them with visually compelling reality. In Mathscript graph3D objects, you may simulate a flight over a natural landscape, or through a strange attractor that you create; you may observe the stately phases of the moon, or the chaotic vibrations of a forced double pendulum. You may build and control an articulated robot arm, or study the behavior of spinning tops. It's up to you.

  Mathwright32 is a Simulation Toolkit. It will invite you to create simulations that will give your readers (Players) unique opportunities to visualize, and to participate in, exciting ideas and constructions of mathematics and science, in the interplay between geometry, graphics, and art, and in the underlying logical principles that bind them all together in this vibrant new mind-tool, the three-dimensional desktop. And of course, the simulations you build can support and develop intuition in the broad range of topics in mathematics and science that you may choose to develop.

  Almost all of the literature that develops and discusses the techniques of 3D graphics is written in a patois that is, at best, an uncomfortable combination of C++ and geometric linear algebra. This was necessary, but is unfortunate, because it tends to obscure the interesting themes of this subject by burying them in the details of implementation. Because of this, it has been difficult for anyone but seasoned programmers to have immediate access to the fruit of this new technology. And since the mathematics required to achieve realistic simulations of motion is difficult to implement at the level of C++, there has been little progress in creating expressive and versatile 3D simulations for the desktop. This situation has just changed.

The OpenGL Architecture Review Board has, by abstracting and separating the issues of quality graphics performance from those of implementation on a particular platform (or language), taken a very important step towards making the creation of powerful graphics more accessible to everyone.

  Creating powerful and convincing simulations still requires very careful attention both to mathematics, and to programming. But in the proper language environment, it is possible to make the necessary tools available at a conceptual level of abstraction that integrates the mathematics and the 3D graphics in a natural way, and encourages top-down planning and design. Mathscript 3D is built on an object-oriented paradigm that focuses on "actors" and their behaviors in a "scene." These actors are "objects" that you may create and manipulate at a high level of abstraction. You determine their behaviors with "scripts." These scripts are of course programs, but they may be developed incrementally, and may be tested interactively as you build your project. This is important, because it means that your project does not have to be complete in order to work at all. You may focus attention on details as you get to them, easily testing and modifying behaviors while the project is running in order to get them right. Mathscript 3D inherits from LISP, Logo and from SmallTalk these insights into the programmer's craft.

  The language that you use to create and manipulate the objects is called Mathscript, and it contains (essentially verbatim) an interactive version of a large part of OpenGL, as well as a powerful language for creating mathematical objects and object hierarchies. This means that your scripts can also be abstract, focusing on ideas above implementation details. And we hope that you will find in Mathscript a direct route (if not a Royal Road) to translating your ideas into working simulations.

  Needless to say, it is not enough to have available pretty graphics to build dynamic and interactive environments that can demonstrate powerful ideas in mathematics and science. The underlying language that controls the graphics must be smart and flexible enough to enable you to transform your ideas easily into responsive and dynamic interactions. Mathscript is an extensible object-oriented computer algebra language with over 275 built-in functions, programs, and commands that is designed to build simulations. It is an extensible language. You may create your own programs and commands within it, and even design your own command language, if need be, to support your simulation. As an interactive scripting language, it makes it easy for you to design and test your simulations step-by-step, in small, conceptually simple pieces in our graph3D objects.

  The earliest version of Mathscript was developed under the sponsorship of the Mathematics Association of America, in collaboration with hundreds of college mathematics and science teachers, during the five year tenure of the Interactive Mathematics Text Project (funded by IBM Corporation and the National Science Foundation). Since Mathscript has no difficulty with symbolic algebra and calculus, it will support models that require real-time solution of differential equations, or exact solutions of systems of algebraic equations, and will display the symbolic results in a legible 2-dimensional symbolic form. And as an object-oriented language, it will allow you to construct object hierarchies, even extending them at run-time, to simulate the orchestrated motion of articulated parts of complex 3 dimensional objects.

  OpenGL is a powerful rendering language that will display your scenes and allow the user to interact with them in real time. Another aim of Mathscript 3D is to enable you to learn and to experiment with this remarkable language. For that, Mathscript contains 57 primitive OpenGL commands, and their OpenGL constants and principal state variables in interactive form. In addition, it contains 19 new Mathscript 3D animation commands that work with OpenGL to support interactive simulations. This means that you (and your readers) may use OpenGL in essentially its original form (its syntax blends seamlessly with Mathscript syntax) to experiment with geometry and graphics interactively.

And since Mathscript is so tightly integrated with OpenGL, it can in fact be an excellent tool to learn and experiment with OpenGL itself. In its scripts, you will see the results of your graphics instructions immediately, however, without the need to compile them into C++. This is a great advantage for building simulations that must be fine-tuned over many incremental cycles. But Mathscript is also a highly flexible language that allows you to create your own commands, to build your own object hierarchies, and to create the mathematical objects that will support your simulations.

  What distinguishes Mathscript 3D from other 3D Graphics environments is the fact that the "actors" in the scenes you create are dynamic. They may be moved individually either interactively (at the command line) or under the control of scripts that you write. They may be selected by the reader when she clicks on them, and may respond in appropriate ways with the full Euclidean group of motions. Further, the reader may navigate your scenes by flying through them in the first person, or by moving the entire scene itself. This makes possible a large variety of realistic experiments and simulations that it would be difficult to imagine supporting on a 2 dimensional canvas.

  The actors that populate your 2 or 3 dimensional scenes come in 6 types. .

  Mathscript 3D scenes are collections of actors of any of these types, situated in the 2 or 3-dimensional canvas, and given any initial orientation, size, material properties, and so on, that you desire in individual OpenGL "actor scripts." When you save the scenes you create for a graph3D object, they are compiled into Mathscript 3D scene file format (.sce) and are then brought back to be viewed and manipulated at any future time. Normally, they are all brought back automatically by graph3D object of which they are a part. You may, however, add any members of any scene file to the current scene.

  Now a number of first rate 3D rendering programs are available to create static 3D scenes of the type we are discussing. We mention again that what is special and different about a Mathscript 3D scene is the ability you (or your Reader) will have to move the actors individually, or to navigate through a scene under the control of scripts (short programs) that you write in Mathscript.

  Mathscript graph3D objects are rather large and complex, and a few tips about the Windows idiom for navigating menus, and for context-sensitive help will be useful here. Each graph3D object (like all display objects in Mathscript) has its own menu of properties, actions, and utilities. The most important property is, of course, the scene that it holds. The scene consists of actors which may be selected into or out of the current Display List. Use the Scene, Create Display List for this. When an actor is in the current Display List, it is visible in the viewport, otherwise, it is not. You may actually remove an actor from a scene entirely by selecting Actor, Edit an Actor, Remove (if the actor is already removed from the display list).

  It is time to say something about the system requirements for these graphics effects. Small scenes (5 to 10 objects, say) are rendered in real-time motion on a Pentium system running at about 90 MHZ, without any hardware graphics acceleration at all! More complex scenes in which there is a lot of motion may begin to slow down a bit. But at the time of this writing (Spring, 2002), 3D graphics accelerator cards have become standard equipment on new machines. This, and the increased speed of the chip, promises to boost performance by an order of magnitude. These improvements will have a direct impact on the Windows implementation of OpenGL. Our problem then will be to slow the animations down. Foreseeing this, we use the system clock so that you (or your user) may tune performance to the system.

Actors and Scenes

  The arena in which your simulations take place is called a graph3D "scene." It is the 2D or 3D microworld that you construct in which you make things happen. As you work in 3 dimensions, most of the "actors" in that world have names, and your scripts (programs) manipulate them by name.

  If you want to work in 2-dimensions,  then you may arrange for that simply by placing in the Setup Actor Script the following:

matrixmode(GL_PROJECTION);
loadidentity();
ortho(-10,10,-10,10,20, -1000);
matrixmode(GL_MODELVIEW);
loadidentity();

       This will allow you to mimic some of the behavior of our Graph2D gadgets, and to populate the graph3D with rotating "sprites" that are now 3D models. To work temporarily in 2-dimensions in 3D mode, then change ViewPort Setting to 2D by selecting:  Scene, Toggle Perspective View. 

In graph2D gadgets, the actors are called sprites. They have names. Analogous objects in graph3D objects may be treated as sprites in the above 2D mode. Other actors (called anonymous actors) are created by your scripts. These latter actors do not have names, and will remain on the screen only until you clear it with the refresh command. Again, in 3 dimensions, the analogous surface and tube commands create named actors.

Examples of anonymous actors might be trajectories of ordinary differential equations, or polygonal paths recorded to "trace" the motion of another actor. They may be other things, such as detailed color maps representing local behavior of analytic functions, or the "invisible" actors that guide the detailed motion (translation and rotation) of sets of actors (like an Earth-Moon-Sun system, for example).

  You may create and attach scripts to named actors themselves (These are activated when the object is "selected" or "picked" by the Reader). At any time, objects have fixed positions and orientations with respect to the global coordinate system (the scene coordinates). These actors are viewed from an imaginary "camera." This camera has its own coordinate system (the camera coordinates) attached to it.

  The graph3D window shows either the perspective or the orthogonal projection of what is within the field of view of the camera. When you work in orthogonal projection mode (for example, using the Setup Script above), then the viewport represents essentially a 2-dimensional screen, although objects may still be "rotated" as well as translated.

  A scene is a collection of named actors. If you load a full scene, an .sce file, it brings a set of named actors into the workspace (replacing any others that might have been there). You may also add to your scene any actors from other .sce files, as you wish. You then create a scene by selecting any of the actors into it from the workspace. These named actors are always created in the first place from the Actors menu, or imported from other file formats, as described below. For each actor, you may give it a position and orientation in the scene and give it various other properties by typing a "script" for it. These scripts consist of Mathscript and OpenGL statements. Thus, you define the properties (position, orientation, color, material properties, such as reflectivity, and scale) of the named actors in their scripts, and then save the collection of named actors to disk in an .sce file.

  The camera metaphor is useful for navigation in a 3D scene. Imagine that the camera flies through the scene. It is intuitive and natural in many circumstances to navigate with reference to the camera's own local coordinate system. For example, "forward" and "up" always have the same meaning in that case, as do the rotations: "pitch", "roll", and "yaw." Another way to navigate is to click with the mouse on certain objects in the scene, thereby causing the camera to move appropriately. In other circumstances, however, it is useful to navigate by means of active transformations of the scene in the global frame. One knows that to rotate an object about some fixed axis is not quite equivalent to revolving the camera. You will have your choice of frame (camera or scene) when you navigate, and if you generally use the camera coordinates, you will find that intuitive and familiar.

  Objects in a scene may be rendered in a variety of ways. You may choose to view them as solid objects, or as wireframe objects. And you may choose to show only the faces of the object that point toward the camera, or to show both forward and backward facing faces. If, for example, you "enter" a building, then it makes sense to make the inner walls (inward pointing faces, generally) visible. The orientation of faces will be explained later. Usually, showing only forward faces renders more quickly than showing both types.

Surfaces are "lit" by the default light. Your scripts may create and position other lights, but at least one light must be "on" at any time. These lights have properties that your script may control (such as color, brightness, whether it is diffuse or focused, etc.) And the surfaces of objects have reflective properties (shiny or dull, etc.) that you may also control. The lighting effects lend a stunning realism to the objects depicted. In addition, parametric surfaces may have textures that give them whatever appearance is desired (wood, metal, lunar surface, etc.). When one object is "in front" of another (with reference to the camera) the former obscures the part of the latter that it blocks. All of these things are handled with quiet virtuosity by OpenGL, and usually, you will not have to think about them at all.

  You create the actors in a scene, either interactively in scripts and programs, from the Actors menu, or by importing them from other files. There are 2 types of actors, "anonymous actors" and "named actors." Anonymous actors are created interactively by your scripts and from the command line. Named actors are created from the Actors menu, or are imported from other programs, and you may save sets of them to disk in (.sce) and eventually in project (.mwp) files. Once created, these actors are compiled into "display lists" that execute quickly and smoothly.

  Among the types of anonymous actors are:

Solution curves to ordinary differential equations

A Lorenz Attractor

 

Tracks of motion of other objects (like the logo turtle paths).

Graphs of functions, and curves in space

Color coded representations of analytic functions, or other maps from the plane to the plane

The Identity map of the complex plane

The map z -> (z+5*i)*(z-5*i)/z

Arbitrary polyhedra defined using triangular simplices, quadrilaterals, polygons, etc. Built-in OpenGL objects from the "Aux" library, such as Platonic solids, spheres, tori, cones, etc. Any desired combination of these with lights, textures, and so on.

  The named actors are of 5 types:

Geometric Actors: arbitrary polyhedra defined using triangular simplices, quadrilaterals, polygons, together with the Built-in OpenGL objects from the "Aux" library, such as Platonic solids, spheres, tori, cones, etc.

A Simple Robot

Implicit Surface Actors: any surface defined by an equation of the form f(x,y,z)=0. The function f(x,y,z) is a Mathscript function, and may be defined algebraically, or by using inequalities or even by logical conditions.

Implicit Surface: (x^2+y^2+z^2+2)^2 = 9*(y^2+z^2)

Parametric Surface Actors: These are defined as vector-valued functions mapping a rectangle to the scene: [s,t] ->[ x(s,t), y(s,t), z(s,t) ]. Again, these functions are Mathscript functions which may be defined rather arbitrarily. See also the Surface Command.

Parametric Surface: Immersion of a Klein Bottle

Space Curve Actors: Functions of the form: t ->[x(t), y(t), z(t)] mapping an interval to the scene. These are created as hollow "tubes" (with any desired thickness) when the curve is differentiable and satsfies a certain regularity condition. Otherwise, they may be created in the usual way with 0 thickness.

Tube: Lissajous Curve

Imported Actors: These are actors defined in Direct3D x-file format. They are imported (that is, translated) to Mathscript 3D native graphics format and rendered there by OpenGL. On import, all material properties (colors, shininess, transparency, etc.) are retained. Normal vectors for lighting are automatically recomputed, textures may be reassigned, and the objects may be scaled and moved to fit the target scene. Objects in 3D Studio format may be translated first to Direct3D using its conv3ds.exe utility, and then imported as usual. Once in Mathscript 3D, they may be "scripted" in the same way as all named actors.

Import from Direct3D: A Table

  The difference between named and anonymous actors is this. Named actors may be manipulated interactively and individually by your scripts and programs. They are "objects" in the scene. They may be "selected" by viewers (on left mouse click). Anonymous actors are created by your scripts and programs in the first place. They might be integral curves of ordinary differential equations, or three dimensional illustrations of Brownian motion, for example.

  In general, you may think of Mathscript 3D as implementing "retained mode" graphics, as opposed to "immediate mode" graphics. Both types of actors are "retained," that is, remain on the screen through various actions (such as navigation and other animation), but anonymous actors may not be manipulated individually by your scripts, or selected. Named actors are placed in the scene, or removed from it, by selecting them into it either by program, or from a menu. Anonymous actors are removed when you "refresh" the screen from the menu, or when you issue the refresh command from a program.

Interactive Command-line Graphics: 3D

  The Interactive command-line functions and commands for 3D Graphics fall into two groups:

 The 57 Interactive OpenGL functions (within Actor Scripts or using the glDraw {...} Protocol):

 A] Properties of Graph3D Object Listing:

CLEARCOLOR(EX red, EX green, EX blue, EX alpha)
CULLFACE( GLConstant mode )
GLCOLOR (EX red,EX green, EX blue,EX alpha);
LIGHT( EX thelight, EX pname, EX param , 0, 0, 0);
LINEWIDTH( EX width)
POINTSIZE( EX size)

B] Utilities of Graph3D Object Listing:

DISABLE (GLConstant capability)
ENABLE (GLConstant capability)
FLUSH ()
FRUSTUM(EX left, EX right,EX bottom,EX top,EX znear, EX zfar )
GLCLEAR(GLConstant buffer)
LOADIDENTITY()
MAKECURRENT ()
MAKENOTCURRENT ()
MATRIXMODE( GLConstant mode)
NORMAL(EX x, EX y, EX z)
ORTHO( EX x1, EX x2, EX y1, EX y2, EX z1, EX z2)
PERSPECTIVE( EX fovy, EX aspect, EX zNear, EX zFar )
POPMATRIX ()
PUSHMATRIX ()
QUATERNION (VE #4 q)
RECT( EX x1, EX y1, EX x2, EX y2)
RENDERMODE( GLConstant mode)
SHADEMODEL( GLConstant mode)
SWAPBUFFERS ()
VERTEX (VE #3 v)
VERTEX2 ( EX x, EX y)
VERTEX3 ( EX x, EX y, EX z)
VERTEX4 ( EX x, EX y, EX z, Ex w)
VIEWPORT ( EX x1, EX y1, EX x2, EX y2)

C] Generic OpenGL Actor Listing:

BEGIN (GLConstant mode)
END()
PICK()
QUAT (EX angle, VE #3 direction)
SCALE(EX x, EX y, EX z)
SOLIDBOX( EX x, EX y, EX z)
SOLIDCONE(EX x, EX y)
SOLIDCUBE( EX size)
SOLIDCYLINDER( EX x, EX y)
SOLIDDODECAHEDRON( EX radius)
SOLIDICOSAHEDRON( EX radius)
SOLIDOCTAHEDRON( EX radius)
SOLIDSPHERE( EX radius)
SOLIDTEAPOT( EX x)
SOLIDTORUS(EX x, EX y)
WIREBOX( EX x, EX y, EX z)
WIRECONE(EX x, EX y)
WIRECUBE( EX size)
WIRECYLINDER( EX x, EX y)
WIREDODECAHEDRON( EX size)
WIREICOSAHEDRON( EX size)
WIREOCTAHEDRON( EX size)
WIRESPHERE( EX size)
WIRETEAPOT( EX x)
WIRETORUS(EX x, EX y) 

The 19 Mathscript Scene and Actor Manipulation Functions and Commands listed below:

DOSHIFT
DOSHIFTSPIN
DOSPINSHIFT
LOADSCENE
MOVE
PUSH
REFRESH
ROTATE
SHIFT
SHIFTSPIN
SPIN
SPINSHIFT
SURFACE
SURFACEDOMAIN
S
URFACEREFINEMENT
TRANSLATE
TUBE

TURN

Protocol:

GLDraw{ ... }

Origins in Logo

Much of the pedagogic attraction of the Logo Computer Language that Seymour Papert introduced in the early '80s lay in the strong interaction between computational logic and geometry that the language disclosed. Logo was a command language then, largely because all interpreted languages were. Nevertheless, the power of Logo lay in its LISP origins: it enabled players easily to build communities of interacting programs incrementally, creating structure as they went, checking and debugging their plans using the visual feedback that the turtle provided. Often this meant that the Player would design clusters of programs in small steps.  

Mathscript 3D offers the same sort of interaction. One of our Tutorials develops a 3-dimensional Logo-like environment, with its own command language, in which learners may experiment in an open-ended way with geometry and logic. Interactions in this environment will take place most naturally on a Command Line. And complex scenarios may be constructed by writing programs in TextBoxes.

  The glDraw Protocol is the low-level entry point to OpenGL's wide range of functions. It makes it possible easily to experiment with geometry at the command line. While there is a built in Tube Command in Mathscript 3D to graph curves, we show here how one could write a 2 dimensional graph command like the following, that creates a minimal grapher:

Command glgraph (fn ff)  {    'This creates a new command called GLGraph'

local (ve #2 v2) (ve #3 v3) {  'These Local declarations tell the compiler that v2 and v3 are vectors'

make variable x;

let v2 be [-10,10];    

let re be 500;

let fg(x) be [x, ff(x), 0];   'This defines the vector-valued function (curve) to be drawn.'

let a be v2(1);

let b be v2(2);

let inc be (b-a)/re;

gldraw {                      'Finally call OpenGL with the GLDraw protocol'

begin(GL_LINE_STRIP);         'Draw the graph as a sequence of segments'

do

  i = 0

  until i >= re {

  v3 := fg(a+i*inc);          'This object form is the next vertex'

  vertex(v3);                 'Give the vertex to OpenGL'

  i := i+1;

}

end();

}

}

}

Then

glgraph sin;

or

make f(x) x^2/10;

glgraph f;

will draw the graphs, with a difference.  You may rotate these!

For Three Dimensional graphics from the command line, two special Mathscript Commands are noteworthy. The Surface Command actually creates parametric surfaces which are named actors from the command line. These surfaces may therefore later be manipulated by Spin, Shift, SpinShift, ShiftSpin, Install, DoShift, DoShiftSpin, DoSpinShift and Select commands.  

The Surface command does several things. This command may be used in 3 ways:

In each case, you may set the domain and drawing refinement either with the Surfacedomain, and Surfacerefinement commands or directly from the Actors, create parametric surface menu. Once you create the actor with this command, you may define a script for it (say to change its color) in the Actor, Edit Script Menu. And you may save a collection of surfaces permanently to a scene file using the Save Scene File As... menu.

1) Draw a graph of a function of 2 variables.

This is the simplest and most direct way to draw graphs of functions of two variables. 

For example if given a real function of 2 variables: g(x,y), it can be graphed by executing:

surface g;

or

draw the surface g;

By the way,

make h 2*g;

surface h;

has the desired effect, just as in graph2D windows.

 

saddle surface drawn from command line

If we choose not to draw filled, the last call causes the wireframe to be drawn instead:

wireframe saddle surface drawn from command line

  In a similar way, the Tube Command draws the image of a mapping from an interval to a box. Such a mapping is a Mathscript function from R -> R^3. We leave it to you to construct a command that draws plane curves from the command line.

Navigation

We will discuss 4 basic animation commands and functions that are useful for navigation in this section:

MOVE

TURN

QUAT (EX angle, VE #3 direction)

PICK()

  The 3 dimensional scene environment, while more natural in a sense than its projection on a plane, takes a certain amount of getting used to. It is easy to get lost in this microworld since the familiar cues (such as gravity) that we use to move around in our own 3D world may be absent. We may find ourselves staring out into space, not knowing where we are, or which way we're heading. For that reason, navigation becomes an important issue for us. All navigation is done by reference to the global Coordinate Frame which is represented in the Setup Actor.

  This coordinate system is color-coded in the Setup actor. The red arrow points in the positive x direction (to the right, initially). The white arrow extends in the positive y direction (up, initially), and the blue arrow points in the positive z direction (out of the screen, towards the viewer initially). There are two quite different types of motion that are available for navigation. We may actively transform the scene (apply an orthonormal affine transformation to everything), or we may move the camera (apply an orthonormal affine transformation to our "eyes," the camera). Both types leave the relative positions and orientations of the actors fixed in the scene. But they are subtly different in the effect they have on our viewpoint.

Active transformations (moving the scene):

  In what follows immediately below, we shall assume "Move the Scene" has been selected, either from the graph3D Menu, or from the selection bar:

  When we described the color-coding of the coordinates above, we said "initially" because if we perform active transformations of the scene, using the global or scene coordinates, then the named arrows will still point in their named directions, but those directions may no longer be to the right, or up, or out. Let us consider navigation by active transformations of the scene first. These will be of two types: motions from the menu, or motions from commands or scripts.

Active transformations move the entire scene (as a whole with reference to an imaginary fixed background frame) in one of two ways: They may translate the scene in some direction, or they may rotate the scene about some axis. Any composition of these rigid motions (the Euclidean affine transformations) will actually be uniquely representable as a rotation about the origin followed by a translation. This six dimensional group of motions is sometimes called the Euclidean group. These are the only way to move space rigidly.

  We have all of these motions of space available to us if we use programs or commands to navigate. But if we use the navigation bar (see the picture below) we have six "generators" of that group. These are the translations in the x,y, and z directions, or rotations (using the right hand rule) about the x,y, and z axes. These translations and rotations are multiples of a predetermined "step" that you may set using the Scene, Set Timer option on the menu. The shorter the delay, the larger the step. You may also reset the system using the "centering button" below.

The Navigation Bar

   

Using the navigation bar, the 6 arrows on the left describe the 6 ways of translating the scene: ±x, ±y, and ±z. By themselves, these are easy to understand. But once rotations are admitted, using the next six buttons (which are color coded), things become a bit more complicated, because the x, y, and z directions have now become mixed together. Motion in the positive x direction still means motion in the direction of the "red arrow" but now you have to look to see what direction that is. And new rotations are similarly affected. Here is a great opportunity to explore the geometric consequences of the non-commutativity of the Euclidean group.

  Now how do we perform these active transformations by command or by script?

There are two special Mathscript commands and one Mathscript function (not OpenGL commands) that we may use: Move, Turn, and Quat. Move and Turn are relative, not absolute motions. Each time they are repeated a new motion results. Each time with reference to the current frame.

  Move is easy (at first). It takes a single argument, a 4-vector, the last component of which is 1. The reason for that is that OpenGL represents all Euclidean motions handily as the 4x4 matrix representation: the Projective transformations. We'll simply always supply a 1 as the last argument and that will not lead us astray. If you type:

Move [x,y,z,1] the scene will be translated by the vector W whose coordinates in our current frame are [x,y,z]. Initially, these will coincide with the global background coordinates ("the fixed stars").

  The Turn command is more interesting. We use the unit quaternions to represent rotations in Mathscript, because that makes it easy to work with them algebraically. A unit quaternion is given by the Quat function whenever we supply an angle t (measured in degrees) and a nonzero 3-vector N. So Quat( t, N ) is one of the two unit quaternions that represents the rotation about the unit vector N through t degrees, where the rotation is calculated using the right-hand rule: point your right thumb in the N direction, and the fingers curl in the direction of turning. You may, of course enterthe quaternion as a 4-tuple:

[t, u, v, w ] is interpreted as: t*1 + u*i + v*j + w*k.

If we say, for example:

Make u quat(30, [1,0,0]);

Make v quat (30, [0,1,0]);

then the setup frame:

is affected in predictable ways by the commands:

Turn u;

Turn v;

But try Turn u*v and then Turn v*u:

Turn u*v

Turn v*u;

Do you see the difference? In the first case the rotation is done first about x ("u") then about y ("v"). In the second case, it is done first about y ("v") then about x ("u"). It is easier to see these things if you execute:

Turn v; then

Turn u;

Instead of Turn v*u all at once. And you will see the result in the first Figure above if you execute:

Turn u; then

Turn v;

On the command line.

  In navigation, quaternions are composed from left to right under multiplication. The amount of turn is of course determined by the angle supplied. And the amount of translation is precisely controlled by the vector argument to Move.   These are the active transformations. We now move to the more intuitive Camera transformations.

Flying transformations (moving the camera):

  When you select "move the camera," navigation becomes a little easier. The reason for this is probably that (as Poincare observed) we first learn motion by moving ourselves, the changes in our perception of space which we consider geometric may always be undone by changing the attitude of our bodies, by turning our heads, walking towards an object, or away from it, and so on.

  So the camera represents our field of view. We should mention that initially, we view a "frustum" that extends from "fixed star coordinates" z = -1 to z = -1000, with a 45 degree field of view . The "origin" of the Setup actor is placed at [0,0,-40] and we face towards it. This becomes the origin of scene coordinates. All global transformations are done with respect to that origin. For example, all global (pure) rotations leave that point fixed. And the solid frustum is projected on the Viewport window.

  The 6 translations on the navigation bar are always in terms of local camera coordinates. So "up" is always up, forward is always straight ahead, and so on. The rotations are also with respect to the camera coordinates. Clockwise rotation about the y axis always has the effect of "turning the camera to the left" so the scene drifts to the right. This is an important point. As the camera moves "up" the scene moves "down." In general, the actions of the camera have the opposite effect (other things being equal) of active transformations of the scene.

  When you select "Move the camera" the Move and Turn commands fly the camera through the scene. We still have to contend with the noncommutativity of the Euclidean group, but it is more natural in this context.

  The following imported image depicts a space station centered on the coordinate origin. The antennae extend along the y-axis.

Space Station

  To see the difference between moving the scene and moving the camera, we show the effect of the command: turn quat( 10, [0,1,0]) in those two circumstances on the imported "space station" above.

turn quat( 10, [0,1,0]) (move the camera):

Space Station (move the camera)

  Notice that the entire Coordinate Frame has moved. In particular, the y-axis and the space station antenna appear to right of center. Now reset the scene, select Move the Scene, and try the same command:

turn quat( 10, [0,1,0]) (move the scene):

Space Station (move the scene)

This time, the y axis remains fixed, but the station has rotated slightly around it.

Quaternions in Mathscript

  We use the unit quaternions to represent rotations in Mathscript, because that makes it easy to work with them algebraically. In fact, Mathscript has a built-in quaternion number type.

Each 4-vector of reals represents a quaternion as follows:

[t, u, v, w ] is interpreted as: t*1 + u*i + v*j + w*k.

  Here, i^2 = j^2 = k^2 = -1; and i*j = -j*i = k; j*k = -k*j = i; k*i = -i*k = j

  These may be multiplied, divided, added and subtracted directly. Thus if you say:

make a [0,1,0,0];

make b [0,0,1,0];

and then type at the command line:

print a*b;

you will see [0,0,0,1]

if you type:

print a/b;

you will see [0,0,0,-1]

  The unit quaternions (t^2+u^2+v^2+w^2 = 1) form a group, and there are precisely two unit quaternions for each rotation of space. Multiplication of unit quaternions corresponds to composition of their rotations.

  A unit quaternion is given by the Quat function whenever we supply an angle t (measured in degrees) and a unit (length-1) 3-vector N. So Quat( t, N ) is one of the two quaternions that represents the rotation about the unit vector N through t degrees, where the rotation is calculated using the right-hand rule: point your right thumb in the N direction, and the fingers curl in the direction of turning. If you supply a vector for N which is not normalized, Mathscript normalizes it for you.

If we say, for example:

Make a quat(30, [1,0,0]);

Make b quat (30, [0,1,0]);

then a represents a rotation of 30 degrees about the x-axis, using the right hand rule. And b represents a 30-degree rotation about the y-axis. And a*b represents the rotation obtained by doing a first, and b next.

Notice that a*b is not the same as b*a.

  The Quat Function should be distinguished from the Quaternion Function. The latter is used to encode a call to OpenGL's Vertex4 function. If P = [x,y,z,t] is a 4-vector, then the call

Quaternion(P);

returns the call Vertex4(x,y,z,t) within a glDraw{...} Protocol. This is useful for establishing rotation steps for DoShift, DoSpinShift, and DoShiftSpin, and is similar in this regard to the Vertex Function.

Moving Actors

            Animation is, of course, a central element in any simulation.  Mathscript has a few commands and conventions that make it easy to animate a scene either interactively, through the navigation bar, or under program control.  The coordinate system is color-coded in the Setup actor.  The red arrow points in the positive x direction (to the right, initially).  The white arrow extends in the positive y direction (up, initially), and the blue arrow points in the positive z direction (out of the screen, towards the viewer initially).  We will discuss 10 basic animation commands and functions that manipulate objects in this section:

DoShift

DoShiftSpin

DoSpinShift

Push

Shift

Spin

ShiftSpin

SpinShift

Pick

Refresh

             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.  The second way is to apply the command to move one or several actors through a "continuous" sequence of motions, for example, to spin a top, to orbit a planet, or to swing a pendulum. 

            Since the second method is a simple extension of the first, we discuss the "discrete" motions first.   Afterwards, we take up the "continuous" versions of these commands.

Discrete Motion:

            First of all, we should observe that 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 Mathwright 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 transformation, I,  from R^3 to R^3.  We have already observed that each rigid transformation from R^3 to R^3 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.  (For those of you familiar with group theory, we may observe that it requires "conjugating" a spin by a translation (or vice versa) and this cannot be done simply with quaternions -- rotations --alone.) 

            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 R^3:  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, as we shall see.  The important thing to understand is that, in order to move any given actor, we must define a Euclidean transformation of all of R^3 -- 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 Restore 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. 

            Mercifully, we don't have to think about this much, because OpenGL is pleased to do all the hard work for us.  But if we want to move an individual actor, we must understand what the vertex set, V, of an actor is.   And then we have four intuitive commands that will intercede for us with OpenGL to work its magic.  Again, the commands we shall need are: SpinShift, ShiftSpin, Shift and Spin.  The Shift and Spin commands are simplest.

            The Syntax of Shift is:

Shift "myActor" to [x,y,z,1];

It simply translates the vertices V of "myActor"  by (x,y,z).  The 4th component must be 1.

And the syntax of Spin is similar,

Spin "myActor" to quat(Angle, [n1,n2,n3]);

It rotates the vertices of V about the origin by the unit quaternion argument.  These simple commands are combined in two ways into single motions using the remaining two commands:  SpinShift and ShiftSpin.

            Probably the most useful rigid motion in Mathwright 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 fragment:

local (ve #4 current) (ve #4 inc) {   'declare quaternions for compiler'

do

    step := 0

    current := quat(0, [1,0,0])

    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. 

The syntax of the ShiftSpin command is:

Shiftspin "myActor" to [x,y,z,1] quat(Angle, [n1,n2,n3]);

            The order of the arguments is now, shift first, spin second, as the name indicates, and as they are applied.  ShiftSpin fist translates V, then rotates. 

            For example.  Suppose we wanted to illustrate the noncommutativity of rotations.  We might imagine placing a cube at the point [-10,0,0], and applying a continuous series of rotations of the form  v*u, that is, u first and v second, where

u = quat( angle, [1,0,0])

v = quat( angle, [0,1,0])

for increasing values of angle.  This would cause the actor to revolve about the y-axis as it spun end-over-end about "its own" x-axis.  It would orbit and spin.

            On the other hand, a continuous sequence of the transformations u*v, that is, v first then u, would have an entirely different effect.  If the center of gravity of "myActor" is at the origin initially, this demonstration would be difficult to achieve with SpinShift, but easy with ShiftSpin, as the following scripts show:

program demoVU (ex step) {

  do

  s := 0

  until s > 360 {

  u := quat(s, [1,0,0]);

  v := quat(s, [0,1,0]);

  shiftspin "myActor" to [-10,0,0,1], v*u;

  s := s+step;

}

}

program demoUV (ex step) {

  do

  s := 0

  until s > 360 {

  u := quat(s, [1,0,0]);

  v := quat(s, [0,1,0]);

  shiftspin "myActor" to [-10,0,0,1], u*v;

  s := s+step;

}

}

            Now of course if V initially positioned the actor at [-10,0,0] then we could simply use the Spin command to achieve this effect.  But, for a given actor, we cannot have it both ways.  The SpinShift and ShiftSpin commands naturally complement each other, and together ease the burden of calculating motion.

            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.

Rocket before launch

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-10, 2.5*sin(radian(i)),1];

  i := i+step;

}

}

Rocket after launch (1/2 turn)

            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 or ShiftSpin in this way. But some Geometry is required to discover the right transformations.  Sometimes, however it is desirable to use the two components of a SpinShift or  ShiftSpin independently.  Sometimes, only one is needed.

            Since all shifts commute with all other shifts, the order in which you do them is unimportant -- except for the motions they produce while passing through the intermediate stages -- the final result is the same. 

            Again, Spin is the rotation command.  It is the "rotation" part of a SpinShift or ShiftSpin command.  Thus, if r is a quaternionic rotation, then

Spin A r;

has the effect of changing the current Stage transformation of A to r . 

Spin "cube" to quat(30, [0,1,0]);

simply rotates a cube (without translation)  30 degrees about the y axis.  It is entirely equivalent to (but a little faster than)

SpinShift "cube" to quat(30, [0,1,0]) [0,0,0,1];

                        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.

Continuous Motion:

Gldraw {...}

Push

DoShift or DoSpinShift or DoShiftSpin

            When several actors must be moved simultaneously, intercalating their motions in a script using the SpinShift, ShiftSpin, Spin, and Shift commands is a simple method to use.  But when many individual calls are needed for a very smooth motion, it can be a bit slow.  An alternative for continuous motion is available that will in general be faster. 

            Suppose, for example, that you wanted to move some actors simultaneously along their trajectories given by differential equations,  or by curves, or even stochastically.  You could create the trajectories as an anonymous (and invisible) actor.  If the trajectories were to be sequences of positions (without changing orientation) you would use the Gldraw{...} protocol with the desired sequences of positions as Vertex() or Vertex3() calls computed within it.  You should not use Begin() ... end() within the Gldraw{..} or any other OpenGL functions.   Then you would use the DoShift command to move the actors in the queue through the sequence of steps in the invisible actor. 

            The queue works this way.  Suppose it contains actors:  {a1, a2, .., an}.  Then when DoShift is called, tha actor a1 is moved to the first position, a2 is moved to the second position, a3 is moved to the third position, ..., an is moved to the nth position,  then a1 is moved to the (n+1) position, and so on cyclically, until there are no more positions.

            If you wanted the orientation to change, as well as the position, for example, in rotating and orbiting a planet, you would create an invisible actor with the sequence of pairs of orientations and positions, in that order, and use either DoSpinShift or DoShiftSpin.

            Let us consider first, the Shift case, where orientation does not change, since that is simpler.  Suppose, for example, you wanted to revolve a sphere about a circular orbit.  Suppose the V vertices place the sphere initially at the origin  [0,0,0].  Then the following script would move it in a circular orbit in the x-z plane.

refresh;                                                             'clear all anonymous actors'

c(t) := [5*cos(radian(t)), 0, 5*sin(radian(t))];  'define the curve trajectory'

gldraw {                                               'now create the invisible actor'

    do

       angle := 0

       until angle > 3600 {

       vertex( c(angle) );

       angle := angle + 1;

            }

}

push "sphere";                           'place the sphere in the queue'

DoShift;                                                'move it'

            The first line, refresh; removes all anonymous actors so that we may start a new one.   In general,  anonymous actors are concatenated in memory to the limit of storage, roughly 13,000 OpenGL statements.  

            The second command creates a curve whose points will be the translation (Shift) vectors that we want to apply in sequence.  This is a convenience, and we needn't use a curve.  We could instead generate the points from a differential equation, stochastic process, or any other means.

            The third command is a Gldraw {...} protocol.  This creates the anonymous actor.  It is the only way to create an anonymous actor.  In this case, it is a sequence of 3600 points, that will cause the sphere to revolve 10 times.  The "points" must be created with the "vertex()" or the "vertex3()" command within the gldraw.  Vertex is convenient here, because we can simply write:  vertex( c(angle)) and MathScript knows what we mean. 

            Now in general, you may create an invisible actor of this type (sequence of vertices) with up to 13,000 vertices.  So long sequences of motion are possible.  To stop drawing, simply press the Esc key.  The actor is "invisible" because we have not used a begin() - end() block.  So the Vertex() calls do not actually trigger OpenGL to draw anything.  But this sequence is nevertheless "compiled" into a display list.  The invisible actor (the orbit) remains around until you make another call to refresh; or until you select:  Viewport, Scene, Refresh.  Remember, however, only to use the Vertex() or Vertex3() OpenGL commands. 

            The next statement adds "sphere" to the queue of actors to move along this invisible actor.  For each actor that you plan to move, you should make a separate Push call with its name. If a name is repeated, it is ignored the second time an attempt is made to push it. The actors are moved in the order in which they are pushed after the invisible actor is created. 

Note:  Each time an invisible actor is created (or any call to Gldraw{..} is made), the queue is erased.  So you should call Gldraw{...} first to create the invisible actor, then create the queue of actors to move using push.

            The last statement, which may appear strange, actually puts the sphere in motion.  It is:

DoShift;

            Each vertex() statement in the invisible actor is interpreted as a "shift" in the usual sense.  There should be only vertex (or vertex3() ) statements in the invisible actor.  If MathScript encounters any other type of OpenGL statement, it stops the animation at that point.

            When MathScript sees a DoShift() call, it always behaves this way.  If there is no invisible actor defined, then it simply returns.  When this entire script is run, there is a small delay while MathScript builds the invisible actor, and then the sphere is set in motion.  You should, of course, create the invisible actor only once.  Each time DoShift is called henceforth, this sequence of motions will be repeated.

DoShift;

will immediately set the queue in motion using the invisible actor already created.  And of course, if you want to change the track of motion, then call Refresh; first, and build a new invisible actor.

            That is how DoShift works for continuous motion.  It is generally a bit faster than a series of Shift calls, because it computes the whole motion at once.  The discrete form (Shift) should be used if you want to have decision points along the trajectory.  Motion is terminated with the Esc key.

            Now suppose you wanted to orbit a "Space Shuttle" along the same path.  Again, for simplicity, suppose that the V vertices placed the center of mass of the shuttle at the origin initially, and that it was oriented, bottom to top, along the y-axis.

            To make the motion natural, we want a sequence of "Spinshifts"  first orienting the rocket in a direction tangent to the curve, then shifting it out to the appropriate location.

            For this, we need the second type of invisible anonymous actor, which is a sequence of pairs:   Quaternion(), Vertex()  or (Vertex4(),  Vertex3()) in that order.  The arguments to Quaternion() or Vertex4() will always be interpreted as spins, and the arguments to the Vertex() (or Vertex3() ) will always be interpreted as shifts.  This is the order we use:  Vertex4(),  Vertex3(), Vertex4(),  Vertex3(), ..., Vertex4(),  Vertex3(), that is:  Spin, Shift, Spin, Shift, Spin, Shift, ... Spin, Shift  when defining the invisible actor whether we use DoSpinShift or DoShiftSpin.

            Clearly, the above sequence of shifts would not do.  The shuttle would "translate" around the orbit in an unnatural manner.  Suppose that the Initial Stage transformation of the shuttle (that defined by V, before the actor's script is run is:

Initial Shuttle position and orientation

            The following script will cause the shuttle to orbit in a natural way:

refresh; 'clear anonymous actors'

c(t) := [5*cos(radian(t)), 0, 5*sin(radian(t))];

                        'Define the orbit curve.  Since we pass arguments in degrees, use Radian() to translate for Sin and Cos'

local (ve #4 w) {  'Local declaration is for the compiler as explained below'

gldraw {                       'this creates the anonymous invisible actor'

make u quat(90, [1,0,0]);

do

  angle := 0

  until angle > 360 {

  v := quat(-angle, [0,1,0]);

  w := v*u;                                'compose two spins'

  quaternion(w);  'spin component'

  vertex( c(angle) );                      'shift component'

  angle := angle+5;                       'increment the angle which is measured in degrees'

}

}

}

push "shuttle";

dospinshift;

Shuttle position and orientation after 90 degrees

            There are several things to note.  We generally work with degree measure of angles, since OpenGL and Quat expect degrees.  So we use the Radian() function where necessary to translate degrees to radians.  The trigonometric functions always expect radians.

            The curve c(t) defines an orbit of radius 5 centered at the origin in the x-z plane.  It begins and ends at [5,0,0].  But notice that it is traversed in such a way that initially it comes "out" of the screen towards the viewer.  This is the opposite of what would be dictated by the right-hand rule with the y-axis as center.

            To "point" the rocket correctly, we need two spins:  first, rotate it 90 degrees about the positive x-axis  (right-hand rule), and then rotate it through the opposite of the current angle about the positive y-axis (right-hand rule).  This orients it correctly at the origin.  Next, shift it to the location on the orbit with that orientation.

            To compose the two spins, we simply multiply the appropriate quaternions.

u := quat(90, [1,0,0]);

v := quat(-angle, [0,1,0]);

and form v*u = w.  This means u first, then v as noted earlier.  Then we execute the two commands for each angle: 

quaternion(w);                                      'spin component'

vertex( c(angle) );                     'shift component'

            The Vertex and Quaternion commands are useful for this sort of script because they accept vectors (as MathScript objects) as arguments, and these may be computed in a flexible, symbolic way.  Otherwise, we'd have to "peel off" the components for Vertex3() or Vertex4().

            It is always Quaternion() = Spin first, then Vertex() = Shift second, whether we use a SpinShi