|
/*ScianSpaces.c
Space objects
Eric Pepke
April 6, 1990
*/
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianColors.h"
#include "ScianLists.h"
#include "ScianArrays.h"
#include "ScianLights.h"
#include "ScianSpaces.h"
#include "ScianEvents.h"
#include "ScianDraw.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianVisWindows.h"
#include "ScianVisObjects.h"
#include "ScianScripts.h"
#include "ScianIcons.h"
#include "ScianIDs.h"
#include "ScianControls.h"
#include "ScianButtons.h"
#include "ScianTextBoxes.h"
#include "ScianTitleBoxes.h"
#include "ScianPerspec.h"
#include "ScianErrors.h"
#include "ScianTimers.h"
#include "ScianDialogs.h"
#include "ScianSliders.h"
#include "ScianTextBoxes.h"
#include "ScianStyle.h"
#include "ScianComplexControls.h"
#include "ScianMethods.h"
#include "ScianPick.h"
#include "ScianPreferences.h"
#include "ScianFiles.h"
#define NEWCLOCK
Bool phsco = false; /*True iff phscologram is on*/
Bool drawingTransparent = false; /*True iff drawing only transparent*/
static void StartSpace(ObjPtr, int, int, int, int, int, int);
typedef struct
{
unsigned char alpha;
unsigned char blue;
unsigned char green;
unsigned char red;
} Pixel;
Pixel imageBuffer[SCRWIDTH * SCRHEIGHT];
#define PLAYCLOCK (0.1) /*Rate for clock play*/
#define FASTCLOCK (0.2) /*Rate for clock fast*/
#define ROTLIGHTS /*Rotate lights with object*/
#define DSPCLOCKWIDTH 97
#define DSPCLOCKHEIGHT 48
#define DSPCLOCKLEFT 55
#define DSPCLOCKTOP 70
#define DSPCLOCKTLEFT 10
#define DSPCLOCKTRIGHT 10
#define DSPCLOCKTTOP 23
#define ASPECT (((float) (right - left)) / ((float) (top - bottom))) * (phsco ? 0.61 : 1.0)
#define MINROT 3 /*Minimum number of pixels to move to rotate*/
#define SQUARE(x) ((x) * (x))
#define AIRSPEEDFACTOR 0.02 /*Airspeed of eye per throttle stop*/
#define MAXROLL (M_PI / 10.0) /*Pi/10 of roll max*/
#define MAXDPITCH 0.1 /*Maximum delta percentage pitch*/
#define ROLLYAWFACTOR 1.0 /*Roll into yaw per unit time*/
#define ROLLDSPFACTOR (900.0 / M_PI)
#define STARSIZE 0.05 /*Size of a star*/
#define MOVEFACTOR 0.35 /*Move per D unit * distance*/
Matrix tempMatrix; /*Temporary matrix*/
ObjPtr curSpace = 0; /*Current space*/
real curSpacePerspec[4]; /*Current perspective stuff*/
ObjPtr curSpaceLights; /*Lights in the current space*/
Matrix viewerCoordsMatrix; /*Transformation matrix for viewer coords*/
Matrix viewerUnrotMatrix; /*Transformation matrix for unrotated viewer coords*/
ObjPtr spaceClass = 0; /*Class of a 3-D space*/
extern ObjPtr visClass; /*Class of all visualization objects*/
extern ObjPtr iconClass; /*Class of icons*/
real spaceTime = 0.0; /*Time for current space. Cute name, huh?*/
ObjPtr spacePanelClass; /*Invisible panel over a space*/
ObjPtr spaceBackPanelClass; /*Colored panel behind a space*/
ObjPtr controllerClass; /*Class for space controllers (clocks, lights, etc.)*/
ObjPtr clockClass; /*Class of clocks*/
ObjPtr observerClass; /*Class of observers*/
ObjPtr rendererClass; /*Class of renderers*/
Bool oneObserver = false;
Bool oneRenderer = false;
Bool oneClock = false;
Bool oneLights = false; /*True iff to make only one controller*/
Coord starPoints[][3] = /*Points of a star*/
{
{1.0, STARSIZE, 0.0},
{1.0, 0.0, STARSIZE},
{1.0, -STARSIZE, 0.0},
{1.0, 0.0, -STARSIZE}
};
void DrawSpaceContents(ObjPtr, int, int, int, int, int);
static float f(x)
float x;
/*Performs the increasing monotonic function required for the virtual trackball
of click radius ratio x. Yeah, right.*/
{
if (x <= 0.0)
{
return 0.0;
}
else if (x >= 1.0)
{
return M_PI_2;
}
else
{
return M_PI_2 * x;
}
}
Bool GetListExtent(list, bounds)
ObjPtr list;
real bounds[];
/*Puts the extent of the objects (or the objects they represent) in list
into bounds. Returns true iff it's nonzero*/
{
ThingListPtr runner;
/*Make bounds ludicrous*/
bounds[0] = bounds[2] = bounds[4] = 1e12;
bounds[1] = bounds[3] = bounds[5] = -1e12;
runner = LISTOF(list);
if (!runner)
{
return false;
}
while (runner)
{
ObjPtr repObj;
repObj = runner -> thing;
if (IsObject(repObj))
{
FuncTyp boundsMethod;
boundsMethod = GetMethodSurely("GetListExtent", repObj, GETBOUNDS);
if (boundsMethod)
{
real localBounds[6];
(*boundsMethod)(repObj, localBounds);
if (localBounds[0] < bounds[0]) bounds[0] = localBounds[0];
if (localBounds[1] > bounds[1]) bounds[1] = localBounds[1];
if (localBounds[2] < bounds[2]) bounds[2] = localBounds[2];
if (localBounds[3] > bounds[3]) bounds[3] = localBounds[3];
if (localBounds[4] < bounds[4]) bounds[4] = localBounds[4];
if (localBounds[5] > bounds[5]) bounds[5] = localBounds[5];
}
}
runner = runner -> next;
}
return true;
}
#define NUDGESIZE 400 /*Size of one nudge*/
#define MAXNNUDGES 3 /*Maximum number of nudges*/
long zMin = 0 + NUDGESIZE * MAXNNUDGES;
long zMax = 0x7fffff - NUDGESIZE * MAXNNUDGES;
long curZMin, curZMax;
void NudgeCloser()
/*Nudges objects to be drawn closer*/
{
curZMax -= NUDGESIZE;
curZMin -= NUDGESIZE;
lsetdepth(curZMin, curZMax);
}
void NudgeFarther()
/*Nudges objects to be drawn farther*/
{
curZMax += NUDGESIZE;
curZMin += NUDGESIZE;
lsetdepth(curZMin, curZMax);
}
static void StartSpace(space, left, right, bottom, top, action, whichView)
ObjPtr space;
int left, right, bottom, top;
int action;
int whichView;
/*Start a drawing, rotation, or press in a space. Action is the action
that is being performed*/
{
real eyePosn[3]; /*Coords of the eye*/
real focusPoint[3]; /*Coords of the focus point*/
real roll;
ObjPtr object;
ObjPtr psStuff;
ObjPtr boundsArray;
real bounds[4];
real bigBounds[6]; /*Bounds of the objects*/
Coord center[3]; /*The center of the data set*/
ObjPtr contents;
float scaleFactor; /*Factor to scale*/
ObjPtr xformMatrix; /*The rotation matrix*/
ObjPtr shearMatrix; /*The shear matrix*/
real maxSize; /*Maximum size of the objects this draw*/
ObjPtr observer; /*Observer of the space*/
ObjPtr clock; /*The clock*/
real eyeOffset;
real eyeDir[3];
MyPushMatrix();
if (whichView == VIEW_XLEFT)
{
left = (left + right) / 2 + 1;
}
else if (whichView == VIEW_XRIGHT)
{
right = (left + right) / 2;
}
switch (whichView)
{
case VIEW_XLEFT:
case VIEW_RCLEFT:
eyeOffset = -0.2;
break;
case VIEW_XRIGHT:
case VIEW_RCRIGHT:
eyeOffset = 0.2;
break;
default:
eyeOffset = 0.0;
}
/*Set a new viewport to the current area*/
pushviewport();
viewport(left, right, bottom, top);
InitClipRect();
#if 0
observers = GetListVar("StartSpace", space, OBSERVERS);
if (!observers || !LISTOF(observers)) return;
observer = LISTOF(observers) -> thing;
#else
observer = GetObjectVar("StartSpace", space, OBSERVER);
#endif
if (!observer) return;
curSpacePerspec[0] = INITEYEDIST;
psStuff = GetFixedArrayVar("StartSpace", observer, PERSPECSTUFF, 1, 4L);
if (psStuff)
{
Array2CArray(curSpacePerspec, psStuff);
}
/*Get information about the eye*/
object = GetFixedArrayVar("StartSpace", observer, LOCATION, 1, 3L);
if (object)
{
Array2CArray(eyePosn, object);
}
else
{
eyePosn[0] = 0.0;
eyePosn[1] = 0.0;
eyePosn[2] = curSpacePerspec[0];
}
object = GetFixedArrayVar("StartSpace", observer, FOCUSPOINT, 1, 3L);
if (object)
{
Array2CArray(focusPoint, object);
}
else
{
focusPoint[0] = 0.0;
focusPoint[1] = 0.0;
focusPoint[2] = 0.0;
}
object = GetRealVar("StartSpace", observer, ROLL);
if (object)
{
roll = GetReal(object);
}
else
{
roll = 0.0;
}
if (whichView == VIEW_XLEFT || whichView == VIEW_XRIGHT ||
rgbp && (whichView == VIEW_RCLEFT || whichView == VIEW_RCRIGHT))
{
real temp;
/*Have to slide eyePosn and focusPoint over somewhat*/
eyeDir[0] = focusPoint[0] - eyePosn[0];
eyeDir[1] = focusPoint[1] - eyePosn[1];
eyeDir[2] = focusPoint[2] - eyePosn[2];
NORMALIZE(eyeDir);
/*Make 90 degree rotation in plane*/
temp = eyeDir[0];
eyeDir[0] = eyeDir[2];
eyeDir[2] = -temp;
eyeDir[1] = 0.0;
NORMALIZE(eyeDir);
/*Twist by roll*/
temp = rcos(roll);
eyeDir[0] *= temp;
eyeDir[2] *= temp;
eyeDir[1] *= rsin(roll);
/*Offset*/
eyeDir[0] *= eyeOffset;
eyeDir[1] *= eyeOffset;
eyeDir[2] *= eyeOffset;
eyePosn[0] -= eyeDir[0];
eyePosn[1] -= eyeDir[1];
eyePosn[2] -= eyeDir[2];
/*
focusPoint[0] -= eyeDir[0];
focusPoint[1] -= eyeDir[1];
focusPoint[2] -= eyeDir[2];
*/
}
/*Get information about the clock*/
clock = GetObjectVar("StartSpace", space, CLOCK);
if (clock)
{
ObjPtr time;
time = GetVar(clock, TIME);
if (time)
{
spaceTime = GetReal(time);
}
else
{
spaceTime = 0.0;
}
}
else
{
spaceTime = 0.0;
}
/*Set the current space*/
curSpace = space;
RegisterInSpace(true);
shearMatrix = (ObjPtr) GetVar(space, SHEAR);
if (observer)
{
xformMatrix = (ObjPtr) GetMatrixVar("StartSpace", observer, XFORM);
}
curSpaceLights = GetVar(space, LIGHTS);
/*Set up the lights*/
if (action == DRAWSPACE)
{
shademodel(GOURAUD);
#ifdef GL4D
mmode(MPROJECTION);
loadmatrix(Identity);
#endif
ortho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
lookat(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0);
#ifdef GL4D
mmode(MVIEWING);
loadmatrix(Identity);
#endif
/*Shear the light sources*/
if (shearMatrix && IsMatrix(shearMatrix))
{
multmatrix(MATRIXOF(shearMatrix));
}
#ifdef ROTLIGHTS
/*Rotate the light sources*/
if (xformMatrix && IsMatrix(xformMatrix))
{
multmatrix(MATRIXOF(xformMatrix));
}
#endif
#ifdef GL4D
if (curSpaceLights)
{
StartLights(space, curSpaceLights);
}
#endif
}
if (action == PICKSPACE)
{
StartPick(space);
}
#ifdef GL4D
mmode(MPROJECTION);
#endif
if (whichView == VIEW_ORTHO)
{
/*Plain old orthographic projection.*/
ortho(-ASPECT * curSpacePerspec[1], ASPECT * curSpacePerspec[1],
-curSpacePerspec[1], curSpacePerspec[1], curSpacePerspec[2], curSpacePerspec[3]);
}
else
{
/*Exciting perspective projection!!!*/
perspective((long) (curSpacePerspec[1] * 10.0), ASPECT, curSpacePerspec[2], curSpacePerspec[3]);
}
lookat(eyePosn[0], eyePosn[1], eyePosn[2],
focusPoint[0], focusPoint[1], focusPoint[2],
-(Angle) (roll * ROLLDSPFACTOR));
#ifdef GL4D
mmode(MVIEWING);
loadmatrix(Identity);
#endif
if (action == DRAWSPACE || action == PICKSPACE)
{
/*Draw the background of the space*/
if (action == DRAWSPACE)
{
zbuffer(TRUE);
zclear();
curZMin = zMin + NUDGESIZE * MAXNNUDGES;
curZMax = zMax - NUDGESIZE * MAXNNUDGES;
lsetdepth(curZMin, curZMax);
}
if (rgbp && whichView == VIEW_RCLEFT)
{
RGBwritemask(0, 0xFF, 0xFF);
}
else if (rgbp && whichView == VIEW_RCRIGHT)
{
RGBwritemask(0xFF, 0, 0);
}
/*Shear the objects*/
if (shearMatrix && IsMatrix(shearMatrix))
{
multmatrix(MATRIXOF(shearMatrix));
}
/*Draw stuff in focus centered space coordinates*/
if (action == DRAWSPACE)
{
Matrix justRot;
int i;
real radius;
radius = SPACESPHERESIZE * curSpacePerspec[0] * rsin(curSpacePerspec[1] * M_PI / 180.0);
MATCOPY(justRot, MATRIXOF(xformMatrix));
for (i = 0; i < 3; ++i)
{
justRot[3][i] = justRot[i][3] = 0.0;
}
pushmatrix();
translate(focusPoint[0], focusPoint[1], focusPoint[2]);
getmatrix(viewerUnrotMatrix);
multmatrix(justRot);
getmatrix(viewerCoordsMatrix);
if (GetPredicate(curSpace, ROTATING) && GetPrefTruth(PREF_ROTGUIDES))
{
DrawWFSphere(0.0, 0.0, 0.0,
radius, UIGRAY50);
DrawSpaceLine(0.0 - radius, 0.0, 0.0, 0.0 + radius, 0.0, 0.0, UIGRAY50);
DrawSpaceLine(0.0, 0.0 - radius, 0.0, 0.0, 0.0 + radius, 0.0, UIGRAY50);
DrawSpaceLine(0.0, 0.0, 0.0 - radius, 0.0, 0.0, 0.0 + radius, UIGRAY50);
}
else if (GetPredicate(curSpace, MOVING) && GetPrefTruth(PREF_MOVEGUIDES))
{
real x, y;
for (x = -10.0; x <= 10.0; x += 2.0)
{
for (y = -10.0; y <= 10.0; y += 2.0)
{
DrawSpaceLine(x, y, -10.0, x, y, 10.0, UIGRAY50);
DrawSpaceLine(x, -10.0, y, x, 10.0, y, UIGRAY50);
DrawSpaceLine(-10.0, x, y, 10.0, x, y, UIGRAY50);
}
}
}
popmatrix();
}
/*Rotate the objects*/
multmatrix(MATRIXOF(xformMatrix));
/*Determine the size of the objects within*/
contents = GetVar(space, CONTENTS);
GetListExtent(contents, bigBounds);
/*Get their center*/
center[0] = (bigBounds[1] + bigBounds[0]) / 2.0;
center[1] = (bigBounds[3] + bigBounds[2]) / 2.0;
center[2] = (bigBounds[5] + bigBounds[4]) / 2.0;
/*Scale to a reasonable scaling factor for the data*/
maxSize = ABS(bigBounds[1] - bigBounds[0]);
if (ABS(bigBounds[3] - bigBounds[2]) > maxSize)
{
maxSize = ABS(bigBounds[3] - bigBounds[2]);
}
if (ABS(bigBounds[5] - bigBounds[4]) > maxSize)
{
maxSize = ABS(bigBounds[5] - bigBounds[4]);
}
scaleFactor = 1.6 / maxSize;
scale(scaleFactor, scaleFactor, scaleFactor);
/*Translate to be centered around object's center*/
translate(-center[0], -center[1], -center[2]);
}
}
static void StopSpace(action)
int action;
/*Stops drawing, rotating, or pressing in a space*/
{
if (action == PICKSPACE)
{
StopPick(curSpace);
}
if (action == DRAWSPACE)
{
real radius;
radius = SPACESPHERESIZE * curSpacePerspec[0] * rsin(curSpacePerspec[1] * M_PI / 180.0);
/*Draw the lights*/
loadmatrix(viewerCoordsMatrix);
if (curSpaceLights)
{
DrawLights(curSpace, curSpaceLights, action, radius);
StopLights(curSpace);
}
if (rgbp)
{
RGBwritemask(0xFF, 0xFF, 0xFF);
}
}
ResetClipRect();
popviewport();
#ifdef GL4D
mmode(MSINGLE);
loadmatrix(Identity);
#endif
EndRegister();
MyPopMatrix();
zbuffer(FALSE);
curSpace = NULLOBJ;
}
Bool ChangeFocus(observer)
ObjPtr observer;
/*Changes the focus of observer according to roll, pitch, yaw*/
{
ObjPtr tempObj;
real eyePosn[3];
real pitch, yaw, roll;
real perspecStuff[4];
tempObj = GetFixedArrayVar("ChangeFocus", observer, LOCATION, 1, 3L);
if (!tempObj)
{
return false;
}
Array2CArray(eyePosn, tempObj);
tempObj = GetRealVar("ChangeFocus", observer, PITCH);
if (!tempObj)
{
return false;
}
pitch = GetReal(tempObj);
tempObj = GetRealVar("ChangeFocus", observer, YAW);
if (!tempObj)
{
return false;
}
yaw = GetReal(tempObj);
tempObj = GetRealVar("ChangeFocus", observer, ROLL);
if (!tempObj)
{
return false;
}
roll = GetReal(tempObj);
/*Get perspective stuff for scaling movement and eyeball distance*/
tempObj = GetFixedArrayVar("ChangeFocus", observer, PERSPECSTUFF, 1, 4L);
if (tempObj)
{
Array2CArray(perspecStuff, tempObj);
}
else
{
perspecStuff[0] = INITEYEDIST;
}
/*Change focus point to be from new eye position*/
eyePosn[0] += rsin(yaw) * perspecStuff[0];
eyePosn[1] += rsin(pitch) * perspecStuff[0];
eyePosn[2] += rcos(yaw) * perspecStuff[0];
tempObj = NewRealArray(1, 3L);
CArray2Array(tempObj, eyePosn);
SetVar(observer, FOCUSPOINT, tempObj);
ResolveController(observer);
return true;
}
ObjPtr WakeObserver(observer, lateness)
ObjPtr observer;
double lateness;
/*Wakes an observer and makes it continue rotation*/
{
ObjPtr var;
real axis[3];
Matrix rotDelta; /*Delta rotation matrix*/
real phi;
Bool wakeAgain = false; /*True iff wake again*/
DoNotDisturb(observer, MARKTIME);
var = GetVar(observer, ROTSPEED);
if (var && 0.0 != (phi = GetReal(var)))
{
/*Turn phi from speed to amount*/
phi *= lateness;
var = GetFixedArrayVar("WakeObserver", observer, ROTAXIS, 1, 3L);
if (var)
{
Array2CArray(axis, var);
RotateObserver(observer, axis, phi, false);
}
wakeAgain = true;
}
else if (GetPredicate(observer, FLYING))
{
real focusPoint[3];
ObjPtr var;
real eyePosn[3];
real roll, yaw, dPitch, pitch;
float dTime;
real perspecStuff[4];
real airspeed;
dTime = lateness;
var = GetFixedArrayVar("DrawSpace", observer, FOCUSPOINT, 1, 3L);
if (var)
{
Array2CArray(focusPoint, var);
}
else
{
focusPoint[0] = 0.0;
focusPoint[1] = 0.0;
focusPoint[2] = 0.0;
}
/*Move eye according to airspeed*/
var = GetVar(observer, AIRSPEED);
if (var && IsReal(var))
{
airspeed = GetReal(var);
}
else
{
airspeed = 0.0;
}
var = GetVar(observer, LOCATION);
if (var && IsArray(var) && RANK(var) == 1 &&
DIMS(var)[0] == 3)
{
Array2CArray(eyePosn, var);
}
else
{
eyePosn[0] = 0.0;
eyePosn[1] = 0.0;
eyePosn[2] = INITEYEDIST;
var = NewRealArray(1, (long) 3);
SetVar(observer, LOCATION, var);
}
/*Update eye position based on airspeed*/
var = GetVar(observer, ROLL);
if (var && IsReal(var))
{
roll = GetReal(var);
}
else
{
roll = 0.0;
var = NewReal(roll);
SetVar(observer, ROLL, var);
}
/*Get dPitch*/
var = GetVar(observer, DPITCH);
if (var && IsReal(var))
{
dPitch = GetReal(var);
}
else
{
dPitch = 0.0;
var = NewReal(dPitch);
SetVar(observer, DPITCH, var);
}
/*Get pitch*/
var = GetVar(observer, PITCH);
if (var && IsReal(var))
{
pitch = GetReal(var);
}
else
{
pitch = 0.0;
var = NewReal(pitch);
SetVar(observer, PITCH, var);
}
/*Change the pitch based on dpitch, asymptotic to up and down*/
if (dPitch > 0.0)
{
pitch = pitch + (M_PI_2 - pitch) * dPitch * dTime;
}
else
{
pitch = pitch + (pitch + M_PI_2) * dPitch * dTime;
}
SetVar(observer, PITCH, NewReal(pitch));
/*Get yaw*/
var = GetRealVar("DrawSpace", observer, YAW);
if (var && IsReal(var))
{
yaw = GetReal(var);
}
else
{
yaw = M_PI;
var = NewReal(yaw);
SetVar(observer, YAW, var);
}
/*Change yaw based on roll*/
yaw -= roll * ROLLYAWFACTOR * dTime;
SetVar(observer, YAW, NewReal(yaw));
/*Move eye*/
eyePosn[2] += rcos(yaw) * airspeed * dTime;
eyePosn[1] += rsin(pitch) * airspeed * dTime;
eyePosn[0] += rsin(yaw) * airspeed * dTime;
var = NewRealArray(1, 3L);
CArray2Array(var, eyePosn);
SetVar(observer, LOCATION, var);
/*Recalculate focuspoint*/
ChangeFocus(observer);
ResolveController(observer);
wakeAgain = true;
}
if (wakeAgain)
{
WakeMe(observer, MARKTIME, Clock() + 0.001);
}
return ObjTrue;
}
#ifdef PROTO
void SetRotationMotor(real rotSpeed, real ax, real ay, real az)
#else
void SetRotationMotor(rotSpeed, ax, ay, az)
real rotSpeed, ax, ay, az;
#endif
/*Sets a rotation motor at rotSpeed around axis ax, ay, az*/
{
ObjPtr var;
real axis[3];
char logLine[256];
ObjPtr space, observer;
if (!selWinInfo)
{
return;
}
space = FindSpace(selWinInfo);
if (!space)
{
return;
}
observer = GetObjectVar("SetRotationMotor", space, OBSERVER);
if (!observer)
{
return;
}
var = NewRealArray(1, 3L);
axis[0] = ax;
axis[1] = ay;
axis[2] = az;
CArray2Array(var, axis);
SetVar(observer, ROTAXIS, var);
SetVar(observer, ROTSPEED, NewReal(rotSpeed));
if (logging)
{
sprintf(logLine, "set rotation %g [%g %g %g]\n",
rotSpeed * 180.0 / M_PI, ax, ay, az);
Log(logLine);
}
if (rotSpeed > 0.0)
{
DoNotDisturb(observer, MARKTIME);
WakeMe(observer, MARKTIME, Clock());
}
}
static ObjPtr RotateSpace(object, x, y, flags)
ObjPtr object;
int x, y;
int flags;
/*Does a rotate in a space beginning at x and y. Returns
true iff the rotate really was in the panel.*/
{
int left, right, bottom, top;
Get2DIntBounds(object, &left, &right, &bottom, &top);
if (x >= left && x <= right && y >= bottom && y <= top)
{
ObjPtr xformMatrix = 0;
ObjPtr observer/*, observers*/;
ObjPtr lights;
ThingListPtr runner;
if (TOOL(flags) == T_HELP)
{
ContextHelp(object);
return ObjTrue;
}
/*Hey! It really was a click in the space!*/
StartSpace(object, left, right, bottom, top, ROTSPACE, VIEW_CENTER);
/*See if there are any lights to rotate*/
lights = GetVar(object, LIGHTS);
if (lights)
{
runner = LISTOF(lights);
while (runner)
{
if (IsLightSelected(runner -> thing, GetVar(object, CORRAL)))
{
break;
}
runner = runner -> next;
}
}
/*If none selected, say there are none*/
if (!runner)
{
lights = NULLOBJ;
}
/*Get the rotation matrix*/
#if 0
observers = GetListVar("RotateSpace", object, OBSERVERS);
if (!observers || !LISTOF(observers)) return ObjFalse;
observer = LISTOF(observers) -> thing;
#else
observer = GetObjectVar("RotateSpace", object, OBSERVER);
#endif
if (observer)
{
xformMatrix = (ObjPtr) GetMatrixVar("RotateSpace", observer, XFORM);
}
if (xformMatrix && IsMatrix(xformMatrix))
{
Bool motorOn = false; /*True iff motor on*/
float rotSpeed = 0.0; /*Rotation speed*/
float ax, ay, az; /*Three components of axis of rotation*/
real axis[3];
if (flags & F_DOUBLECLICK)
{
if (lights)
{
/*Double-click. Snap lights to their closest values*/
runner = LISTOF(lights);
while (runner)
{
if (IsLightSelected(runner -> thing, GetVar(object, CORRAL)))
{
/*Rotate the light*/
ObjPtr var;
int i;
float biggest;
int ibiggest;
real location[3];
var = GetFixedArrayVar("RotateSpace", runner -> thing, LOCATION, 1, 3L);
if (var)
{
Array2CArray(location, var);
biggest = 0.0;
ibiggest = 0;
for (i = 0; i < 3; ++i)
{
if (ABS(location[i]) > biggest)
{
ibiggest = i;
biggest = ABS(location[i]);
}
}
location[ibiggest] =
location[ibiggest] > 0.0 ? 1.0 : -1.0;
for (i = 0; i < 3; ++i)
{
if (i != ibiggest)
{
location[i] = 0.0;
}
}
var = NewRealArray(1, 3L);
CArray2Array(var, location);
SetVar(runner -> thing, LOCATION, var);
ResolveController(runner -> thing);
}
}
runner = runner -> next;
}
}
else
{
/*It was a double click. Align the rotation matrix to the
nearest orthogonal unit vector*/
Matrix newRot; /*New rotation matrix*/
Bool jSet[3]; /*True iff this j has been set nonzero*/
int i, j; /*Counters within the matrix*/
float biggest; /*The biggest xform coefficient so far*/
int jbiggest; /*The j of biggest*/
MATCOPY(newRot, MATRIXOF(xformMatrix));
for (j = 0; j < 3; ++j)
{
jSet[j] = false;
}
for (i = 0; i < 3; ++i)
{
biggest = -1.0;
jbiggest = -1;
for (j = 0; j < 3; ++j)
{
if ((ABS(newRot[i][j]) > biggest) && (!jSet[j]))
{
biggest = ABS(newRot[i][j]);
jbiggest = j;
}
}
if (jbiggest == -1)
{
StopSpace(ROTSPACE);
return ObjTrue;
}
else
{
jSet[jbiggest] = 1;
for (j = 0; j < 3; ++j)
{
if (j == jbiggest)
{
if (newRot[i][j] < 0.0) newRot[i][j] = -1.0;
else newRot[i][j] = 1.0;
}
else newRot[i][j] = 0.0;
}
}
}
MATCOPY(MATRIXOF(xformMatrix), newRot);
ResolveController(observer);
}
ImInvalid(object);
}
else
{
/*It's a click and drag. Do the trackball stuff.*/
float Ox, Oy; /*Center of virtual trackball*/
float r; /*Radius of virtual trackball*/
float omega; /*Ratio of the click radius to the trackball radius*/
float theta; /*Angle to the click radius*/
float tao; /*Angle from the click radius to the direction*/
float phi; /*Amount the trackball is pushed*/
int newX, newY; /*New X and Y values*/
Bool firstRun = true; /*The first run*/
float lastTime = 0.0; /*The last time*/
float cumPhi = 0.0; /*Cumulative phi*/
float curTime = 0.0; /*The current time*/
SetVar(object, ROTATING, ObjTrue);
DrawMe(object);
/*Calculate origin and radius of trackball*/
Ox = (left + right) / 2;
Oy = (top + bottom) / 2;
r = 0.4 * (float) (top - bottom);
x -= Ox; /*Offset x and y around trackball*/
y -= Oy;
while (Mouse(&newX, &newY))
{
newX -= Ox; /*Offset the new x and y*/
newY -= Oy;
if (ABS(newX - x) >= MINROT || ABS(newY - y) >= MINROT)
{
/*Now we have a differential*/
float dr; /*Click axis distance*/
float sw, cw; /*Sin and cosine of omega*/
float st, ct; /*Sin and cosine of theta*/
float sp, cp; /*Sin and cosine of phi*/
float a1x, a1y, a1z; /*Temporary values for axis of rotation*/
float a2x, a2y, a2z; /*Temporary values for axis of rotation*/
float t; /*1-cos phi*/
Matrix rotDelta; /*Change in rotation*/
ObjPtr dRotMatrix; /*Delta rotation matrix*/
dr = fsqrt(SQUARE((float) x) +
SQUARE((float) y));
omega = f(dr / r);
/*Calculate theta*/
theta = fatan2((float) (y), (float) (x));
/*Calculate tao as offset from theta*/
tao = fatan2((float) (newY - y), (float) (newX - x)) - theta;
/*Calculate phi simplistically*/
phi = fsqrt(SQUARE((float) (newX - x)) +
SQUARE((float) (newY - y))) / r;
/*Calculate sin and cos of the angles for speed*/
sw = fsin(omega);
cw = fcos(omega);
st = fsin(theta);
ct = fcos(theta);
sp = fsin(phi);
cp = fcos(phi);
t = 1.0 - cp;
/*Calculate the axis of rotation*/
/*First the motion from origin component*/
a1x = -fsin(tao);
a1y = fcos(tao);
a1z = 0.0;
/*Now multiply in the "on x-axis" component*/
a2x = a1x * cw + a1z * sw;
a2y = a1y;
a2z = a1x * -sw + a1z * cw;
/*Now multiply in the from the x-axis component*/
ax = a2x * ct + a2y * -st;
ay = a2x * st + a2y * ct;
az = a2z;
/*Calculate the phi and delta time*/
if (firstRun)
{
firstRun = false;
cumPhi = 0.0;
lastTime = Clock();
}
else
{
curTime = Clock();
cumPhi += phi;
if (curTime > lastTime + MINROTTIME)
{
motorOn = true;
rotSpeed = cumPhi / (curTime - lastTime);
lastTime = curTime;
}
}
axis[0] = ax;
axis[1] = ay;
axis[2] = az;
/*Now that there's a new delta, save it and redraw*/
if (lights)
{
RotateLights(lights, axis, phi, flags & F_SHIFTDOWN ? true : false, observer, object);
}
else
{
RotateObserver(observer, axis, phi, flags & F_SHIFTDOWN ? true : false);
}
x = newX;
y = newY;
ResolveController(observer);
DrawMe(object);
}
else
{
firstRun = true;
motorOn = false;
}
}
SetVar(object, ROTATING, false);
}
StopSpace(ROTSPACE);
if (logging)
{
char cmd[256];
char *s;
Bool rotLights;
ThingListPtr runner;
/*See if it's lights that were rotated*/
rotLights = false;
if (lights)
{
runner = LISTOF(lights);
while (runner)
{
if (IsLightSelected(runner -> thing, GetVar(object, CORRAL)))
{
/*Rotated light*/
ObjPtr var;
real location[3];
rotLights = true;
var = GetFixedArrayVar("RotateSpace", runner -> thing, LOCATION, 1, 3L);
if (var)
{
Array2CArray(location, var);
sprintf(cmd, "set location ");
s = cmd;
while (*s) ++s;
MakeObjectName(s, runner -> thing);
while (*s) ++s;
sprintf(s, " [%g %g %g]\n",
location[0],
location[1],
location[2]);
Log(cmd);
}
}
runner = runner -> next;
}
}
else
{
/*No lights rotated*/
xformMatrix = (ObjPtr) GetMatrixVar("RotateSpace", observer, XFORM);
MatrixToText(tempStr, MATRIXOF(xformMatrix));
sprintf(cmd, "rotate to %s\n", tempStr);
Log(cmd);
}
}
if (!lights && GetPrefTruth(PREF_ROTINERTIA))
{
if (motorOn)
{
SetRotationMotor((real) rotSpeed, axis[0], axis[1], axis[2]);
}
else
{
SetRotationMotor((real) 0.0, axis[0], axis[1], axis[2]);
}
}
return ObjTrue;
}
else
{
return ObjFalse;
}
}
else
{
return ObjFalse;
}
}
static ObjPtr PressSpace(object, x, y, flags)
ObjPtr object;
int x, y;
int flags;
/*Does a press in a space beginning at x and y. Returns
true iff the press really was in the space.*/
{
int left, right, bottom, top;
ObjPtr lights, corral;
ThingListPtr runner;
if (TOOL(flags) == T_ROTATE)
{
return RotateSpace(object, x, y, flags);
}
Get2DIntBounds(object, &left, &right, &bottom, &top);
if (x >= left && x <= right && y >= bottom && y <= top)
{
/*It's a click in the space*/
int d;
int newX, newY;
ObjPtr observer/*, observers*/;
ObjPtr pickedObjects;
ObjPtr lights, objects;
ThingListPtr runner;
ObjPtr xformMatrix;
ObjPtr tempObj;
real eyePosn[3];
real moveVector[3];
real pitch, yaw, roll;
real uMove, vMove;
real uMoveP, vMoveP, wMoveP;
real sr, cr;
real sp, cp;
real sy, cy;
real perspecStuff[4];
real sf;
d = top - bottom;
if (TOOL(flags) == T_HELP)
{
ContextHelp(object);
return ObjTrue;
}
corral = GetVar(object, CORRAL);
lights = GetVar(object, LIGHTS);
if (lights && corral)
{
runner = LISTOF(lights);
while (runner)
{
if (IsLightSelected(runner -> thing, corral))
{
return ObjTrue;
}
runner = runner -> next;
}
}
#if 0
observers = GetListVar("PressSpace", object, OBSERVERS);
if (!observers || !LISTOF(observers)) return ObjFalse;
observer = LISTOF(observers) -> thing;
#else
observer = GetObjectVar("PressSpace", object, OBSERVER);
#endif
if (!observer) return ObjFalse;
SetRotationMotor(0.0, 0.0, 0.0, 1.0);
MakeMeCurrent(NULLOBJ);
xformMatrix = (ObjPtr) GetMatrixVar("PressSpace", observer, XFORM);
if (!xformMatrix)
{
return ObjFalse;
}
#if 0
Don't pick for now
/*See if this press picks anything*/
StartSpace(object, left, right, bottom, top, PICKSPACE, VIEW_CENTER);
StopSpace(PICKSPACE);
/*Deselect all objects if shift not down*/
if (0 == (flags & F_SHIFTDOWN))
{
/*First lights*/
objects = GetVar(object, LIGHTS);
if (objects)
{
runner = LISTOF(objects);
while (runner)
{
FuncTyp method;
method = GetMethod(runner -> thing, SELECT);
if (method)
{
(*method)(runner -> thing, false);
}
runner = runner -> next;
}
}
}
if (pickedObjects = PickedObjects())
{
/*Some objects were picked*/
/*Now select all the picked objects*/
runner = LISTOF(pickedObjects);
while (runner)
{
/*Select or deselect the object*/
if (GetPredicate(runner -> thing, SELECTED))
{
/*Already selected. Only deselect if shift down*/
if (flags & F_SHIFTDOWN)
{
FuncTyp method;
method = GetMethod(runner -> thing, SELECT);
if (method)
{
(*method)(runner -> thing, false);
}
}
}
else
{
FuncTyp method;
method = GetMethod(runner -> thing, SELECT);
if (method)
{
(*method)(runner -> thing, true);
}
}
runner = runner -> next;
}
}
#endif
StartSpace(object, left, right, bottom, top, MOVESPACE, VIEW_CENTER);
SetVar(object, MOVING, ObjTrue);
DrawMe(object);
/*Get info about the space*/
tempObj = GetFixedArrayVar("PressSpace", observer, LOCATION, 1, 3L);
if (!tempObj)
{
return ObjFalse;
}
Array2CArray(eyePosn, tempObj);
tempObj = GetRealVar("PressSpace", observer, PITCH);
if (!tempObj)
{
return ObjFalse;
}
pitch = GetReal(tempObj);
tempObj = GetRealVar("PressSpace", observer, YAW);
if (!tempObj)
{
return ObjFalse;
}
yaw = GetReal(tempObj);
tempObj = GetRealVar("PressSpace", observer, ROLL);
if (!tempObj)
{
return ObjFalse;
}
roll = GetReal(tempObj);
/*Get perspective stuff for scaling movement and eyeball distance*/
tempObj = GetFixedArrayVar("PressSpace", observer, PERSPECSTUFF, 1, 4L);
if (tempObj)
{
Array2CArray(perspecStuff, tempObj);
}
else
{
perspecStuff[0] = INITEYEDIST;
}
if (flags & F_DOUBLECLICK)
{
}
/*Press the contents of the space*/
else while (Mouse(&newX, &newY))
{
if (newX != x && newY != y)
{
real focusPoint[3];
/*Get raw uMove and vMove*/
sf = perspecStuff[0] * MOVEFACTOR;
uMove = sf * ((float) (x - newX)) / (float) d;
vMove = sf * ((float) (y - newY)) / (float) d;
/*Transform by roll*/
sr = sin((double) roll);
cr = cos((double) roll);
uMoveP = uMove * cr + vMove * sr;
vMoveP = uMove * -sr + vMove * cr;
sp = sin((double) pitch);
cp = cos((double) pitch);
sy = sin((double) yaw);
cy = cos((double) yaw);
/*Produce a motion vector*/
moveVector[0] = uMoveP * -cy + vMoveP * (sy * -sp);
moveVector[1] = vMoveP * cp;;
moveVector[2] = uMoveP * sy + uMoveP * (sy * sp);
if (flags & F_SHIFTDOWN)
{
int k, best = 0;
float curDot, maxDot = 0.0;
float rr;
/*Constrain the axis to the nearest ortho axis*/
for (k = 0; k < 3; ++k)
{
curDot =
moveVector[0] * MATRIXOF(xformMatrix)[k][0] +
moveVector[1] * MATRIXOF(xformMatrix)[k][1] +
moveVector[2] * MATRIXOF(xformMatrix)[k][2];
if (ABS(curDot) > ABS(maxDot))
{
/*It's a better choice*/
maxDot = curDot;
best = k;
}
}
/*Now we have a best match*/
moveVector[0] = maxDot * MATRIXOF(xformMatrix)[best][0];
moveVector[1] = maxDot * MATRIXOF(xformMatrix)[best][1];
moveVector[2] = maxDot * MATRIXOF(xformMatrix)[best][2];
}
eyePosn[0] += moveVector[0];
eyePosn[1] += moveVector[1];
eyePosn[2] += moveVector[2];
/*Put eyePosn back*/
tempObj = NewRealArray(1, 3L);
CArray2Array(tempObj, eyePosn);
SetVar(observer, LOCATION, tempObj);
/*Change focus point*/
focusPoint[0] = eyePosn[0] + rsin(yaw) * perspecStuff[0];
focusPoint[1] = eyePosn[1] + rsin(pitch) * perspecStuff[0];
focusPoint[2] = eyePosn[2] + rcos(yaw) * perspecStuff[0];
tempObj = NewRealArray(1, 3L);
CArray2Array(tempObj, focusPoint);
SetVar(observer, FOCUSPOINT, tempObj);
x = newX;
y = newY;
ResolveController(observer);
DrawMe(object);
}
}
SetVar(object, MOVING, ObjFalse);
ImInvalid(object);
StopSpace(MOVESPACE);
if (logging)
{
ObjPtr eyeStuff;
real eyePosn[3];
eyeStuff = GetFixedArrayVar("PressSpace", observer, LOCATION, 1, 3L);
if (eyeStuff)
{
Array2CArray(eyePosn, eyeStuff);
sprintf(tempStr, "eyeposn [%g %g %g]\n",
eyePosn[0], eyePosn[1], eyePosn[2]);
Log(tempStr);
}
}
return ObjTrue;
}
else
{
return ObjFalse;
}
}
int dsn = 0;
void DrawSpaceContents(space, left, right, bottom, top, viewType)
ObjPtr space;
int left, right, bottom, top;
int viewType;
{
ObjPtr contents;
Bool anyTransparent; /*True iff any objects are transparent*/
ThingListPtr drawList; /*List of objects to draw*/
ObjPtr frontPanel;
frontPanel = GetVar(space, FRONTPANEL);
if (frontPanel && GetVar(frontPanel, BACKGROUND))
{
/*Front panel has a background. Don't need to draw space*/
return;
}
/*Set a new viewport to the current area*/
StartSpace(space, left, right, bottom, top, DRAWSPACE, viewType);
/*Draw the contents of the space*/
contents = GetVar(space, CONTENTS);
/*First draw opaque objects*/
anyTransparent = false;
drawList = LISTOF(contents);
while (drawList)
{
if (IsObject(drawList -> thing))
{
if (GetPredicate(drawList -> thing, ISTRANSPARENT))
{
anyTransparent = true;
}
DrawObject(drawList -> thing);
}
else if (IsList(drawList -> thing))
{
DrawList(drawList -> thing);
}
drawList = drawList -> next;
}
if (anyTransparent && rgbp)
{
zwritemask(0);
blendfunction(BF_SA, BF_MSC);
/***DEBUG
blendfunction(BF_SA, BF_MSA);
*/
drawingTransparent = true;
drawList = LISTOF(contents);
while (drawList)
{
if (IsObject(drawList -> thing))
{
DrawObject(drawList -> thing);
}
else if (IsList(drawList -> thing))
{
DrawList(drawList -> thing);
}
drawList = drawList -> next;
}
blendfunction(BF_ONE, BF_ZERO);
zwritemask(0xffffffff);
drawingTransparent = false;
}
StopSpace(DRAWSPACE);
}
#ifdef PROTO
Bool RotateObserver(ObjPtr observer, real axis[3], real phi, Bool constrain)
#else
Bool RotateObserver(observer, axis, phi, constrain)
ObjPtr observer;
real axis[3];
real phi;
Bool constrain;
#endif
/*Rotates an observer by rotDelta*/
{
ObjPtr xformMatrix;
ObjPtr var;
real focusPoint[3];
Matrix rotDelta;
float t;
float sp, cp; /*Sin and cosine of phi*/
sp = rsin(phi);
cp = rcos(phi);
t = 1.0 - cp;
/*Now that there's a new delta, rotate and redraw*/
xformMatrix = GetMatrixVar("RotateObserver", observer, XFORM);
if (!xformMatrix)
{
return false;
}
if (constrain)
{
int k, best;
float curDot, maxDot = 0.0;
float rr;
/*Constrain the axis to the nearest ortho axis*/
for (k = 0; k < 3; ++k)
{
curDot =
axis[0] * MATRIXOF(xformMatrix)[k][0] +
axis[1] * MATRIXOF(xformMatrix)[k][1] +
axis[2] * MATRIXOF(xformMatrix)[k][2];
if (ABS(curDot) > ABS(maxDot))
{
/*It's a better choice*/
maxDot = curDot;
best = k;
}
}
/*Now we have a best match*/
axis[0] = MATRIXOF(xformMatrix)[best][0];
axis[1] = MATRIXOF(xformMatrix)[best][1];
axis[2] = MATRIXOF(xformMatrix)[best][2];
if (maxDot < 0.0)
{
axis[0] = -axis[0];
axis[1] = -axis[1];
axis[2] = -axis[2];
}
/*Normalize the axis*/
rr = 1.0 / sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
axis[0] *= rr;
axis[1] *= rr;
axis[2] *= rr;
}
/*Now make the change rotation matrix by rows from top to bottom*/
rotDelta[0][0] = t * SQUARE(axis[0]) + cp;
rotDelta[0][1] = t * axis[0] * axis[1] + sp * axis[2];
rotDelta[0][2] = t * axis[0] * axis[2] - sp * axis[1];
rotDelta[0][3] = 0.0;
rotDelta[1][0] = t * axis[0] * axis[1] - sp * axis[2];
rotDelta[1][1] = t * SQUARE(axis[1]) + cp;
rotDelta[1][2] = t * axis[1] * axis[2] + sp * axis[0];
rotDelta[1][3] = 0.0;
rotDelta[2][0] = t * axis[0] * axis[2] + sp * axis[1];
rotDelta[2][1] = t * axis[1] * axis[2] - sp * axis[0];
rotDelta[2][2] = t * SQUARE(axis[2]) + cp;
rotDelta[2][3] = 0.0;
rotDelta[3][0] = 0.0;
rotDelta[3][1] = 0.0;
rotDelta[3][2] = 0.0;
rotDelta[3][3] = 1.0;
var = GetFixedArrayVar("RotateObserver", observer, FOCUSPOINT, 1, 3L);
if (var)
{
Array2CArray(focusPoint, var);
}
else
{
focusPoint[0] = 0.0;
focusPoint[1] = 0.0;
focusPoint[2] = 0.0;
}
pushmatrix();
loadmatrix(Identity);
translate(focusPoint[0], focusPoint[1], focusPoint[2]);
multmatrix(rotDelta);
translate(-focusPoint[0], -focusPoint[1], -focusPoint[2]);
multmatrix(MATRIXOF(xformMatrix));
xformMatrix = NewMatrix();
getmatrix(MATRIXOF(xformMatrix));
popmatrix();
SetVar(observer, XFORM, xformMatrix);
ResolveController(observer);
return true;
}
#ifdef PROTO
Bool RotateLights(ObjPtr lights, real startAxis[3], real phi, Bool constrain, ObjPtr observer, ObjPtr space)
#else
Bool RotateLights(lights, axis, phi, constrain, observer, space)
ObjPtr lights;
real startAxis[3];
real phi;
Bool constrain;
ObjPtr observer;
ObjPtr space;
#endif
/*Rotates lights by rotDelta wrt observer within space*/
{
ObjPtr xformMatrix;
Matrix rotFixed;
ThingListPtr runner;
Matrix rotDelta;
real axis[3];
float t;
float sp, cp; /*Sin and cosine of phi*/
xformMatrix = GetMatrixVar("RotateObserver", observer, XFORM);
if (!xformMatrix)
{
return false;
}
MATCOPY(rotFixed, MATRIXOF(xformMatrix));
runner = LISTOF(lights);
while (runner)
{
if (IsLightSelected(runner -> thing, GetVar(space, CORRAL)))
{
/*Rotate the light*/
ObjPtr var;
real location[3];
real newLoc[3];
float tv[3];
axis[0] = startAxis[0];
axis[1] = startAxis[1];
axis[2] = startAxis[2];
var = GetFixedArrayVar("DrawSpace", runner -> thing, LOCATION, 1, 3L);
if (!var)
{
return false;
}
Array2CArray(location, var);
/*Test elevation*/
tv[0] = location[1];
tv[1] = -location[0];
tv[2] = 0.0;
/*Prerotate by rotFixed*/
newLoc[0] = rotFixed[0][0] * location[0] +
rotFixed[1][0] * location[1] +
rotFixed[2][0] * location[2];
newLoc[1] = rotFixed[0][1] * location[0] +
rotFixed[1][1] * location[1] +
rotFixed[2][1] * location[2];
newLoc[2] = rotFixed[0][2] * location[0] +
rotFixed[1][2] * location[1] +
rotFixed[2][2] * location[2];
location[0] = newLoc[0];
location[1] = newLoc[1];
location[2] = newLoc[2];
if (constrain)
{
float dot1, dot2;
float rr;
/*Constrain the axis to the azimuth or elevation*/
/*Test azimuth*/
dot1 = axis[0] * rotFixed[2][0] +
axis[1] * rotFixed[2][1] +
axis[2] * rotFixed[2][2];
/*Test elevation*/
newLoc[0] = rotFixed[0][0] * tv[0] +
rotFixed[1][0] * tv[1] +
rotFixed[2][0] * tv[2];
newLoc[1] = rotFixed[0][1] * tv[0] +
rotFixed[1][1] * tv[1] +
rotFixed[2][1] * tv[2];
newLoc[2] = rotFixed[0][2] * tv[0] +
rotFixed[1][2] * tv[1] +
rotFixed[2][2] * tv[2];
tv[0] = newLoc[0];
tv[1] = newLoc[1];
tv[2] = newLoc[2];
rr = sqrt(tv[0] * tv[0] + tv[1] * tv[1] + tv[2] * tv[2]);
if (rr > 0.0)
{
rr = 1.0 / rr;
tv[0] *= rr;
tv[1] *= rr;
tv[2] *= rr;
}
else
{
tv[0] = 1.0;
tv[1] = 0.0;
tv[2] = 0.0;
}
dot2 = axis[0] * tv[0] +
axis[1] * tv[1] +
axis[2] * tv[2];
if (ABS(dot1) > ABS(dot2))
{
/*Azimuth is better*/
axis[0] = rotFixed[2][0];
axis[1] = rotFixed[2][1];
axis[2] = rotFixed[2][2];
if (dot1 < 0.0)
{
axis[0] = -axis[0];
axis[1] = -axis[1];
axis[2] = -axis[2];
}
}
else
{
axis[0] = tv[0];
axis[1] = tv[1];
axis[2] = tv[2];
/*Elevation is better*/
if (dot2 < 0.0)
{
axis[0] = -axis[0];
axis[1] = -axis[1];
axis[2] = -axis[2];
}
}
}
sp = rsin(phi);
cp = rcos(phi);
t = 1.0 - cp;
/*Now make the change rotation matrix by rows from top to bottom*/
rotDelta[0][0] = t * SQUARE(axis[0]) + cp;
rotDelta[0][1] = t * axis[0] * axis[1] + sp * axis[2];
rotDelta[0][2] = t * axis[0] * axis[2] - sp * axis[1];
rotDelta[0][3] = 0.0;
rotDelta[1][0] = t * axis[0] * axis[1] - sp * axis[2];
rotDelta[1][1] = t * SQUARE(axis[1]) + cp;
rotDelta[1][2] = t * axis[1] * axis[2] + sp * axis[0];
rotDelta[1][3] = 0.0;
rotDelta[2][0] = t * axis[0] * axis[2] + sp * axis[1];
rotDelta[2][1] = t * axis[1] * axis[2] - sp * axis[0];
rotDelta[2][2] = t * SQUARE(axis[2]) + cp;
rotDelta[2][3] = 0.0;
rotDelta[3][0] = 0.0;
rotDelta[3][1] = 0.0;
rotDelta[3][2] = 0.0;
rotDelta[3][3] = 1.0;
/*Rotate this location by rotDelta*/
newLoc[0] = rotDelta[0][0] * location[0] +
rotDelta[1][0] * location[1] +
rotDelta[2][0] * location[2];
newLoc[1] = rotDelta[0][1] * location[0] +
rotDelta[1][1] * location[1] +
rotDelta[2][1] * location[2];
newLoc[2] = rotDelta[0][2] * location[0] +
rotDelta[1][2] * location[1] +
rotDelta[2][2] * location[2];
location[0] = newLoc[0];
location[1] = newLoc[1];
location[2] = newLoc[2];
/*Unrotate by rotFixed*/
newLoc[0] = rotFixed[0][0] * location[0] +
rotFixed[0][1] * location[1] +
rotFixed[0][2] * location[2];
newLoc[1] = rotFixed[1][0] * location[0] +
rotFixed[1][1] * location[1] +
rotFixed[1][2] * location[2];
newLoc[2] = rotFixed[2][0] * location[0] +
rotFixed[2][1] * location[1] +
rotFixed[2][2] * location[2];
NORM3(newLoc);
var = NewRealArray(1, 3L);
CArray2Array(var, newLoc);
SetVar(runner -> thing, LOCATION, var);
ResolveController(runner -> thing);
}
runner = runner -> next;
}
}
ObjPtr DrawSpace(object)
ObjPtr object;
/*Draws a space and everything it contains*/
{
int left, right, bottom, top;
ObjPtr eyePosnObj; /*Eyeposition object*/
ObjPtr tempObj; /*Temporary object*/
ObjPtr dRotMatrix;
ObjPtr observer, observers; /*Observer of the space*/
ObjPtr panel; /*Front and back panel*/
ObjPtr renderer; /*Current renderer*/
ObjPtr var; /*A random variable*/
int renderType, filterType; /*Types of renderers and filters*/
int viewType; /*Type of view*/
renderer = GetObjectVar("DrawSpace", object, RENDERER);
if (!renderer) return ObjFalse;
/*Get render type*/
var = GetIntVar("DrawSpace", renderer, RENDERTYPE);
if (var)
{
renderType = GetInt(var);
}
else
{
renderType = RT_HARDWARE;
}
if (renderType == RT_NONE)
{
/*Don't render*/
return ObjTrue;
}
/*Get filter type*/
var = GetIntVar("DrawSpace", renderer, FILTERTYPE);
if (var)
{
filterType = GetInt(var);
}
else
{
filterType = FT_NONE;
}
Get2DIntBounds(object, &left, &right, &bottom, &top);
/*Update the rotation matrix and fly through*/
#if 0
observers = GetListVar("DrawSpace", object, OBSERVERS);
if (!observers || !LISTOF(observers)) return ObjFalse;
observer = LISTOF(observers) -> thing;
#else
observer = GetObjectVar("DrawSpace", object, OBSERVER);
#endif
var = GetVar(observer, VIEWTYPE);
if (var)
{
viewType = GetInt(var);
}
else
{
viewType = VT_PERSPECTIVE;
}
switch(viewType)
{
case VT_PERSPECTIVE:
DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER);
break;
case VT_ORTHOGRAPHIC:
DrawSpaceContents(object, left, right, bottom, top, VIEW_ORTHO);
break;
case VT_CROSSEYED:
DrawSpaceContents(object, left, right, bottom, top, VIEW_XLEFT);
DrawSpaceContents(object, left, right, bottom, top, VIEW_XRIGHT);
break;
case VT_REDCYAN:
if (rgbp)
{
DrawSpaceContents(object, left, right, bottom, top, VIEW_RCLEFT);
DrawSpaceContents(object, left, right, bottom, top, VIEW_RCRIGHT);
}
else
{
DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER);
}
break;
}
/*Filter space if need be*/
if (rgbp && drawingQuality == DQ_FULL && filterType == FT_SHRINK)
{
/*Shrink the pixels in the window*/
int s, d;
register int y, x;
int xdd, ydd;
int xSize, ySize;
if (right - left > SCRWIDTH) right = left + SCRWIDTH;
if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT;
if ((right - left) & 1) --right;
if ((top - bottom) & 1) --top;
xdd = (right - left) / 2;
ydd = (top - bottom) / 2;
xSize = (right - left);
ySize = (top - bottom);
lrectread(left, bottom, right, top, (unsigned int *) imageBuffer);
s = 0;
d = 0;
for (y = 0; y <= ydd; ++y)
{
for (x = 0; x <= xdd; ++x)
{
imageBuffer[d] . red =
(imageBuffer[s] . red +
imageBuffer[s + 1] . red +
imageBuffer[s + xSize] . red +
imageBuffer[s + xSize + 1] . red) / 4;
imageBuffer[d] . green =
(imageBuffer[s] . green +
imageBuffer[s + 1] . green +
imageBuffer[s + xSize] . green +
imageBuffer[s + xSize + 1] . green) / 4;
imageBuffer[d] . blue =
(imageBuffer[s] . blue +
imageBuffer[s + 1] . blue +
imageBuffer[s + xSize] . blue +
imageBuffer[s + xSize + 1] . blue) / 4;
imageBuffer[d] . alpha =
(imageBuffer[s] . alpha +
imageBuffer[s + 1] . alpha +
imageBuffer[s + xSize] . alpha +
imageBuffer[s + xSize + 1] . alpha) / 4;
s += 2;
++d;
}
s += xSize;
}
cpack(0);
clear();
lrectwrite(left, bottom, left + xdd, bottom + ydd, (unsigned int *) imageBuffer);
}
if (rgbp && drawingQuality == DQ_FULL && filterType == FT_4AVERAGE)
{
/*Average the pixels in the window*/
int s;
register int y, x;
int xSize, ySize;
if (right - left > SCRWIDTH) right = left + SCRWIDTH;
if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT;
xSize = (right - left);
ySize = (top - bottom);
lrectread(left, bottom, right, top, (unsigned int *) imageBuffer);
s = 0;
for (y = 0; y <= ySize - 1; ++y)
{
for (x = 0; x <= xSize - 1; ++x)
{
imageBuffer[s] . red =
(imageBuffer[s] . red +
imageBuffer[s + 1] . red +
imageBuffer[s + xSize] . red +
imageBuffer[s + xSize + 1] . red) / 4;
imageBuffer[s] . green =
(imageBuffer[s] . green +
imageBuffer[s + 1] . green +
imageBuffer[s + xSize] . green +
imageBuffer[s + xSize + 1] . green) / 4;
imageBuffer[s] . blue =
(imageBuffer[s] . blue +
imageBuffer[s + 1] . blue +
imageBuffer[s + xSize] . blue +
imageBuffer[s + xSize + 1] . blue) / 4;
imageBuffer[s] . alpha = 255;
++s;
}
++s;
}
lrectwrite(left, bottom, right, top, (unsigned int *) imageBuffer);
}
return ObjTrue;
}
static ObjPtr KeyDownSpace(object, key)
ObjPtr object;
short key;
/*Does a keydown in a space. Returns
true iff the press really was in the space.*/
{
ObjPtr retVal;
return ObjFalse;
}
ObjPtr ShowIconControls(object)
ObjPtr object;
/*Shows the controls for an icon*/
{
DoTask(DoShowControls);
return ObjTrue;
}
static void DoFileInVisAlert()
{
WinInfoPtr errWindow;
errWindow = AlertUser(UIRED, (WinInfoPtr) 0, "Files cannot be visualized directly. First open the files and then visualize the datasets they contain.", 0, 0, "");
SetVar((ObjPtr) errWindow, HELPSTRING,
NewString("SciAn must first read data files into datasets before the data can \
be visualized. First open the file, then select the \
datasets you want to visualize within the Datasets window and visualize them."));
}
static void DoCannotVisError()
/*Whines at the user that it cannot visualize an object*/
{
WinInfoPtr errWindow;
errWindow = AlertUser(UIRED, (WinInfoPtr) 0, "Some objects could not be visualized.", 0, 0, "");
SetVar((ObjPtr) errWindow, HELPSTRING,
NewString("Some of the objects which you have tried to visualize could not \
be visualized, either because they are of the wrong type for visualization or \
because they need to be modified before they can be visualized."));
}
Bool AddObjToSpace(object, space, corral, loc, visType)
ObjPtr object;
ObjPtr space;
ObjPtr corral;
ObjPtr loc;
ObjPtr visType;
/*Adds object or a representation of it to space.
If the class of object is
iconClass Finds a preferred visualization for REPOBJ and adds it
visClass Adds it directly
fileClass Whines
otherwise Finds a preferred visualization and adds it
loc is a location to put the new icon, or NULLOBJ
visType is the preferred visualization, or NULLOBJ to get the preferred
*/
{
ObjPtr repObj, icon, defaultIcon;
ObjPtr name;
ObjPtr contents;
ObjPtr clock;
FuncTyp AddControls;
FuncTyp method;
ObjPtr parents;
if (InClass(object, iconClass))
{
/*It's an icon; got to find a REPOBJ*/
object = GetVar(object, REPOBJ);
if (!object)
{
return false;
}
}
if (InClass(object, fileClass))
{
/*It's a file. Whine*/
DoUniqueTask(DoFileInVisAlert);
return false;
}
if (InClass(object, visClass) && visType)
{
/*Go down to dataset level if visObject*/
repObj = GetVar(object, MAINDATASET);
if (!repObj)
{
repObj = GetVar(object, REPOBJ);
}
object = repObj;
}
if (!InClass(object, visClass))
{
/*It's not a visualization yet. Gotta find one*/
repObj = object;
object = NewVis(repObj, visType);
if (!object)
{
DoUniqueTask(DoCannotVisError);
return false;
}
if (globalEventFlags & F_OPTIONDOWN)
{
SetVar(object, HIDDEN, ObjTrue);
}
}
else
{
repObj = GetVar(object, MAINDATASET);
if (!repObj)
{
repObj = GetVar(object, REPOBJ);
}
}
/*If object is a template, make a new copy*/
if (GetPredicate(object, TEMPLATEP))
{
FuncTyp method;
method = GetMethodSurely("AddObjToSpace", object, CLONE);
if (method)
{
object = (*method)(object);
}
}
contents = GetVar(space, CONTENTS);
PrefixList(contents, object);
parents = GetListVar("AddObjToSpace", object, PARENTS);
if (parents)
{
PrefixList(parents, space);
}
else
{
SetVar(object, PARENT, space);
}
ImInvalid(object);
SetVar(object, SPACE, space);
/*Make an icon that represents the field and put it in the corral*/
icon = NewVisIcon(object);
if (icon)
{
SetVar(icon, ICONLOC, loc);
SetVar(icon, SPACE, space);
if (globalEventFlags & F_OPTIONDOWN)
{
SetVar(icon, ICONGREYED, ObjTrue);
}
DropIconInCorral(corral, icon);
}
/*Reinitialize the clock in the space*/
clock = GetVar(space, CLOCK);
if (clock)
{
ReinitController(clock);
}
return true;
}
Bool DeleteControllerFromSpace(controller, space, corral)
ObjPtr controller, space, corral;
/*Deletes a controller from a space and its icon from the corral*/
{
ObjPtr spaces;
ObjPtr contents;
ThingListPtr list;
spaces = GetListVar("DeleteControllerFromSpace", controller, SPACES);
if (!spaces) return false;
/*Remove the space reference from the controller*/
if (0 == DeleteFromList(spaces, space)) return false;
/*Remove the controller's icon from the corral*/
contents = GetListVar("DeleteControllerFromSpace", corral, CONTENTS);
list = LISTOF(contents);
while (list)
{
ObjPtr foundController;
foundController = GetVar(list -> thing, REPOBJ);
if (foundController == controller)
{
DeleteFromList(contents, list -> thing);
ImInvalid(corral);
break;
}
list = list -> next;
}
return true;
}
static ObjPtr BindClockToSpace(clock, space)
ObjPtr clock, space;
/*Makes space know about clock*/
{
SetVar(space, CLOCK, clock);
return ObjTrue;
}
static ObjPtr BindObserverToSpace(observer, space)
ObjPtr observer, space;
/*Makes space know about observer*/
{
#if 0
ObjPtr observersList;
observersList = GetVar(space, OBSERVERS);
if (!observersList)
{
observersList = NewList();
SetVar(space, OBSERVERS, observersList);
}
PostfixList(observersList, observer);
#else
SetVar(space, OBSERVER, observer);
#endif
return ObjTrue;
}
static ObjPtr BindRendererToSpace(renderer, space)
ObjPtr renderer, space;
/*Makes space know about renderer*/
{
SetVar(space, RENDERER, renderer);
return ObjTrue;
}
Bool AddControllerToSpace(controller, space, corral, loc)
ObjPtr controller;
ObjPtr space;
ObjPtr corral;
ObjPtr loc;
/*Adds a space controller to space at loc and puts its icon in corral.
*/
{
ObjPtr repObj, icon, iconY;
ObjPtr contents, spaces;
ThingListPtr list;
ObjPtr controllerClass;
ObjPtr defaultIcon;
ObjPtr name;
FuncTyp method;
controllerClass = GetVar(controller, CONTROLLERCLASS);
if (controllerClass)
{
/*First see if there is already a controller there*/
contents = GetListVar("AddControllerToSpace", corral, CONTENTS);
if (!contents) return;
list = LISTOF(contents);
while (list)
{
ObjPtr foundController;
foundController = GetVar(list -> thing, REPOBJ);
if (InClass(foundController, controllerClass))
{
if (foundController == controller)
{
/*This is really the same controller. Just move the icon*/
if (loc)
{
SetVar(list -> thing, ICONLOC, loc);
ImInvalid(list -> thing);
}
return true;
}
else
{
/*It's a new controller. Delete the old one and fall through*/
DeleteControllerFromSpace(foundController, space, corral);
break;
}
}
list = list -> next;
}
}
/*Make an icon that represents the new controller and put it in the corral*/
name = GetStringVar("AddControllerToSpace", controller, NAME);
defaultIcon = GetObjectVar("AddControllerToSpace", controller, DEFAULTICON);
if (defaultIcon)
{
icon = NewObject(defaultIcon, 0);
SetVar(icon, NAME, name ? name : NewString("Controller"));
SetVar(icon, ICONGREYED, GetVar(controller, HIDDEN));
}
else
{
icon = NewIcon(0, 0, ICONQUESTION,
name ? GetString(name) : "Controller");
SetVar(icon, HELPSTRING,
NewString("This icon represents a controller. For some reason, \
the default icon could not be found, so the icon appears as a question mark. \
Please report this as a bug in SciAn."));
}
SetMethod(icon, DOUBLECLICK, GetMethod(controller, DOUBLECLICK));
SetVar(icon, SPACE, space);
SetVar(icon, REPOBJ, controller);
method = GetMethod(controller, BINDTOSPACE);
if (method)
{
(*method)(controller, space);
}
SetVar(icon, ICONLOC, loc);
DropIconInCorral(corral, icon);
/*Make the controller know about this space*/
spaces = GetListVar("AddControllerToSpace", controller, SPACES);
if (!spaces) return;
PrefixList(spaces, space);
ResolveController(controller);
return true;
}
ObjPtr NewSpace(left, right, bottom, top)
int left, right, bottom, top;
/*Makes a new Space with bounds left, right, bottom, top*/
{
ObjPtr xformMatrix; /*The rotation matrix of the space, I to start*/
ObjPtr retVal;
retVal = NewObject(spaceClass, 0);
Set2DIntBounds(retVal, left, right, bottom, top);
SetVar(retVal, CONTENTS, NewList());
return retVal;
}
#ifdef PROTO
void PrintClock(char *s, char *f, real t)
#else
void PrintClock(s, f, t)
char *s;
char *f;
real t;
#endif
/*Prints t in seconds to a string s using format f*/
{
while (*f)
{
if (*f == '%')
{
/*Format string*/
++f;
if (*f == '%')
{
/*Just print out a %*/
*s++ = *f++;
}
else
{
char *temp; /*Pointer into tempStr to assemble*/
real n; /*Number to print*/
long i; /*Temporary integer*/
/*It's a real format. Start assembling*/
temp = tempStr;
*temp++ = '%';
/*Skip over flag(s)*/
while (*f == '-' || *f == '+' || *f == ' ' || *f == '#')
{
*temp++ = *f++;
}
/*Skip over first number*/
while (*f >= '0' && *f <= '9')
{
*temp++ = *f++;
}
/*Skip over second number*/
if (*f == '.')
{
*temp++ = *f++;
while (*f >= '0' && *f <= '9')
{
*temp++ = *f++;
}
}
/*Now see what the format is*/
switch (*f)
{
case 'h':
/*Hours, [1,12]*/
n = t / 3600.0;
i = n / 12.0;
n = n - i * 12;
if (n < 1.00) n += 12.00;
break;
case 'H':
/*Hours, [0,24)*/
n = t / 3600.0;
i = n / 24.0;
i = n - i * 24;
n = i;
break;
case 'M':
/*Unrestricted minutes*/
n = t / 60.0;
break;
case 'm':
/*Minutes, 0 to 59*/
n = t / 60.0;
i = n / 60.0;
i = n - i * 60;
n = i;
break;
case 's':
/*Seconds, 0 to 59*/
i = t / 60.0;
i = t - i * 60;
n = i;
break;
case 'S':
/*Unrestricted seconds*/
case 't':
case 'T':
/*Just a timestep*/
n = t;
break;
case 'd':
case 'D':
case 'i':
case 'I':
/*Integral seconds*/
i = t;
n = i;
break;
default:
strcpy(s, "Bad format: ");
while (*s) ++s;
*s++ = *f++;
*s = 0;
goto dontsprintf;
}
++f;
/*Ready to print*/
*temp++ = 'f';
*temp = 0;
sprintf(s, tempStr, n);
dontsprintf:
while (*s)
{
++s;
}
}
}
else
{
*s++ = *f++;
}
}
*s = 0;
}
static ObjPtr TouchSpaceClock(clock, space)
ObjPtr clock, space;
/*Touches a space with a clock. Returns ObjTrue if it had an effect,
ObjFalse otherwise.*/
{
real time;
ObjPtr timeObj;
ObjPtr timeBounds;
Bool clockHasTime;
Bool boundsChanged;
ObjPtr objTime;
real tb[2];
ObjPtr displayClock;
ObjPtr format;
ObjPtr contents;
ObjPtr repObj; /*The repobj of a vis object, ie, main dataset*/
ObjPtr data; /*The DATA ARRAY of the main dataset*/
char s[256];
ThingListPtr runner;
timeObj = GetVar(clock, TIME);
if (timeObj)
{
time = GetReal(timeObj);
}
else
{
time = 0.0;
}
format = GetVar(clock, FORMAT);
if (format)
{
PrintClock(s, GetString(format), time);
}
else
{
sprintf(s, "%#.2f", time);
}
/*See if there's a display clock in the space*/
displayClock = GetVar(space, DISPLAYCLOCK);
if (displayClock)
{
/*Yes, display it*/
SetTextBox(displayClock, s);
}
/*Go through all the objects in the space expanding the time bounds*/
timeBounds = GetVar(clock, TIMEBOUNDS);
clockHasTime = timeBounds ? true : false;
contents = GetListVar("TouchSpaceClock", space, CONTENTS);
if (!contents)
{
return ObjTrue;
}
boundsChanged = false;
runner = LISTOF(contents);
while (runner)
{
ChangeVar(runner -> thing, TIME, timeObj);
repObj = GetVar(runner -> thing, MAINDATASET);
if (!repObj) repObj = GetObjectVar("TouchSpaceClock", runner -> thing, REPOBJ);
if (repObj)
{
data = GetVar(repObj, DATA);
if (data)
{
ObjPtr objTime;
real otb[2];
MakeVar(data, TIMEBOUNDS);
objTime = GetVar(data, TIMEBOUNDS);
if (objTime)
{
ObjPtr lastTimeStep;
real lts;
lastTimeStep = GetRealVar("TouchSpaceClock", data, LASTTIMESTEP);
if (lastTimeStep)
{
lts = GetReal(lastTimeStep);
}
else
{
lts = 0.0;
}
Array2CArray(otb, objTime);
if (clockHasTime)
{
if (boundsChanged)
{
if (otb[0] < tb[0])
{
tb[0] = otb[0];
}
}
else
{
tb[0] = otb[0];
boundsChanged = true;
}
if (boundsChanged)
{
if (otb[1] > tb[1] + lts)
{
tb[1] = otb[1] + lts;
}
}
else
{
tb[1] = otb[1] + lts;
boundsChanged = true;
}
}
else
{
tb[0] = otb[0];
tb[1] = otb[1] + lts;
clockHasTime = true;
boundsChanged = true;
}
}
}
}
runner = runner -> next;
}
if (boundsChanged)
{
timeBounds = NewRealArray(1, 2L);
CArray2Array(timeBounds, tb);
SetVar(clock, TIMEBOUNDS, timeBounds);
}
return ObjTrue;
}
ObjPtr RegisterTime(clock)
ObjPtr clock;
/*Registers time or timebounds changes in clock*/
{
ObjPtr time;
WinInfoPtr dialog;
time = GetVar(clock, TIME);
/*Now change the slider*/
dialog = DialogExists((WinInfoPtr) clock, NewString("Clock"));
if (dialog)
{
ObjPtr control;
control = GetVar((ObjPtr) dialog, TIMECONTROL);
if (control)
{
ObjPtr value;
value = GetVar(control, VALUE);
if (time && !Equal(value, time))
{
FuncTyp method;
method = GetMethod(control, CHANGEDVALUE);
SetMethod(control, CHANGEDVALUE, (FuncTyp) 0);
SetValue(control, time);
AutoScroll(control);
SetMethod(control, CHANGEDVALUE, method);
}
}
}
SetVar(clock, TIMEREGISTERED, ObjTrue);
return ObjTrue;
}
ObjPtr ResolveClock(clock)
ObjPtr clock;
/*Resolves a clock after all the spaces have ben touched*/
{
MakeVar(clock, TIMEREGISTERED);
return ObjTrue;
}
static ObjPtr TouchSpaceObserver(observer, space)
ObjPtr observer, space;
/*Touches a space with an observer. Returns ObjTrue if it had an effect,
ObjFalse otherwise.*/
{
#if 0
ObjPtr observers;
if ((observers = GetListVar("TouchSpaceObserver", space, OBSERVERS)) &&
LISTOF(observers) -> thing == observer)
{
return ObjTrue;
}
else
{
return ObjFalse;
}
#else
return ObjTrue;
#endif
}
static ObjPtr TouchSpaceRenderer(renderer, space)
ObjPtr renderer, space;
/*Touches a space with an renderer. Returns ObjTrue if it had an effect,
ObjFalse otherwise.*/
{
ObjPtr renderers;
if (GetVar(space, RENDERER) == renderer)
{
return ObjTrue;
}
else
{
return ObjFalse;
}
}
void ResolveController(controller)
ObjPtr controller;
/*Resolves all the space references from controller*/
{
ObjPtr spaces;
ThingListPtr list;
FuncTyp method;
spaces = GetListVar("ResolveController", controller, SPACES);
if (!spaces) return;
list = LISTOF(spaces);
while (list)
{
method = GetMethod(controller, TOUCHSPACE);
if (method)
{
if (IsTrue((*method)(controller, list -> thing)))
{
ImInvalid(list -> thing);
}
}
method = GetMethod(controller, TOUCHPANEL);
if (method)
{
if (IsTrue((*method)(controller, GetVar(list -> thing, PANEL))))
{
ImInvalid(GetVar(list -> thing, PANEL));
}
}
list = list -> next;
}
/*Let the controller know if it's been resolved*/
method = GetMethod(controller, RESOLVE);
if (method)
{
(*method)(controller);
}
}
void ReinitController(controller)
ObjPtr controller;
/*Reinitializes a controller by sending it a REINIT message and then resolving
it*/
{
FuncTyp method;
method = GetMethod(controller, REINIT);
if (method)
{
(*method)(controller);
}
ResolveController(controller);
}
static void DoClockAlert()
{
WinInfoPtr errWindow;
errWindow = AlertUser(UIRED, (WinInfoPtr) 0, "A clock cannot be displayed in a space it doesn't control.", 0, 0, "");
SetVar((ObjPtr) errWindow, HELPSTRING,
NewString("You have just tried to drag a clock into a space that it does not control. \
In order for a clock to be displayed in a space, it must control that space. \
You can make a clock control a space by dragging it into the icon corral of \
the visualization window which contains the space."));
}
static ObjPtr DeleteClockReadout(readout)
ObjPtr readout;
/*Deletes a clock readout from a space*/
{
ObjPtr space;
space = GetObjectVar("DeleteClockReadout", readout, SPACE);
if (!space)
{
return ObjFalse;
}
SetVar(space, DISPLAYCLOCK, NULLOBJ);
return ObjTrue;
}
void AddClockToSpacePanel(clock, panel, space, x, y)
ObjPtr clock, panel, space;
int x, y;
/*Adds clock to panel. If there is already a clock there, just move it and
update the clock*/
{
ObjPtr clockThere;
if (GetVar(space, CLOCK) != clock)
{
DoTask(DoClockAlert);
return;
}
clockThere = GetVar(space, DISPLAYCLOCK);
if (clockThere)
{
/*There's already a clock there, just move and update*/
int w, h;
int l, r, b, t;
Get2DIntBounds(clockThere, &l, &r, &b, &t);
w = r - l;
h = t - b;
Set2DIntBounds(clockThere, x - w / 2, x + (w + 1) / 2,
y - (h + 1) / 2, y + h / 2);
ImInvalid(clockThere);
}
else
{
clockThere = NewTextBox(x - DSPCLOCKWIDTH / 2, x + DSPCLOCKWIDTH / 2,
y - DSPCLOCKHEIGHT / 2, y + DSPCLOCKHEIGHT / 2,
/*WITH_BG + EDITABLE*/ ADJUSTABLE, "Space Clock", "1:37");
PrefixList(GetVar(panel, CONTENTS), clockThere);
SetMethod(clockThere, DELETEICON, DeleteClockReadout);
SetVar(clockThere, SPACE, space);
SetVar(clockThere, HELPSTRING, NewString("This text box displays the time \
given by the clock in this space. The display format is controlled by the Format \
text box in the clock control panel.\n"));
SetVar(clockThere, PARENT, panel);
SetTextAlign(clockThere, RIGHTALIGN);
SetTextFont(clockThere, "Helvetica-Bold");
SetTextSize(clockThere, 18);
SetTextColor(clockThere, NewInt(UIGRAY75));
SetVar(clockThere, STICKINESS, NewInt(FLOATINGLEFT + FLOATINGRIGHT + FLOATINGTOP + FLOATINGBOTTOM));
MakeMeCurrent(clockThere);
}
SetVar(clockThere, REPOBJ, clock);
SetVar(space, DISPLAYCLOCK, clockThere);
/*Resolve the clock references*/
ResolveController(clock);
}
int pdspSerialNum = 0;
static ObjPtr DeletePaletteDisplay(display)
ObjPtr display;
/*Deletes display*/
{
return ObjTrue;
}
void AddPaletteToSpacePanel(palette, panel, space, x, y)
ObjPtr palette, panel, space;
int x, y;
/*Adds palette to panel.*/
{
ObjPtr paletteDisplay;
sprintf(tempStr, "Palette Display %d", ++pdspSerialNum);
paletteDisplay = NewPaletteDisplay(x - DSPPALETTEWIDTH / 2, x + DSPPALETTEWIDTH / 2,
y - DSPPALETTEHEIGHT / 2, y + DSPPALETTEHEIGHT / 2,
tempStr, palette);
PrefixList(GetVar(panel, CONTENTS), paletteDisplay);
SetVar(paletteDisplay, PARENT, panel);
SetMethod(paletteDisplay, DELETEICON, DeletePaletteDisplay);
SetVar(paletteDisplay, STICKINESS, NewInt(FLOATINGLEFT + FLOATINGRIGHT + FLOATINGTOP + FLOATINGBOTTOM));
MakeMeCurrent(paletteDisplay);
SetTextFont(paletteDisplay, ANNOTFONT);
SetTextSize(paletteDisplay, ANNOTFONTSIZE);
}
static ObjPtr DropInSpacePanel(panel, dropObj, x, y)
ObjPtr panel, dropObj;
int x, y;
/*Drops object in a panel beginning at x and y. Returns
true iff the drop really was in the panel.*/
{
int left, right, bottom, top;
Get2DIntBounds(panel, &left, &right, &bottom, &top);
if (x >= left && x <= right && y >= bottom && y <= top)
{
/*Hey! It really was a drop in the panel*/
ObjPtr contents;
ObjPtr firstIcon;
ThingListPtr restIcons;
ThingListPtr runner;
ObjPtr repObj;
ObjPtr space;
ObjPtr iconLoc;
real loc[2];
int xDisp, yDisp;
if (IsList(dropObj))
{
restIcons = LISTOF(dropObj);
firstIcon = restIcons -> thing;
restIcons = restIcons -> next;
}
else if (InClass(dropObj, iconClass))
{
firstIcon = dropObj;
restIcons = 0;
}
else
{
ReportError("DropInSpacePanel", "An object other than an icon was dropped");
return ObjFalse;
}
space = GetObjectVar("DropInSpacePanel", panel, SPACE);
if (!space) return;
iconLoc = GetFixedArrayVar("DropInSpacePanel", firstIcon, ICONLOC, 1, 2L);
if (!iconLoc)
{
return ObjFalse;
}
Array2CArray(loc, iconLoc);
/*Setup the new viewport*/
StartPanel(left, right, bottom, top);
x -= left;
y -= bottom;
xDisp = x - loc[0];
yDisp = y - loc[1];
/*Drop first icon*/
repObj = GetVar(firstIcon, REPOBJ);
if (InClass(repObj, clockClass))
{
AddClockToSpacePanel(repObj, panel, space, x, y);
}
else if (IsPalette(repObj))
{
AddPaletteToSpacePanel(repObj, panel, space, x, y);
}
else
{
AddObjToSpace(repObj, space, GetVar(space, CORRAL), NULLOBJ, NULLOBJ);
}
/*Drop remaining icons*/
runner = restIcons;
while (runner)
{
repObj = GetVar(runner -> thing, REPOBJ);
if (InClass(repObj, clockClass))
{
iconLoc = GetFixedArrayVar("DropInSpacePanel", runner -> thing, ICONLOC, 1, 2L);
if (!iconLoc) break;
Array2CArray(loc, iconLoc);
loc[0] += xDisp;
loc[1] += yDisp;
AddClockToSpacePanel(repObj, panel, space, (int) loc[0], (int) loc[1]);
}
else if (IsPalette(repObj))
{
iconLoc = GetFixedArrayVar("DropInSpacePanel", runner -> thing, ICONLOC, 1, 2L);
if (!iconLoc) break;
Array2CArray(loc, iconLoc);
loc[0] += xDisp;
loc[1] += yDisp;
AddPaletteToSpacePanel(repObj, panel, space, (int) loc[0], (int) loc[1]);
}
else
{
AddObjToSpace(repObj, space, GetVar(space, CORRAL), NULLOBJ, NULLOBJ);
}
runner = runner -> next;
}
StopPanel();
return ObjTrue;
}
else
{
return ObjFalse;
}
}
#ifdef PROTO
void SetClock(ObjPtr clock, real time)
#else
void SetClock(clock, time)
ObjPtr clock;
real time;
#endif
/*Sets clock to time*/
{
SetVar(clock, TIME, NewReal(time));
ResolveController(clock);
}
static ObjPtr MarkClockTime(clock, lateness)
ObjPtr clock;
double lateness;
/*Marks time in a clock after a delay of lateness*/
{
ObjPtr curTime;
ObjPtr dTime;
ObjPtr timeBounds;
real deltaTime;
real time;
real tb[2];
ObjPtr name;
ObjPtr whichDialog;
WinInfoPtr dialog;
ObjPtr spaces;
ThingListPtr list;
Bool wrap;
InhibitLogging(true);
dTime = GetVar(clock, DTIME);
if (!dTime || !IsReal(dTime))
{
return ObjTrue;
}
deltaTime = GetReal(dTime);
wrap = GetPredicate(clock, CYCLECLOCK);
/*Another wakeup call*/
DoNotDisturb(clock, MARKTIME);
WakeMe(clock, MARKTIME, Clock());
if (lateness <= 0.0)
{
return ObjTrue;
}
curTime = GetVar(clock, TIME);
if (curTime)
{
time = GetReal(curTime);
}
else
{
time = 0.0;
}
/*Increment the time by the elapsed time*/
time += deltaTime * lateness;
/*Check against time bounds*/
timeBounds = GetVar(clock, TIMEBOUNDS);
if (timeBounds && IsArray(timeBounds) && RANK(timeBounds) == 1 &&
DIMS(timeBounds)[0] == 2)
{
Array2CArray(tb, timeBounds);
}
else
{
tb[0] = 0.0;
tb[1] = 1.0;
}
if (deltaTime > 0.0 && time > tb[1])
{
if (wrap) time = tb[0];
else time = tb[1];
}
else if (deltaTime < 0.0 && time < tb[0])
{
if (wrap) time = tb[1];
else time = tb[0];
}
/*Now change the clock*/
SetClock(clock, time);
InhibitLogging(false);
return ObjTrue;
}
static ObjPtr ChangeTime(object)
ObjPtr object;
/*Changed value for a control time*/
{
real time;
ObjPtr val;
ObjPtr repObj;
ObjPtr spaces;
ThingListPtr list;
repObj = GetObjectVar("ChangeTime", object, REPOBJ);
if (!repObj)
{
return ObjFalse;
}
val = GetValue(object);
if (val)
{
time = GetReal(val);
}
else
{
time = 0.0;
}
SetClock(repObj, time);
spaces = GetListVar("ChangeTime", repObj, SPACES);
if (!spaces) return ObjFalse;
list = LISTOF(spaces);
while (list)
{
DrawMe(list -> thing);
list = list -> next;
}
return ObjTrue;
}
#ifdef PROTO
void GetSliderRange(ObjPtr slider, real *loValue, real *hiValue)
#else
void GetSliderRange(slider, loValue, hiValue)
ObjPtr slider;
real *loValue, *hiValue;
#endif
/*Returns the range of slider into loValue and hiValue*/
{
ObjPtr var;
var = GetRealVar("GetSliderRange", slider, LOVALUE);
if (var) *loValue = GetReal(var);
var = GetRealVar("GetSliderRange", slider, HIVALUE);
if (var) *hiValue = GetReal(var);
}
static void ClockSliderToRadio(radioGroup, slider)
ObjPtr radioGroup, slider;
/*Sets the value of the radio group based on the value of the dtime slider*/
{
real loValue, hiValue;
FuncTyp method;
ObjPtr var;
real value;
/*Temporarily save changedvalue method*/
method = GetMethod(radioGroup, CHANGEDVALUE);
SetMethod(radioGroup, CHANGEDVALUE, (FuncTyp) 0);
GetSliderRange(slider, &loValue, &hiValue);
var = GetValue(slider);
if (var) value = GetReal(var); else value = 0.0;
if (value == 0.0)
{
/*Stop*/
SetValue(radioGroup, NewInt(0));
}
else if (value >= (hiValue - loValue) * (FASTCLOCK + PLAYCLOCK) * 0.5)
{
/*Fast Forward*/
SetValue(radioGroup, NewInt(3));
}
else if (value <= (loValue - hiValue) * (FASTCLOCK + PLAYCLOCK) * 0.5)
{
/*Fast Reverse*/
SetValue(radioGroup, NewInt(4));
}
else if (value > 0.0)
{
/*Play*/
SetValue(radioGroup, NewInt(1));
}
else
{
/*Reverse*/
SetValue(radioGroup, NewInt(2));
}
/*Put changedvalue method back*/
SetMethod(radioGroup, CHANGEDVALUE, method);
}
static ObjPtr ChangeDeltaTime(object)
ObjPtr object;
/*Changed value for a slider delta time*/
{
real deltaTime;
ObjPtr val;
ObjPtr repObj;
ObjPtr speedControl;
repObj = GetObjectVar("ChangeDeltaTime", object, REPOBJ);
if (!repObj)
{
return ObjFalse;
}
val = GetVar(object, VALUE);
if (val)
{
deltaTime = GetReal(val);
}
else
{
deltaTime = 0.0;
}
SetVar(repObj, DTIME, NewReal(deltaTime));
speedControl = GetObjectVar("ChangeDeltaTime", object, SPEEDCONTROL);
if (speedControl)
{
ClockSliderToRadio(speedControl, object);
}
DoNotDisturb(repObj, MARKTIME);
if (deltaTime != 0.0)
{
WakeMe(repObj, MARKTIME, Clock());
}
return ObjTrue;
}
static ObjPtr ChangeCycleClock(checkBox)
ObjPtr checkBox;
/*Sets the CYCLECLOCK in the checkBoxes repobj to the value of checkBox*/
{
ObjPtr repObj, value;
repObj = GetObjectVar("ChangeCycleClock", checkBox, REPOBJ);
if (!repObj) return ObjFalse;
value = GetValue(checkBox);
if (!IsInt(value) && !IsReal(value)) return ObjFalse;
SetVar(repObj, CYCLECLOCK, GetInt(value) ? ObjTrue : ObjFalse);
return ObjTrue;
}
static ObjPtr ChangeClockSpeed(radioGroup)
ObjPtr radioGroup;
/*Changes the speed of a clock according to a newly pressed button*/
{
ObjPtr slider;
real loValue, hiValue;
ObjPtr var;
int value;
FuncTyp method;
var = GetValue(radioGroup);
if (var) value = GetInt(var); else value = 0;
slider = GetObjectVar("ChangeClockSpeed", radioGroup, REPOBJ);
if (!slider) return ObjFalse;
GetSliderRange(slider, &loValue, &hiValue);
/*Temporarily save my changedvalue method*/
method = GetMethod(radioGroup, CHANGEDVALUE);
SetMethod(radioGroup, CHANGEDVALUE, (FuncTyp) 0);
switch (value)
{
case 0: /*Stop*/
SetValue(slider, NewReal(0.0));
break;
case 1: /*Play*/
SetValue(slider, NewReal((hiValue - loValue) * PLAYCLOCK));
break;
case 2: /*Reverse*/
SetValue(slider, NewReal((loValue - hiValue) * PLAYCLOCK));
break;
case 3: /*Fast Forward*/
SetValue(slider, NewReal((hiValue - loValue) * FASTCLOCK));
break;
case 4: /*Fast Reverse*/
SetValue(slider, NewReal((loValue - hiValue) * FASTCLOCK));
break;
}
SetMethod(radioGroup, CHANGEDVALUE, method);
return ObjTrue;
}
ObjPtr ChangeClockFormat(textBox)
ObjPtr textBox;
/*Changes a clock's FORMAT according to textBox*/
{
ObjPtr repObj;
repObj = GetObjectVar("ChangeClockFormat", textBox, REPOBJ);
if (!repObj) return ObjFalse;
SetVar(repObj, FORMAT, GetValue(textBox));
ResolveController(repObj);
return ObjTrue;
}
ObjPtr ShowClockControls(clock, ownerWindow, windowName)
ObjPtr clock;
ObjPtr ownerWindow, windowName;
/*Makes a new clock window to control clock. Ignores ownerWindow and windowName*/
{
WinInfoPtr clockWindow;
ObjPtr name;
ObjPtr var;
ObjPtr panel;
ObjPtr contents;
ObjPtr button, checkBox;
int left;
ObjPtr format;
WinInfoPtr dialogExists;
ObjPtr whichDialog;
if (!clock) return NULLOBJ;
name = GetVar(clock, NAME);
whichDialog = NewString("Clock");
dialogExists = DialogExists((WinInfoPtr) clock, whichDialog);
if (name)
{
strncpy(tempStr, GetString(name), TEMPSTRSIZE);
tempStr[TEMPSTRSIZE] = 0;
}
else
{
strcpy(tempStr, "Clock");
}
clockWindow = GetDialog((WinInfoPtr) clock, whichDialog, tempStr,
CLWINWIDTH, CLWINHEIGHT, CLWINWIDTH, CLWINHEIGHT, WINDBUF + WINFIXEDSIZE);
if (!dialogExists)
{
SetVar((ObjPtr) clockWindow, REPOBJ, clock);
/*Put in a help string*/
SetVar((ObjPtr) clockWindow, HELPSTRING,
NewString("This window shows controls for a clock. Using these \
controls, you can change the time displayed in all the spaces a clock controls \
or set time to advance forward or backward at a certain rate."));
/*Add in a panel*/
panel = NewPanel(greyPanelClass, 0, CLWINWIDTH, 0, CLWINHEIGHT);
if (!panel)
{
return ObjFalse;
}
contents = GetVar((ObjPtr) clockWindow, CONTENTS);
PrefixList(contents, panel);
SetVar(panel, PARENT, (ObjPtr) clockWindow);
contents = GetListVar("ShowClockControls", panel, CONTENTS);
if (contents)
{
ObjPtr slider, button, checkBox, radioGroup, control;
ObjPtr timeBounds;
ObjPtr time;
ObjPtr textBox;
real tb[2], tbt[2];
real bigStep, littleStep, anchor;
real dt, mdt;
int left, right, top;
left = MAJORBORDER;
right = CLWINWIDTH - MAJORBORDER;
top = CLWINHEIGHT - MAJORBORDER;
time = GetVar(clock, TIME);
/*Add a time control*/
control = NewTimeControl(left, right, top - CLTCHEIGHT, top, "Time Control");
PrefixList(contents, control);
SetVar(control, PARENT, panel);
SetVar(control, REPOBJ, clock);
SetVar((ObjPtr) clockWindow, TIMECONTROL, control);
if (time)
{
SetValue(control, time);
}
SetMethod(control, CHANGEDVALUE, ChangeTime);
ReinitController(clock);
top -= CLTCHEIGHT + MAJORBORDER;
timeBounds = GetVar(clock, TIMEBOUNDS);
if (timeBounds && IsArray(timeBounds) && RANK(timeBounds) == 1 &&
DIMS(timeBounds)[0] == 2)
{
Array2CArray(tb, timeBounds);
}
else
{
tb[0] = 0.0;
tb[1] = 1.0;
}
tbt[0] = tb[0]; tbt[1] = tb[1];
ChooseGoodSliderParams(&(tbt[0]), &(tbt[1]), &bigStep, &littleStep, &anchor);
/*Create the format text box*/
textBox = NewTextBox(left, left + 70,
top - EDITBOXHEIGHT + (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2,
top - (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2,
PLAIN, "Format:", "Format:");
PrefixList(contents, textBox);
SetVar(textBox, PARENT, panel);
/*Create the format editable text box*/
format = GetVar(clock, FORMAT);
textBox = NewTextBox(left + 70, right - 70,
top - EDITBOXHEIGHT,
top,
DEFERCHANGE + EDITABLE + WITH_PIT + ONE_LINE, "Format",
format ? GetString(format) : "%#.2t");
PrefixList(contents, textBox);
SetVar(textBox, PARENT, panel);
SetVar(textBox, REPOBJ, clock);
SetMethod(textBox, CHANGEDVALUE, ChangeClockFormat);
top -= EDITBOXHEIGHT + MAJORBORDER;
/*Create the time units per second slider*/
slider = NewSlider(left, right,
top - MAJORBORDER - SLIDERWIDTH,
top - MAJORBORDER,
SCALE, "Delta Time per second");
PrefixList(contents, slider);
SetVar(slider, HELPSTRING,
NewString("This slider controls the rate at which time flows \
in the spaces controlled by the clock. The number on the slider specifies how \
many time units in the space will go by for every second of real time. This rate is \
approximated as well as can be considering the time it takes to draw windows. \
It compensates correctly when recording frame-by-frame. This slider is connected \
to the tape recorder style buttons below it."));
SetVar(slider, PARENT, panel);
dt = (tb[1] - tb[0]) * 0.2;
mdt = -dt;
ChooseGoodSliderParams(&mdt, &dt, &bigStep, &littleStep, &anchor);
SetSliderRange(slider, dt, mdt, 0.0);
SetSliderValue(slider, 0.0);
SetSliderScale(slider, bigStep, littleStep, 0.0, "%g");
SetVar(slider, REPOBJ, clock);
SetMethod(slider, CHANGEDVALUE, ChangeDeltaTime);
/*Create the text box*/
textBox = NewTextBox(left, right,
top - MAJORBORDER - SLIDERWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
top - MAJORBORDER - SLIDERWIDTH - TEXTBOXSEP,
PLAIN, "Delta time text", "Delta time per second");
SetVar(textBox, PARENT, panel);
PrefixList(contents, textBox);
SetTextAlign(textBox, CENTERALIGN);
top -= 2 * MAJORBORDER + SLIDERWIDTH + TEXTBOXSEP + TEXTBOXHEIGHT;
/*Create the cycle time radio group*/
radioGroup = NewRadioButtonGroup("Cycle the clock");
SetVar(radioGroup, PARENT, panel);
SetVar(radioGroup, REPOBJ, clock);
SetMethod(radioGroup, CHANGEDVALUE, ChangeCycleClock);
SetVar(radioGroup, HELPSTRING,
NewString("These radio buttons control whether the clock, when it is running forward \
or backward, stops at the endpoints in time or cycles around to the other end.\n"));
PrefixList(contents, radioGroup);
/*And buttons*/
button = NewRadioButton(left, (right + left) / 2,
top - CHECKBOXHEIGHT,
top,
"Stop at endpoints");
SetVar(button, HELPSTRING,
NewString("Click on this button to have the clock automatically stop when it reaches an endpoint."));
AddRadioButton(radioGroup, button);
button = NewRadioButton((right + left) / 2, right,
top - CHECKBOXHEIGHT,
top,
"Cycle at endpoints");
SetVar(button, HELPSTRING,
NewString("Click on this button to have the clock automatically cycle around it reaches an endpoint."));
AddRadioButton(radioGroup, button);
SetValue(radioGroup, NewInt(GetPredicate(clock, CYCLECLOCK)));
top -= MAJORBORDER + CHECKBOXHEIGHT;
/*Create the icon buttons*/
radioGroup = NewRadioButtonGroup("Speed Control");
SetVar(radioGroup, PARENT, panel);
SetVar(radioGroup, REPOBJ, slider);
SetVar(radioGroup, HELPSTRING,
NewString("These radio buttons control the speed of the clock. \
They are tied to the Delta Time Per Second slider and provide easy access to some convenient values. \
The most useful is Stop, the button in the center with the square icon."));
PrefixList(contents, radioGroup);
/*Stop*/
button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2,
(left + right) / 2 + ICONBUTTONSIZE / 2,
top - ICONBUTTONSIZE, top,
ICONSTOP, UIGREEN, "Stop");
SetVar(button, REPOBJ, slider);
SetVar(button, HELPSTRING, NewString("This button stops the clock."));
AddRadioButton(radioGroup, button);
/*Forward*/
button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 + ICONBUTTONSIZE + MINORBORDER,
(left + right) / 2 + ICONBUTTONSIZE / 2 + ICONBUTTONSIZE + MINORBORDER,
top - ICONBUTTONSIZE, top,
ICONPLAY, UIGREEN, "Play");
SetVar(button, REPOBJ, slider);
SetVar(button, HELPSTRING, NewString("This button sets time moving forward at a moderate pace."));
AddRadioButton(radioGroup, button);
/*Reverse*/
button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 - ICONBUTTONSIZE - MINORBORDER,
(left + right) / 2 + ICONBUTTONSIZE / 2 - ICONBUTTONSIZE - MINORBORDER,
top - ICONBUTTONSIZE, top,
ICONREV, UIGREEN, "Reverse");
SetVar(button, REPOBJ, slider);
SetVar(button, HELPSTRING, NewString("This button sets time moving backward at a moderate pace."));
AddRadioButton(radioGroup, button);
/*Fast Forward*/
button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 + 2 * (ICONBUTTONSIZE + MINORBORDER),
(left + right) / 2 + ICONBUTTONSIZE / 2 + 2 * (ICONBUTTONSIZE + MINORBORDER),
top - ICONBUTTONSIZE, top,
ICONFF, UIGREEN, "Fast Forward");
SetVar(button, REPOBJ, slider);
SetVar(button, HELPSTRING, NewString("This button sets time moving forward at a quick pace."));
AddRadioButton(radioGroup, button);
/*Reverse*/
button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 - 2 * (ICONBUTTONSIZE + MINORBORDER),
(left + right) / 2 + ICONBUTTONSIZE / 2 - 2 * (ICONBUTTONSIZE + MINORBORDER),
top - ICONBUTTONSIZE, top,
ICONFR, UIGREEN, "Fast Reverse");
SetVar(button, REPOBJ, slider);
SetVar(button, HELPSTRING, NewString("This button sets time moving backward at a quick pace."));
AddRadioButton(radioGroup, button);
/*Link dtime slider to this speed control*/
SetVar(slider, SPEEDCONTROL, radioGroup);
/*Set the radio group*/
ClockSliderToRadio(radioGroup, slider);
SetMethod(radioGroup, CHANGEDVALUE, ChangeClockSpeed);
}
}
return (ObjPtr) clockWindow;
}
static ObjPtr ChangePerspective(object)
ObjPtr object;
/*Change value for a perspective control*/
{
ObjPtr val;
ObjPtr repObj;
real oldPerspec[4];
real newPerspec[4];
repObj = GetObjectVar("ChangePerspective", object, REPOBJ);
if (!repObj)
{
return ObjFalse;
}
val = GetFixedArrayVar("ChangePerspective", repObj, PERSPECSTUFF, 1, 4L);
if (!val)
{
return ObjFalse;
}
Array2CArray(oldPerspec, val);
val = GetFixedArrayVar("ChangePerspective", object, VALUE, 1, 4L);
if (!val)
{
return ObjFalse;
}
Array2CArray(newPerspec, val);
val = NewRealArray(1, 4L);
CArray2Array(val, newPerspec);
SetVar(repObj, PERSPECSTUFF, val);
/*See if field distance has changed*/
if (oldPerspec[0] != newPerspec[0])
{
/*If so, change LOCATION accordingly*/
real posn[3];
ObjPtr posnArray;
real yaw, pitch;
val = GetRealVar("ChangePerspective", repObj, YAW);
yaw = GetReal(val);
val = GetRealVar("ChangePerspective", repObj, PITCH);
pitch = GetReal(val);
posnArray = GetFixedArrayVar("ChangePerspective", repObj, FOCUSPOINT, 1, 3L);
Array2CArray(posn, posnArray);
posn[0] -= rsin(yaw) * newPerspec[0];
posn[1] -= rsin(pitch) * newPerspec[0];
posn[2] -= rcos(yaw) * newPerspec[0];
posnArray = NewRealArray(1, 3L);
CArray2Array(posnArray, posn);
SetVar(repObj, LOCATION, posnArray);
}
ResolveController(repObj);
DrawMe(object);
return ObjTrue;
}
ObjPtr ChangeViewType(radio)
ObjPtr radio;
/*Changes the view type according to radio*/
{
ObjPtr controller, control;
ObjPtr var;
int oldValue, newValue;
controller = GetObjectVar("ChangeViewType", radio, REPOBJ);
if (!controller)
{
return ObjFalse;
}
var = GetIntVar("ChangeViewType", controller, VIEWTYPE);
if (!var)
{
return ObjFalse;
}
oldValue = GetInt(var);
var = GetValue(radio);
if (!var)
{
return ObjFalse;
}
newValue = GetInt(var);
if (newValue == oldValue)
{
return ObjTrue;
}
control = GetObjectVar("ChangeViewType", radio, PERSPECCONTROL);
if (!control)
{
return ObjFalse;
}
if (newValue == VT_ORTHOGRAPHIC)
{
MakePerspecOrtho(control, true);
ChangedValue(control);
}
else if (oldValue == VT_ORTHOGRAPHIC)
{
MakePerspecOrtho(control, false);
ChangedValue(control);
}
SetVar(controller, VIEWTYPE, NewInt(newValue));
ResolveController(controller);
}
ObjPtr ChangeAirspeed(slider)
ObjPtr slider;
/*Changes the airspeed of an observer according to a slider*/
{
ObjPtr controller;
ObjPtr value;
controller = GetObjectVar("ChangeAirspeed", slider, REPOBJ);
if (!controller)
{
return ObjFalse;
}
value = GetRealVar("ChangeAirspeed", slider, VALUE);
if (!value)
{
return ObjFalse;
}
SetVar(controller, AIRSPEED, value);
ResolveController(controller);
IdleTimers();
return ObjTrue;
}
ObjPtr ChangeRollDpitch(control)
ObjPtr control;
/*Changes roll and dpitch based on an XY control*/
{
ObjPtr controller;
ObjPtr value;
real roll, dpitch;
controller = GetObjectVar("ChangeRollDpitch", control, REPOBJ);
if (!controller)
{
return ObjFalse;
}
GetXYControlValue(control, &roll, &dpitch);
SetVar(controller, ROLL, NewReal(roll));
SetVar(controller, DPITCH, NewReal(-dpitch));
ResolveController(controller);
IdleTimers();
return ObjTrue;
}
ObjPtr ChangeFlying(radio)
ObjPtr radio;
/*Changes whether an observer is flying based on radio*/
{
ObjPtr repObj, var;
Bool flying;
repObj = GetObjectVar("ChangeFlying", radio, REPOBJ);
if (!repObj)
{
return ObjFalse;
}
var = GetValue(radio);
if (!var)
{
return ObjFalse;
}
flying = GetInt(var) ? true : false;
if (flying)
{
WakeMe(repObj, MARKTIME, Clock() + 0.001);
}
SetVar(repObj, FLYING, flying ? ObjTrue : ObjFalse);
ResolveController(repObj);
return ObjTrue;
}
ObjPtr ResetPosition(button)
ObjPtr button;
/*Resets an observer's position*/
{
ObjPtr observer, var;
real perspecStuff[4];
real eyePosn[3];
Matrix justRot;
int i;
observer = GetObjectVar("ResetPosition", button, REPOBJ);
if (!observer)
{
return ObjFalse;
}
perspecStuff[0] = INITEYEDIST;
var = GetFixedArrayVar("StartSpace", observer, PERSPECSTUFF, 1, 4L);
if (var)
{
Array2CArray(perspecStuff, var);
}
/*Change the eye position*/
eyePosn[0] = 0.0;
eyePosn[1] = 0.0;
eyePosn[2] = perspecStuff[0];
var = NewRealArray(1, 3L);
CArray2Array(var, eyePosn);
SetVar(observer, LOCATION, var);
/*Fix roll, pitch, yaw*/
SetVar(observer, ROLL, NewReal(0.0));
SetVar(observer, PITCH, NewReal(0.0));
SetVar(observer, YAW, NewReal(M_PI));
ChangeFocus(observer);
/*Change the transformation matrix*/
var = GetMatrixVar("ResetPosition", observer, XFORM);
if (!var)
{
return NULLOBJ;
}
MATCOPY(justRot, MATRIXOF(var));
for (i = 0; i < 3; ++i)
{
justRot[3][i] = justRot[i][3] = 0.0;
}
var = NewMatrix();
MATCOPY(MATRIXOF(var), justRot);
SetVar(observer, XFORM, var);
ResolveController(observer);
return ObjTrue;
}
ObjPtr ResetRotation(button)
ObjPtr button;
/*Resets an observer's rotation*/
{
ObjPtr observer, var;
Matrix rot;
int i, j;
observer = GetObjectVar("ResetPosition", button, REPOBJ);
if (!observer)
{
return ObjFalse;
}
/*Change the transformation matrix*/
var = GetMatrixVar("ResetPosition", observer, XFORM);
if (!var)
{
return NULLOBJ;
}
MATCOPY(rot, MATRIXOF(var));
for (i = 0; i < 3; ++i)
{
for (j = 0; j < 3; ++j)
{
rot[i][j] = i == j ? 1.0 : 0.0;
}
}
var = NewMatrix();
MATCOPY(MATRIXOF(var), rot);
SetVar(observer, XFORM, var);
ResolveController(observer);
return ObjTrue;
}
ObjPtr ShowObserverControls(observer, ownerWindow, windowName)
ObjPtr observer;
ObjPtr ownerWindow, windowName;
/*Makes a new observer window to control observer. Ignores ownerWindow and windowName*/
{
WinInfoPtr observerWindow;
ObjPtr name;
ObjPtr var;
ObjPtr panel;
ObjPtr contents;
int left;
WinInfoPtr dialogExists;
ObjPtr whichDialog;
if (!observer) return NULLOBJ;
name = GetVar(observer, NAME);
whichDialog = NewString("Observer");
dialogExists = DialogExists((WinInfoPtr) observer, whichDialog);
if (name)
{
strncpy(tempStr, GetString(name), TEMPSTRSIZE);
tempStr[TEMPSTRSIZE] = 0;
}
else
{
strcpy(tempStr, "Observer");
}
observerWindow = GetDialog((WinInfoPtr) observer, whichDialog, tempStr,
OBWINWIDTH, OBWINHEIGHT, OBWINWIDTH, OBWINHEIGHT, WINDBUF);
if (!dialogExists)
{
SetVar((ObjPtr) observerWindow, REPOBJ, observer);
/*Add in a help string*/
SetVar((ObjPtr) observerWindow, HELPSTRING,
NewString("This window shows controls for an observer. The observer \
represents you, looking into the space containing visualization objects. The controls \
allow you to change your viewing angle, focus point, and clipping planes \
and also let you fly through the visualization with a simple flight simulator."));
/*Add in a panel*/
panel = NewPanel(greyPanelClass, 0, OBWINWIDTH, 0, OBWINHEIGHT);
if (!panel)
{
return ObjFalse;
}
contents = GetVar((ObjPtr) observerWindow, CONTENTS);
PrefixList(contents, panel);
SetVar(panel, PARENT, (ObjPtr) observerWindow);
contents = GetListVar("ShowObserverControls", panel, CONTENTS);
if (contents)
{
ObjPtr control;
ObjPtr textBox;
ObjPtr slider, var;
ObjPtr radio, button;
ObjPtr tempObj;
ObjPtr titleBox, checkBox;
real roll, dPitch, airspeed;
int left, right, bottom, top;
int viewType;
/*Bottom control group*/
left = MAJORBORDER;
right = OBWINWIDTH - MAJORBORDER;
bottom = MAJORBORDER;
top = MAJORBORDER + OBBOTTOMHEIGHT;
/*View type chooser*/
titleBox = NewTitleBox(left, right, bottom, top, "View Type");
SetVar(titleBox, PARENT, panel);
PrefixList(contents, titleBox);
radio = NewRadioButtonGroup("View Type Radio");
PrefixList(contents, radio);
SetVar(radio, PARENT, panel);
SetVar(radio, REPOBJ, observer);
SetVar(radio, HELPSTRING, NewString("This radio group controls \
the type of view given in all spaces controlled by this observer. For more information \
about a given view type, use Help In Context on the button naming the view type.\n"));
button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT,
top - TITLEBOXTOP - MINORBORDER, "Perspective");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING, NewString("When this button is down, the standard \
perspective view is used. Objects closer to the observer appear larger, giving a \
realistic image.\n"));
button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
bottom + MINORBORDER,
bottom + MINORBORDER + CHECKBOXHEIGHT, "Orthographic");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING, NewString("When this button is down, the \
orthograpic view is enabled. Objects appear the same size no matter what the \
distance to the observer. This view does not appear as realistic as the perspective \
view, but it does have the advantage that objects at different depths line up. \
It is useful for viewing 2-D data or for comparing values in 3-D data at different \
depths.\n"));
button = NewRadioButton((left + right) / 2 - MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT,
top - TITLEBOXTOP - MINORBORDER, "Crosseyed Stereo");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING, NewString("When this button is down, two \
perspective images are shown side by side for a stereo pair. To view the stereo \
pair, cross your eyes until the images coincide. For some people, this is very \
easy to do. Some people have difficulty doing it. Only try this if you personally \
find it easy, and if it becomes a strain, stop at once."));
button = NewRadioButton((left + right) / 2 - MINORBORDER, right - MINORBORDER,
bottom + MINORBORDER,
bottom + MINORBORDER + CHECKBOXHEIGHT, "Anaglyphic Stereo");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING, NewString("When this button is down, two \
perspective images are shown, one red and one cyan, for a stereo pair. To view the stereo \
pair, you will need to use a pair of red/cyan 3-D glasses. Because one eye \
can see only one set of colors, subtle color variations will be washed out. \
Also, colors which are heavily toward red or heavily toward blue will be confusing."));
var = GetVar(observer, VIEWTYPE);
if (var)
{
SetValue(radio, var);
viewType = GetInt(var);
}
else
{
SetValue(radio, NewInt(viewType = VT_PERSPECTIVE));
}
SetMethod(radio, CHANGEDVALUE, ChangeViewType);
/*Middle control group*/
left = MAJORBORDER;
right = OBWINWIDTH - MAJORBORDER;
bottom = MAJORBORDER + MINORBORDER + OBBOTTOMHEIGHT;
top = bottom + OBMIDHEIGHT;
/*Add in the perspective control*/
control = NewPerspecControl(
right - PCWIDTH,
right,
bottom + TEXTBOXHEIGHT + TEXTBOXSEP,
top,
"Perspective Control");
PrefixList(contents, control);
SetVar(control, PARENT, panel);
SetVar(control, REPOBJ, observer);
if (viewType == VT_ORTHOGRAPHIC)
{
MakePerspecOrtho(control, true);
}
SetValue(control, GetVar(observer, PERSPECSTUFF));
SetMethod(control, CHANGEDVALUE, ChangePerspective);
/*Link it to the radio*/
SetVar(radio, PERSPECCONTROL, control);
/*Add in the perspective text box*/
textBox = NewTextBox(
right - PCWIDTH - MINORBORDER,
right + MINORBORDER,
bottom, bottom + TEXTBOXHEIGHT,
PLAIN,
"Perspective Text",
"Perspective");
PrefixList(contents, textBox);
SetVar(textBox, PARENT, panel);
SetTextAlign(textBox, CENTERALIGN);
/*Get observer's roll and dPitch*/
tempObj = GetVar(observer, ROLL);
if (tempObj && IsReal(tempObj))
{
roll = GetReal(tempObj);
}
else
{
roll = 0.0;
}
tempObj = GetVar(observer, DPITCH);
if (tempObj && IsReal(tempObj))
{
dPitch = GetReal(tempObj);
}
else
{
dPitch = 0.0;
}
/*Put in the flight control*/
control = NewXYControl(
left + SCALESLOP + SLIDERWIDTH + MINORBORDER,
right - PCWIDTH - MINORBORDER,
bottom + TEXTBOXHEIGHT + TEXTBOXSEP,
top,
"Flight Control");
PrefixList(contents, control);
SetVar(control, PARENT, panel);
SetVar(control, REPOBJ, observer);
SetVar(control, ALWAYSCHANGE, ObjTrue);
SetXYControlLimits(control, -MAXROLL, MAXROLL, -MAXDPITCH, MAXDPITCH);
SetXYControlValue(control, roll, dPitch);
SetMethod(control, CHANGEDVALUE, ChangeRollDpitch);
SetVar(control, HELPSTRING, NewString("This control is like the control stick of an airplane. \
Move the indicator down to pull back on the stick and climb. Move the indicator down to push \
forward on the stick and dive. Move the indicator from side to side to bank and turn. \
This will only work properly when you have set your airspeed. Here's a hint: It's easier to fly \
if the perspective control is set to a wide angle view."));
/*Add in the flight control text box*/
textBox = NewTextBox(
left + SCALESLOP + SLIDERWIDTH,
right - PCWIDTH - MINORBORDER,
bottom, bottom + TEXTBOXHEIGHT,
PLAIN,
"Control Text",
"Flight Control");
PrefixList(contents, textBox);
SetVar(textBox, PARENT, panel);
SetTextAlign(textBox, CENTERALIGN);
/*Get the observer's airspeed*/
tempObj = GetVar(observer, AIRSPEED);
if (tempObj && IsReal(tempObj))
{
airspeed = GetReal(tempObj);
}
else
{
airspeed = 0.0;
}
/*Add in the airspeed slider*/
slider = NewSlider(
left + SCALESLOP, left + SCALESLOP + SLIDERWIDTH,
bottom + TEXTBOXHEIGHT + TEXTBOXSEP,
top,
SCALE,
"Airspeed");
if (!slider)
{
return ObjFalse;
}
PrefixList(contents, slider);
SetVar(slider, HELPSTRING,
NewString("This slider controls the airspeed of the flight \
simulator. Use the square flight control to navigate while flying."));
SetVar(slider, PARENT, panel);
SetVar(slider, REPOBJ, observer);
SetSliderRange(slider, 0.2, -0.2, 0.0);
SetSliderScale(slider, 0.1, 0.02, 0.0, "%g");
SetSliderValue(slider, airspeed);
SetMethod(slider, CHANGEDVALUE, ChangeAirspeed);
/*Add in the airspeed text box*/
textBox = NewTextBox(
left - MINORBORDER, left + SCALESLOP + SLIDERWIDTH + MINORBORDER,
bottom, bottom + TEXTBOXHEIGHT,
PLAIN,
"Airspeed Text",
"Airspeed");
PrefixList(contents, textBox);
SetVar(textBox, PARENT, panel);
SetTextAlign(textBox, CENTERALIGN);
/*Add the top controls*/
bottom = top + MINORBORDER;
top = bottom + TITLEBOXTOP + 2 * MINORBORDER + CHECKBOXSPACING + 2 * CHECKBOXHEIGHT;
left = MAJORBORDER;
right = (OBWINWIDTH - MINORBORDER) / 2;
titleBox = NewTitleBox(left, right, bottom, top, "Flight Simulator");
PrefixList(contents, titleBox);
SetVar(titleBox, PARENT, panel);
radio = NewRadioButtonGroup("Flight On/Off");
PrefixList(contents, radio);
SetVar(radio, PARENT, panel);
SetVar(radio, HELPSTRING, NewString("This radio button group controls \
whether the flight simulator is on. If the Anchored button is selected, the \
flight simulator is off. If the Flying button is selected, the flight simulator \
is on, and the Airspeed slider and the Flight Control are used to fly around.\n"));
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT,
top - TITLEBOXTOP - MINORBORDER, "Anchored");
AddRadioButton(radio, button);
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT - CHECKBOXSPACING,
"Flying");
AddRadioButton(radio, button);
SetValue(radio, NewInt(GetPredicate(observer, FLYING) ? 1 : 0));
SetMethod(radio, CHANGEDVALUE, ChangeFlying);
SetVar(radio, REPOBJ, observer);
/*Add in the reset buttons*/
left = right + MINORBORDER;
right = OBWINWIDTH - MAJORBORDER;
button = NewButton(left, right,
bottom + BUTTONHEIGHT + MINORBORDER,
bottom + 2 * BUTTONHEIGHT + MINORBORDER,
"Reset Position");
PrefixList(contents, button);
SetVar(button, PARENT, panel);
SetVar(button, REPOBJ, observer);
SetMethod(button, CHANGEDVALUE, ResetPosition);
SetVar(radio, HELPSTRING, NewString("This button resets the position \
of the observer to the default. It is useful if you get lost.\n"));
button = NewButton(left, right,
bottom, bottom + BUTTONHEIGHT,
"Reset Rotation");
PrefixList(contents, button);
SetVar(button, PARENT, panel);
SetVar(button, REPOBJ, observer);
SetMethod(button, CHANGEDVALUE, ResetRotation);
SetVar(radio, HELPSTRING, NewString("This button resets the rotation \
of the observer to the default. It is useful if you get lost.\n"));
}
}
return (ObjPtr) observerWindow;
}
ObjPtr ChangeRenderType(control)
ObjPtr control;
/*Changes the render type based on a radio button*/
{
ObjPtr controller;
ObjPtr value;
real roll, dpitch;
controller = GetObjectVar("ChangeRenderType", control, REPOBJ);
if (!controller)
{
return ObjFalse;
}
SetVar(controller, RENDERTYPE, GetValue(control));
ResolveController(controller);
return ObjTrue;
}
void RendererRGB(visWindow)
WinInfoPtr visWindow;
/*Sends a deferred message to visWindow if it has objectForRGB*/
{
ObjPtr space;
space = FindSpace(visWindow);
if (space)
{
if (objectForRGB == GetVar(space, RENDERER))
{
if (0 == (visWindow -> flags & WINRGB))
{
DeferMessage((ObjPtr) visWindow, SETRGBMESSAGE);
}
}
}
}
ObjPtr ChangeFilterType(control)
ObjPtr control;
/*Changes the filter type based on a radio button*/
{
ObjPtr controller;
ObjPtr value;
controller = GetObjectVar("ChangeFilterType", control, REPOBJ);
if (!controller)
{
return ObjFalse;
}
value = GetValue(control);
SetVar(controller, FILTERTYPE, GetValue(control));
if (GetInt(value) != FT_NONE)
{
ObjPtr renderType;
renderType = GetVar(controller, RENDERTYPE);
if (renderType && GetInt(renderType) == 1 && hasRGB)
{
objectForRGB = controller;
ForAllVisWindows(RendererRGB);
}
}
ResolveController(controller);
return ObjTrue;
}
ObjPtr ShowRendererControls(renderer, ownerWindow, windowName)
ObjPtr renderer;
ObjPtr ownerWindow, windowName;
/*Makes a new renderer window to control renderer.*/
{
WinInfoPtr rendererWindow;
ObjPtr name;
ObjPtr var;
ObjPtr panel;
ObjPtr contents;
WinInfoPtr dialogExists;
ObjPtr whichDialog;
if (!renderer) return NULLOBJ;
name = GetVar(renderer, NAME);
whichDialog = NewString("Renderer");
dialogExists = DialogExists((WinInfoPtr) renderer, whichDialog);
if (name)
{
strncpy(tempStr, GetString(name), TEMPSTRSIZE);
tempStr[TEMPSTRSIZE] = 0;
}
else
{
strcpy(tempStr, "Renderer");
}
rendererWindow = GetDialog((WinInfoPtr) renderer, whichDialog, tempStr,
RWINWIDTH, RWINHEIGHT, RWINWIDTH, RWINHEIGHT, WINDBUF);
if (!dialogExists)
{
SetVar((ObjPtr) rendererWindow, REPOBJ, renderer);
SetVar((ObjPtr) rendererWindow, OWNERWINDOW, (ObjPtr) ownerWindow);
/*Add controls*/
SetVar((ObjPtr) rendererWindow, HELPSTRING,
NewString("This window shows controls that affect the rendering of \
the objects within the space."));
/*Add in a panel*/
panel = NewPanel(greyPanelClass, 0, RWINWIDTH, 0, RWINHEIGHT);
if (!panel)
{
return ObjFalse;
}
contents = GetVar((ObjPtr) rendererWindow, CONTENTS);
PrefixList(contents, panel);
SetVar(panel, PARENT, (ObjPtr) rendererWindow);
contents = GetListVar("ShowRendererControls", panel, CONTENTS);
if (contents)
{
ObjPtr titleBox;
ObjPtr button;
ObjPtr radio;
ObjPtr var;
int left, right, top;
left = MAJORBORDER;
right = RWINWIDTH - MAJORBORDER;
top = RWINHEIGHT - MAJORBORDER;
/*Make the title box around render type*/
titleBox = NewTitleBox(left, right,
top - TITLEBOXTOP - 2 * MINORBORDER - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING, top,
"Renderer Type");
PrefixList(contents, titleBox);
SetVar(titleBox, PARENT, panel);
/*Make the no renderer button*/
radio = NewRadioButtonGroup("Renderer");
SetVar(radio, HELPSTRING, NewString("These radio buttons control what kind of renderer is \
used to render the objects within the space. At present, there is only one renderer available: a hardware renderer."));
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT,
top - TITLEBOXTOP - MINORBORDER,
"None");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING,
NewString("This button causes rendering not to be done on the space. \
This is sometimes useful as to hide all the visualization objects."));
/*Make the hardware renderer button*/
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT - CHECKBOXSPACING,
"Hardware");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING,
NewString("This button sets the space to use the hardware renderer. \
At present, this is the only renderer available."));
PrefixList(contents, radio);
SetVar(radio, PARENT, panel);
SetVar(radio, REPOBJ, renderer);
var = GetVar(renderer, RENDERTYPE);
if (var)
{
SetValue(radio, var);
}
else
{
SetValue(radio, NewInt(RT_HARDWARE));
}
SetMethod(radio, CHANGEDVALUE, ChangeRenderType);
top -= TITLEBOXTOP + 2 * MINORBORDER + 2 * CHECKBOXHEIGHT + CHECKBOXSPACING + MAJORBORDER;
/*Make the title box around filter type*/
titleBox = NewTitleBox(left, right,
top - TITLEBOXTOP - 2 * MINORBORDER - 3 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, top,
"Image Filter");
PrefixList(contents, titleBox);
SetVar(titleBox, PARENT, panel);
/*Make the no filter button*/
radio = NewRadioButtonGroup("Filter Type");
SetVar(radio, HELPSTRING,
NewString("These radio buttons select the kind of filtration that is done \
to the image of the space after it has been rendererd. Filtration only works when the \
space is set to full color mode."));
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT,
top - TITLEBOXTOP - MINORBORDER,
"None");
AddRadioButton(radio, button);
SetVar(button, HELPSTRING,
NewString("This button causes the image of the space to be shown \
without filtration. This is the fastest and is recommended for interactive work."));
/*Make the shrink filter button*/
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
top - TITLEBOXTOP - MINORBORDER - CHECKBOXHEIGHT - CHECKBOXSPACING,
"2 to 1 shrink");
SetVar(button, HELPSTRING,
NewString("This button causes the image of the space to be shrunk \
2 to 1 and averaged before being displayed. This only works well when the window is \
in full color mode and the entire window is shown on the screen and is not covered by \
any other window. It produces pretty good results with video. Use the Double Video Screen \
item in the Window menu."));
AddRadioButton(radio, button);
/*Make the shrink filter button*/
button = NewRadioButton(left + MINORBORDER, right - MINORBORDER,
top - TITLEBOXTOP - MINORBORDER - 3 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING,
top - TITLEBOXTOP - MINORBORDER - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING,
"Four neighbor average");
SetVar(button, HELPSTRING,
NewString("This button causes each pixel of image of the space to be \
averaged with its four neighbors in the horizontal and vertical directions before being displayed. \
This only works well when the window is in full color mode and is not covered by \
other windows. It produces pretty good results with video, \
though not as good as the shrinking filter."));
AddRadioButton(radio, button);
PrefixList(contents, radio);
SetVar(radio, PARENT, panel);
SetVar(radio, REPOBJ, renderer);
var = GetVar(renderer, FILTERTYPE);
if (var)
{
SetValue(radio, var);
}
else
{
SetValue(radio, NewInt(FT_NONE));
}
SetMethod(radio, CHANGEDVALUE, ChangeFilterType);
}
}
return (ObjPtr) rendererWindow;
}
ObjPtr MakeClockTimeFormat(clock)
ObjPtr clock;
/*Makes a clock's TIMEFORMAT variable*/
{
ObjPtr datasets;
int curFormat;
ObjPtr format;
ObjPtr *elements;
int k;
MakeVar(clock, DATASETS);
curFormat = 0;
datasets = GetVar(clock, DATASETS);
if (datasets)
{
elements = ELEMENTS(datasets);
for (k = 0; k < DIMS(datasets)[0]; ++k)
{
format = GetVar(elements[k], TIMEFORMAT);
if (format)
{
int newFormat;
newFormat = GetInt(format);
curFormat |= newFormat;
}
}
}
if (!curFormat)
{
curFormat = TF_SECONDS + TF_SUBSECONDS;
}
SetVar(clock, TIMEFORMAT, NewInt(curFormat));
return ObjTrue;
}
ObjPtr MakeClockDatasets(clock)
ObjPtr clock;
/*Makes a clock's DATASETS variable, along with time slices*/
{
ObjPtr list, spaces, array;
ObjPtr *elements;
long k;
WinInfoPtr dialog;
list = NewList();
spaces = GetListVar("MakeClockDatasets", clock, SPACES);
if (spaces)
{
ThingListPtr spaceRunner;
spaceRunner = LISTOF(spaces);
while (spaceRunner)
{
ObjPtr contents;
contents = GetListVar("MakeClockDatasets", spaceRunner -> thing, CONTENTS);
if (contents)
{
ThingListPtr contentsRunner;
contentsRunner = LISTOF(contents);
while (contentsRunner)
{
PrefixDatasets(list, contentsRunner -> thing);
contentsRunner = contentsRunner -> next;
}
}
spaceRunner = spaceRunner -> next;
}
}
if (LISTOF(list))
{
/*There's something in the list*/
Bool hasMinValue = false;
real minValue;
/*Convert to an array*/
array = ListToArray(list);
/*Remove duplicate elements*/
array = Uniq(array);
/*Sort the array*/
array = SortArrayByStringVar(array, NAME);
elements = ELEMENTS(array);
for (k = 0; k < DIMS(array)[0]; ++k)
{
ObjPtr name, timeSteps;
MakeVar(elements[k], NAME);
MakeVar(elements[k], TIMESTEPS);
name = GetVar(elements[k], NAME);
timeSteps = GetVar(elements[k], TIMESTEPS);
if (timeSteps)
{
if (hasMinValue)
{
minValue = MIN(minValue, *((real *)ELEMENTS(timeSteps)));
}
else
{
minValue = *((real *)ELEMENTS(timeSteps));
hasMinValue = true;
}
}
}
if (hasMinValue)
{
if (!GetVar(clock, TIME))
{
SetVar(clock, TIME, NewReal(minValue));
ResolveController(clock);
}
}
else
{
SetVar(clock, TIME, NULLOBJ);
}
}
else
{
array = NULLOBJ;
SetVar(clock, TIME, NULLOBJ);
}
/*Set the clock's datasets to datasets*/
SetVar(clock, DATASETS, array);
/*Now inval the control*/
dialog = DialogExists((WinInfoPtr) clock, NewString("Clock"));
if (dialog)
{
ObjPtr control;
control = GetVar((ObjPtr) dialog, TIMECONTROL);
if (control)
{
ImInvalid(control);
MakeVar(control, DATASETS);
}
}
return ObjTrue;
}
ObjPtr commonClock = NULLOBJ;
ObjPtr commonObserver = NULLOBJ;
ObjPtr commonRenderer = NULLOBJ;
int clockNum = 0;
int observerNum = 0;
int rendererNum = 0;
ObjPtr NewClock()
/*Returns a new clock*/
{
ObjPtr retVal;
ObjPtr name;
if (oneClock)
{
if (commonClock) return commonClock;
}
retVal = NewObject(clockClass, 0);
if (oneClock)
{
commonClock = retVal;
}
name = GetVar(clockClass, NAME);
if (name)
{
sprintf(tempStr, "%s %d", GetString(name), ++clockNum);
}
else
{
sprintf(tempStr, "Clock %d", ++clockNum);
}
name = NewString(tempStr);
SetVar(retVal, NAME, name);
SetVar(retVal, SPACES, NewList());
SetVar(retVal, CYCLECLOCK, ObjTrue);
return retVal;
}
ObjPtr NewObserver()
/*Returns a new observer*/
{
ObjPtr retVal;
ObjPtr name;
real perspecStuff[4];
ObjPtr psArray, anArray;
real posn[3];
if (oneObserver)
{
if (commonObserver) return commonObserver;
}
retVal = NewObject(observerClass, 0);
if (oneObserver)
{
commonObserver = retVal;
}
name = GetVar(observerClass, NAME);
if (name)
{
sprintf(tempStr, "%s %d", GetString(name), ++observerNum);
}
else
{
sprintf(tempStr, "Observer %d", ++observerNum);
}
name = NewString(tempStr);
SetVar(retVal, NAME, name);
SetVar(retVal, SPACES, NewList());
SetVar(retVal, XFORM, NewMatrix());
perspecStuff[0] = INITEYEDIST;
perspecStuff[1] = INITAOV;
perspecStuff[2] = INITNEARCLIP;
perspecStuff[3] = INITFARCLIP;
psArray = NewRealArray(1, 4);
CArray2Array(psArray, perspecStuff);
SetVar(retVal, PERSPECSTUFF, psArray);
posn[0] = 0.0;
posn[1] = 0.0;
posn[2] = INITEYEDIST;
anArray = NewRealArray(1, 3L);
CArray2Array(anArray, posn);
SetVar(retVal, LOCATION, anArray);
posn[2] = 0.0;
anArray = NewRealArray(1, 3L);
CArray2Array(anArray, posn);
SetVar(retVal, FOCUSPOINT, anArray);
SetVar(retVal, FOCUSDIST, NewReal((real) INITEYEDIST));
SetVar(retVal, ROLL, NewReal((real) 0.0));
SetVar(retVal, YAW, NewReal((real) M_PI));
SetVar(retVal, PITCH, NewReal((real) 0.0));
return retVal;
}
ObjPtr NewRenderer()
/*Returns a new renderer*/
{
ObjPtr retVal;
ObjPtr name;
if (oneRenderer)
{
if (commonRenderer) return commonRenderer;
}
retVal = NewObject(rendererClass, 0);
if (oneRenderer)
{
commonRenderer = retVal;
}
name = GetVar(rendererClass, NAME);
if (name)
{
sprintf(tempStr, "%s %d", GetString(name), ++rendererNum);
}
else
{
sprintf(tempStr, "Renderer %d", ++rendererNum);
}
name = NewString(tempStr);
SetVar(retVal, NAME, name);
SetVar(retVal, SPACES, NewList());
SetVar(retVal, RENDERTYPE, NewInt(RT_HARDWARE));
SetVar(retVal, FILTERTYPE, NewInt(RT_NONE));
return retVal;
}
static ObjPtr CloneObserver(observer)
ObjPtr observer;
/*Clones observer*/
{
Bool oldOneObserver;
ObjPtr retVal;
ObjPtr m, om;
oldOneObserver = oneObserver;
oneObserver = false;
retVal = NewObserver();
oneObserver = oldOneObserver;
om = GetVar(observer, XFORM);
if (om)
{
m = NewMatrix();
MATCOPY(MATRIXOF(m), MATRIXOF(om));
SetVar(retVal, XFORM, m);
}
CopyVar(retVal, observer, ROTAXIS);
CopyVar(retVal, observer, ROTSPEED);
CopyVar(retVal, observer, PERSPECSTUFF);
CopyVar(retVal, observer, LOCATION);
CopyVar(retVal, observer, FOCUSPOINT);
CopyVar(retVal, observer, FOCUSDIST);
CopyVar(retVal, observer, ROLL);
CopyVar(retVal, observer, YAW);
CopyVar(retVal, observer, PITCH);
CopyVar(retVal, observer, AIRSPEED);
return retVal;
}
static ObjPtr CloneClock(clock)
ObjPtr clock;
{
ObjPtr retVal;
Bool oldOneClock;
oldOneClock = oneClock;
oneClock = false;
retVal = NewClock();
oneClock = oldOneClock;
CopyVar(retVal, clock, CYCLECLOCK);
CopyVar(retVal, clock, TIMESTEPS);
CopyVar(retVal, clock, DATASETS);
CopyVar(retVal, clock, TIME);
return retVal;
}
static ObjPtr CloneRenderer(renderer)
ObjPtr renderer;
/*Clones renderer*/
{
Bool oldOneRenderer;
ObjPtr retVal;
oldOneRenderer = oneRenderer;
oneRenderer = false;
retVal = NewRenderer();
oneRenderer = oldOneRenderer;
CopyVar(retVal, renderer, RENDERTYPE);
CopyVar(retVal, renderer, FILTERTYPE);
return retVal;
}
#if 0
static ObjPtr DrawExtraObserverIcon(icon, x, y)
ObjPtr icon;
int x, y;
/*Draws the extra stuff for an observer icon*/
{
ObjPtr space, observers;
space = GetObjectVar("DrawExtraObserverIcon", icon, SPACE);
if (!space)
{
return ObjFalse;
}
observers = GetListVar("DrawExtraObserverIcon", space, OBSERVERS);
if (!observers)
{
return ObjFalse;
}
if (GetVar(icon, REPOBJ) == LISTOF(observers) -> thing)
{
FrameUIRect(x - ICONSIZE / 2 - 4, x + ICONSIZE / 2 + 4, y - ICONSIZE / 2 - ICONSHADOW - 4, y + ICONSIZE / 2 + 4, UIREC);
SetUIColor(UIBLACK);
}
return ObjTrue;
}
#endif
void InitSpaces()
/*Initializes the spaces*/
{
ObjPtr icon;
/*Create a class of spaces*/
spaceClass = NewObject(NULLOBJ, 0);
AddToReferenceList(spaceClass);
SetMethod(spaceClass, DRAW, DrawSpace);
SetVar(spaceClass, NAME, NewString("Space"));
SetMethod(spaceClass, PRESS, PressSpace);
SetMethod(spaceClass, KEYDOWN, KeyDownSpace);
SetVar(spaceClass, XSTRETCH, NewInt(1));
SetVar(spaceClass, YSTRETCH, NewInt(1));
SetVar(spaceClass, TYPESTRING, NewString("space"));
SetVar(spaceClass, HELPSTRING,
NewString("A space provides a 3-dimensional world for visualization \
objects. Objects are automatically scaled and translated to appear near the \
center by default. Any number of visualization objects can exist within a space. \
Spaces are controlled by controllers, such as observers and lights.\n\
-\n\
Click and drag within the space using the left \
mouse button to move all the objects within the space. Hold down the Shift \
key while dragging to contrain motion to an orthogonal axis of the space. \
If Space Motion Guides is selected in the Preferences window, you will see the \
objects as embedded within a gray lattice showing the orthogonal axes.\n\
-\n\
Click and drag within the space ucing the center mouse button to rotate \
the entire space around the focus point of the observer at the center of the \
space. The space can be rotated around any axis. Hold down the Shift key \
to constrain to rotation around an orthogonal axis of the space. Double-click \
to snap the space to the nearest straight-on orientation. \
If Space Rotation Guides is selected in the Preferences window, you will see the \
virtual trackball that you are rotation as a wire frame sphere. If Rotation Inertia \
is selected in the Preferences window, the space will continue to rotate if you \
give it a spin and let go while moving the mouse.\n\
-\n\
Both motion and rotation will affect other spaces controlled by the same observer."));
lmdef(DEFMATERIAL, 1, 0, NULL);
/*Initialize a space panel*/
spacePanelClass = NewObject(panelClass, 0);
AddToReferenceList(spacePanelClass);
SetMethod(spacePanelClass, DROPOBJECTS, DropInSpacePanel);
/*Initialize a space back panel*/
spaceBackPanelClass = NewObject(spacePanelClass, 0);
AddToReferenceList(spaceBackPanelClass);
SetVar(spaceBackPanelClass, BACKGROUND, NewInt(UIBLACK));
/*Create class of space controllers*/
controllerClass = NewObject(NULLOBJ, 0);
AddToReferenceList(controllerClass);
/*Create class of clocks*/
clockClass = NewObject(controllerClass, 0);
AddToReferenceList(clockClass);
SetMethod(clockClass, MARKTIME, MarkClockTime);
SetMethod(clockClass, CLONE, CloneClock);
SetMethod(clockClass, BINDTOSPACE, BindClockToSpace);
SetMethod(clockClass, TOUCHSPACE, TouchSpaceClock);
SetMethod(clockClass, NEWCTLWINDOW, ShowClockControls);
SetMethod(clockClass, DOUBLECLICK, ShowIconControls);
SetMethod(clockClass, RESOLVE, ResolveClock);
SetMethod(clockClass, REINIT, MakeClockDatasets);
SetVar(clockClass, CONTROLLERCLASS, clockClass);
SetVar(clockClass, DEFAULTICON, icon = NewIcon(0, 0, ICONCLOCK, "Clock"));
SetVar(icon, HELPSTRING,
NewString("This icon represents a clock. The clock controls the current \
time displayed within a space and the rate at which time goes forward or backward. \
You can see controls for this clock by selecting it and choosing the Show Controls \
item in the Object menu.\n\
\n\
You can drag this icon into the icon corral of another visualization window to have \
it control the other space as well. \
A space can only be controlled by one clock at a time. If you drag another \
clock icon into this space, this clock will be replaced.\n\
\n\
You can place a time display in the image of the space itself by dragging this \
icon into the space itself."));
SetVar(clockClass, NAME, NewString("Clock"));
DeclareDependency(clockClass, TIMEREGISTERED, TIMEBOUNDS);
SetMethod(clockClass, TIMEREGISTERED, RegisterTime);
DeclareDependency(clockClass, TIMEFORMAT, DATASETS);
SetMethod(clockClass, TIMEFORMAT, MakeClockTimeFormat);
/*Create class of observers*/
observerClass = NewObject(controllerClass, 0);
AddToReferenceList(observerClass);
SetMethod(observerClass, BINDTOSPACE, BindObserverToSpace);
SetMethod(observerClass, TOUCHSPACE, TouchSpaceObserver);
SetMethod(observerClass, NEWCTLWINDOW, ShowObserverControls);
SetMethod(observerClass, DOUBLECLICK, ShowIconControls);
SetVar(observerClass, VIEWTYPE, NewInt(VT_PERSPECTIVE));
SetMethod(observerClass, MARKTIME, WakeObserver);
icon = NewIcon(0, 0, ICONOBSERVER, "Observer");
SetVar(icon, HELPSTRING,
NewString("This icon represents an observer. The observer represents \
you looking into the 3-dimensional space. You can change attributes such as the \
viewing angle and near and far clipping planes in the control panel, which you \
can show by selecting the icon and choosing the Show Controls \
item in the Object menu.\n\
\n\
You can drag this icon into the icon corral of another visualization window to have \
it control the other space as well. When an observer controls more than one \
space, the view of the objects in the spaces are tied together. This is very \
useful for viewing several similar datasets from the same viewpoint at once. \
A space can only be controlled by one observer at a time. If you drag another \
observer icon into this space, this observer will be replaced."));
#if 0
SetMethod(icon, ICONEXTRADRAW, DrawExtraObserverIcon);
#endif
SetVar(observerClass, DEFAULTICON, icon);
SetVar(observerClass, CONTROLLERCLASS, observerClass);
SetVar(observerClass, NAME, NewString("Observer"));
SetMethod(observerClass, CLONE, CloneObserver);
/*Create class of renderers*/
rendererClass = NewObject(controllerClass, 0);
AddToReferenceList(rendererClass);
SetMethod(rendererClass, BINDTOSPACE, BindRendererToSpace);
SetMethod(rendererClass, TOUCHSPACE, TouchSpaceRenderer);
SetMethod(rendererClass, NEWCTLWINDOW, ShowRendererControls);
SetMethod(rendererClass, DOUBLECLICK, ShowIconControls);
SetVar(rendererClass, CONTROLLERCLASS, rendererClass);
icon = NewIcon(0, 0, ICONRENDERER, "Renderer");
SetVar(icon, HELPSTRING,
NewString("This icon represents a renderer. The controls the process \
of rendering, or producing an image from the visualization objects in the space. \
You can show controls for the renderer by selecting the icon and choosing the Show Controls \
item in the Object menu.\n\
\n\
You can drag this icon into the icon corral of another visualization window to have \
it control the other space as well. \
A space can only be controlled by one renderer at a time. If you drag another \
renderer icon into this space, this renderer will be replaced."));
SetVar(rendererClass, DEFAULTICON, icon);
SetVar(rendererClass, NAME, NewString("Renderer"));
SetMethod(rendererClass, CLONE, CloneRenderer);
InitLights();
}
void KillSpaces()
/*Kills the spaces*/
{
KillLights();
DeleteThing(rendererClass);
DeleteThing(observerClass);
DeleteThing(clockClass);
DeleteThing(controllerClass);
DeleteThing(spaceBackPanelClass);
DeleteThing(spacePanelClass);
DeleteThing(spaceClass);
}
|