/*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); }