/*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 "ScianWindows.h" #include "ScianDraw.h" #include "ScianObjWindows.h" #include "ScianVisWindows.h" #include "ScianVisObjects.h" #include "ScianDialogs.h" #include "ScianScripts.h" #include "ScianIcons.h" #include "ScianIDs.h" #include "ScianControls.h" #include "ScianButtons.h" #include "ScianTextBoxes.h" #include "ScianTitleBoxes.h" #include "ScianFontSystem.h" #include "ScianPerspec.h" #include "ScianErrors.h" #include "ScianTimers.h" #include "ScianSliders.h" #include "ScianStyle.h" #include "ScianComplexControls.h" #include "ScianMethods.h" #include "ScianPick.h" #include "ScianPreferences.h" #include "ScianFiles.h" #include "ScianObjFunctions.h" #include "ScianTemplates.h" #include "ScianTemplateHelper.h" #include "ScianSymbols.h" #include "ScianDepend.h" #include "ScianDatasets.h" #include "ScianFilters.h" #include "ScianDatabase.h" #include "ScianAnimation.h" #include "ScianPictures.h" #include "ScianSnap.h" Bool phsco = false; /*True iff phscologram is on*/ Bool drawingTransparent = false; /*True iff drawing only transparent*/ int clockDisplaySerialNum = 0; /*Clock display serial number*/ extern real fps; ObjPtr spacePickedObject = NULLOBJ; int qualityDrawnSoFar; /*Max quality drawn so far*/ int minDrawingQuality; /*Minimum quality to draw*/ int maxDrawingQuality; /*Maximum quality to draw*/ #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 /*Dial factors*/ #define DIALROTFACTOR 0.02 /*Factor for rotating with dials*/ #define DIALMOVEFACTOR 0.01 /*Factor for moving with dials*/ #define DIALANGLEFACTOR 0.001 /*Factor for changing angles*/ #define DIALWIDTHFACTOR 0.005 /*Factor for changing width*/ #define DIALBINOCFACTOR 0.01 /*Factor for changing binocularity*/ /*Stereo and blanking values for CrystalEyes*/ #define YSTEREO 491 #define YBLANK 41 #define ASPECT (((float) (right - left)) / ((float) (top - bottom))) * (phsco ? 0.61 : 1.0) #define MINROT 3 /*Minimum number of pixels to move to rotate*/ ObjPtr curSpace = 0; /*Current space*/ real curFocusDist = 0.0; /*Current focus distance*/ real curViewField = 0.0; /*Current field of view. Angle if perspec, width if ortho*/ real curViewClip[2]; /*Current viewing clipping distances*/ ObjPtr curSpaceLights; /*Lights in the current space*/ int curWhichView; /*Current which view*/ ObjPtr spaceClass = 0; /*Class of a 3-D space*/ 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 worldClass; /*Class of object with world*/ ObjPtr placementClass; /*Class of object with placement*/ ObjPtr stereoClass; /*Class of object with stereo*/ ObjPtr observerClass; /*Class of observers*/ ObjPtr rendererClass; /*Class of renderers*/ Matrix viewerCoordsMatrix; /*Matrix in viewer coordinates*/ Bool oneObserver = false; Bool oneRenderer = false; Bool oneClock = false; Bool oneLights = false; /*True iff to make only one controller*/ ObjPtr placementTrackClass; /*Track for observer placement*/ ObjPtr jointClass; /*Class of joints*/ real observerPosition[3]; VertexPtr bestPickVertex; /*The best vertex from the last pick*/ void DrawSpaceContents(ObjPtr, int, int, int, int, int); #ifdef HAVE_PROTOTYPES static double f(double x) #else static double f(x) double x; #endif /*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] = plusInf; bounds[1] = bounds[3] = bounds[5] = minusInf; runner = LISTOF(list); if (!runner) { return false; } while (runner) { ObjPtr repObj; ObjPtr var; repObj = runner -> thing; if (IsObject(repObj)) { MakeVar(repObj, BOUNDS); var = GetVar(repObj, BOUNDS); if (var) { real localBounds[6]; Array2CArray(localBounds, var); var = GetVar(repObj, XSCALE); if (var) { real v; v = GetReal(var); localBounds[0] *= v; localBounds[1] *= v; } var = GetVar(repObj, YSCALE); if (var) { real v; v = GetReal(var); localBounds[2] *= v; localBounds[3] *= v; } var = GetVar(repObj, ZSCALE); if (var) { real v; v = GetReal(var); localBounds[4] *= v; localBounds[5] *= v; } 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; } /*If bounds ludicrous, make them sane*/ if (bounds[0] == plusInf || bounds[2] == plusInf || bounds[4] == plusInf || bounds[1] == minusInf || bounds[3] == minusInf || bounds[5] == minusInf) { bounds[0] = bounds[2] = bounds[4] = 0.0; bounds[1] = bounds[3] = bounds[5] = 1.0; } return true; } #define NUDGESIZE 2000 /*Size of one nudge*/ #define MAXNNUDGES 4 /*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*/ { #ifdef GRAPHICS curZMax -= NUDGESIZE; curZMin -= NUDGESIZE; lsetdepth(curZMin, curZMax); #endif } void NudgeFarther() /*Nudges objects to be drawn farther*/ { #ifdef GRAPHICS curZMax += NUDGESIZE; curZMin += NUDGESIZE; lsetdepth(curZMin, curZMax); #endif } #ifdef HAVE_PROTOTYPES void upat(float vx, float vy, float vz, float px, float py, float pz, float ux, float uy, float uz) #else void upat(vx, vy, vz, px, py, pz, ux, uy, uz) float vx; float vy; float vz; float px; float py; float pz; float ux; float uy; float uz; #endif /*Replacement for lookat. Specifies a viewer at (vx, vy, vz) looking at a point (px, py, pz) with an up vector of (ux, uy, uz). Adapted from Thant Tessman's code in 4Dgifts.*/ { #ifdef GRAPHICS int i; float forward[3], side[3], up[3]; float m[4][4]; forward[0] = px - vx; forward[1] = py - vy; forward[2] = pz - vz; /* temporarily use view-up to hold world-up */ up[0] = ux; up[1] = uy; up[2] = uz; NORMALIZE(forward); /* generate the vector side from the * 2 vectors view forward and world up */ CROSS(forward, up, side); NORMALIZE(side); /* generate the view up vector from * the 2 vectors view forward and view side */ CROSS(side, forward, up); m[0][0] = side[0]; m[1][0] = side[1]; m[2][0] = side[2]; m[3][0] = 0.0; m[0][1] = up[0]; m[1][1] = up[1]; m[2][1] = up[2]; m[3][1] = 0.0; m[0][2] = -forward[0]; m[1][2] = -forward[1]; m[2][2] = -forward[2]; m[3][2] = 0.0; m[0][3] = 0.0; m[1][3] = 0.0; m[2][3] = 0.0; m[3][3] = 1.0; multmatrix(m); translate(-vx, -vy, -vz); #endif } #ifdef HAVE_PROTOTYPES void StartSpace(ObjPtr space, int left, int right, int bottom, int top, int action, int whichView) #else void StartSpace(space, left, right, bottom, top, action, whichView) ObjPtr space; int left, right, bottom, top; int action; int whichView; #endif /*Start a drawing, rotation, or press in a space. Action is the action that is being performed*/ { #ifdef GRAPHICS real eyePosn[3]; /*Coords of the eye*/ real focusPoint[3]; /*Coords of the focus point*/ real upVector[3]; /*The up vector of the observer*/ real forwardVector[3]; 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; real scaleFactor[3]; /*Factor to scale*/ real maxSize; /*Maximum size of the objects this draw*/ ObjPtr observer; /*Observer of the space*/ real eyeOffset; ObjPtr var; Matrix proj, model; float aspect; /*Aspect ratio*/ curWhichView = whichView; if (windowSystem == WS_GLWINDOWS) { /*Change bounds and aspect ratio depending on whichView*/ aspect = ASPECT; switch(VIEWOF(whichView)) { case VIEW_XLEFT: case VIEW_WRIGHT: left = (left + right) / 2 + 1; aspect = ASPECT; break; case VIEW_XRIGHT: case VIEW_WLEFT: right = (left + right) / 2; aspect = ASPECT; break; case VIEW_CLEFT: bottom = YSTEREO + YBLANK; top = bottom + YSTEREO; break; case VIEW_CRIGHT: bottom = 0; top = bottom + YSTEREO; break; } /*Set a new viewport to the current area*/ SetSubPort(left, right, bottom, top); } observer = GetObjectVar("StartSpace", space, OBSERVER); if (!observer) return; if (windowSystem == WS_GLWINDOWS) { if (ISLEFT(whichView)) { var = GetVar(observer, BINOCULARITY); eyeOffset = -GetReal(var) * 0.5; } else if (ISRIGHT(whichView)) { var = GetVar(observer, BINOCULARITY); eyeOffset = GetReal(var) * 0.5; } else { eyeOffset = 0.0; } } /*Set up current viewing parameters*/ var = GetVar(observer, FOCUSDIST); if (var) { curFocusDist = GetReal(var); } else { curFocusDist = INITEYEDIST; } var = GetVar(observer, VIEWFIELD); if (var) { curViewField = GetReal(var); } else { curViewField = INITAOV; } var = GetVar(observer, VIEWCLIP); if (var) { Array2CArray(curViewClip, var); } else { curViewClip[0] = INITNEARCLIP; curViewClip[1] = INITFARCLIP; } if (curViewClip[0] < MINCLIP) curViewClip[0] = MINCLIP; if (curViewClip[1] < MINCLIP) curViewClip[1] = MINCLIP; /*Get information about the eye*/ GetObserverLocation(eyePosn, observer); GetFocusPoint(focusPoint, observer); GetAdjustedVectors(forwardVector, upVector, observer); if (windowSystem == WS_GLWINDOWS) { if (ISSTEREO(whichView)) { real sideVector[3]; real temp; /*Have to slide eyePosn and focusPoint over somewhat*/ CROSS(forwardVector, upVector, sideVector); /*Move eye over*/ eyePosn[0] += sideVector[0] * eyeOffset; eyePosn[1] += sideVector[1] * eyeOffset; eyePosn[2] += sideVector[2] * eyeOffset; } } /*Get information about the clock*/ MakeVar(space, TIME); var = GetVar(space, TIME); if (var) { spaceTime = GetReal(var); } else { spaceTime = 0.0; } /*Set the current space*/ curSpace = space; if (action == PICKSPACE) { StartPick(); } if (windowSystem == WS_GLWINDOWS) { #ifdef GL4D mmode(MPROJECTION); #endif if (whichView & VIEW_MOD_ORTHO) { /*Plain old orthographic projection.*/ ortho(-aspect * curViewField, aspect * curViewField, -curViewField, curViewField, curViewClip[0], curViewClip[1]); } else { /*Exciting perspective projection!!!*/ perspective((long) (curViewField * 10.0), aspect, curViewClip[0], curViewClip[1]); } #ifdef GL4D mmode(MVIEWING); loadmatrix(Identity); #endif } mmode(MVIEWING); upat(eyePosn[0], eyePosn[1], eyePosn[2], focusPoint[0], focusPoint[1], focusPoint[2], upVector[0], upVector[1], upVector[2]); getmatrix(viewerCoordsMatrix); /*Set up the lights*/ curSpaceLights = GetVar(space, LIGHTS); if (action == DRAWSPACE) { if (curSpaceLights) { StartLights(space, curSpaceLights, false); } } if (action == DRAWSPACE) { int i; real radius; if (windowSystem == WS_GLWINDOWS) { /*Shift filter for 3-D glasses*/ if (rgbp && (VIEWOF(whichView) == VIEW_CRLEFT || VIEWOF(whichView) == VIEW_RCRIGHT)) { BeginMask(false, true, true, true); } else if (rgbp && (VIEWOF(whichView) == VIEW_CRRIGHT || VIEWOF(whichView) == VIEW_RCLEFT)) { BeginMask(true, false, false, true); } else if (rgbp && (VIEWOF(whichView) == VIEW_GRLEFT || VIEWOF(whichView) == VIEW_RGRIGHT)) { BeginMask(false, true, false, true); } else if (rgbp && (VIEWOF(whichView) == VIEW_GRRIGHT || VIEWOF(whichView) == VIEW_RGLEFT)) { BeginMask(true, false, false, true); } else { BeginMask(true, true, true, true); } if (VIEWOF(whichView) == VIEW_EOLEFT || VIEWOF(whichView) == VIEW_OERIGHT) { BeginEvenOnly(); } if (VIEWOF(whichView) == VIEW_OELEFT || VIEWOF(whichView) == VIEW_EORIGHT) { BeginOddOnly(); } } /*Go to Gouraud shading*/ shademodel(GOURAUD); if (windowSystem == WS_GLWINDOWS) { /*Set up the Z-buffer*/ if (action == DRAWSPACE) { curZMin = zMin + NUDGESIZE * MAXNNUDGES; curZMax = zMax - NUDGESIZE * MAXNNUDGES; lsetdepth(curZMin, curZMax); zbuffer(TRUE); zclear(); } } /*Draw rotation and motion guides*/ if (whichView & VIEW_MOD_ORTHO) { radius = SPACESPHERESIZE * 1.85 * curViewField; } else { radius = SPACESPHERESIZE * curFocusDist * rsin(curViewField * M_PI / 180.0); } if (GetPredicate(curSpace, SHOWROTGUIDES)) { SetUIColor(UIWHITE); DrawWFSphere(focusPoint[0], focusPoint[1], focusPoint[2], radius); DrawSpaceLine(focusPoint[0] - radius, focusPoint[1], focusPoint[2], focusPoint[0] + radius, focusPoint[1], focusPoint[2]); DrawSpaceLine(focusPoint[0], focusPoint[1] - radius, focusPoint[2], focusPoint[0], focusPoint[1] + radius, focusPoint[2]); DrawSpaceLine(focusPoint[0], focusPoint[1], focusPoint[2] - radius, focusPoint[0], focusPoint[1], focusPoint[2] + radius); } if (GetPredicate(curSpace, SHOWMOTGUIDES)) { real x, y; SetUIColor(UIGRAY50); for (x = -10.0; x <= 10.0; x += 1.0) { for (y = -10.0; y <= 10.0; y += 1.0) { DrawSpaceLine(x, y, -10.0, x, y, 10.0); DrawSpaceLine(x, -10.0, y, x, 10.0, y); DrawSpaceLine(-10.0, x, y, 10.0, x, y); } } } } { if (GetPredicate(observer, AUTOADJUST)) { float normFactor; /*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]); } switch (windowSystem) { case WS_GLWINDOWS: normFactor = 1.6 / maxSize; break; case WS_CAVESIM: normFactor = 5.0 / maxSize; break; } scaleFactor[0] = normFactor; scaleFactor[1] = normFactor; scaleFactor[2] = normFactor; var = NewRealArray(1, 3L); ((real *) ELEMENTS(var))[0] = 1.0 / scaleFactor[0]; ((real *) ELEMENTS(var))[1] = 1.0 / scaleFactor[1]; ((real *) ELEMENTS(var))[2] = 1.0 / scaleFactor[2]; SetVar(observer, SPACESCALE, var); var = NewRealArray(1, 3L); CArray2Array(var, center); SetVar(observer, SPACEORIGIN, var); } else { var = GetFixedArrayVar("BeginSpace", observer, SPACESCALE, 1, 3L); if (var) { Array2CArray(scaleFactor, var); scaleFactor[0] = 1.0 / scaleFactor[0]; scaleFactor[1] = 1.0 / scaleFactor[1]; scaleFactor[2] = 1.0 / scaleFactor[2]; } else { scaleFactor[0] = 1.0; scaleFactor[1] = 1.0; scaleFactor[2] = 1.0; } var = GetFixedArrayVar("BeginSpace", observer, SPACEORIGIN, 1, 3L); if (var) { Array2CArray(center, var); } else { center[0] = 0.0; center[1] = 0.0; center[2] = 0.0; } } /*Save observer position*/ observerPosition[0] = eyePosn[0] * scaleFactor[0] + center[0]; observerPosition[1] = eyePosn[1] * scaleFactor[1] + center[1]; observerPosition[2] = eyePosn[2] * scaleFactor[2] + center[2]; scale(scaleFactor[0], scaleFactor[1], scaleFactor[2]); translate(-center[0], -center[1], -center[2]); } #endif } #ifdef HAVE_PROTOTYPES void StopSpace(int action) #else void StopSpace(action) int action; #endif /*Stops drawing, rotating, or pressing in a space*/ { #ifdef GRAPHICS if (action == DRAWSPACE) { if (windowSystem == WS_GLWINDOWS) { real radius; OptimizeSharpness(); if (curWhichView & VIEW_MOD_ORTHO) { radius = SPACESPHERESIZE * 1.85 * curViewField; } else { radius = SPACESPHERESIZE * curFocusDist * rsin(curViewField * M_PI / 180.0); } loadmatrix(viewerCoordsMatrix); /*Draw the observer*/ { ObjPtr observer; FuncTyp method; observer = GetVar(curSpace, OBSERVER); if (observer) { method = GetMethod(observer, DRAW); if (method) { (*method)(observer); } } } /*Draw the lights*/ if (curSpaceLights) { DrawLights(curSpace, curSpaceLights, action, radius); StopLights(curSpace); } EndMask(); if (VIEWOF(curWhichView) == VIEW_EOLEFT || VIEWOF(curWhichView) == VIEW_OERIGHT) { EndEvenOnly(); } if (VIEWOF(curWhichView) == VIEW_OELEFT || VIEWOF(curWhichView) == VIEW_EORIGHT) { EndOddOnly(); } } } if (action == PICKSPACE) { spacePickedObject = StopPick(&bestPickVertex); } if (windowSystem == WS_GLWINDOWS) { if (action == DRAWSPACE) { zbuffer(FALSE); } RestoreSubPort(); } #endif curSpace = NULLOBJ; } ObjPtr DrawObserver(observer) ObjPtr observer; /*Draws an observer*/ { ObjPtr keyList, result; if (drawingQuality < DQ_FULL) return ObjTrue; keyList = NewList(); PostfixList(keyList, NewSymbol(CLASSID)); PostfixList(keyList, NewInt(CLASS_SEQUENCE)); zbuffer(FALSE); SetUIFont(UIBOLDNORMALFONT); SetUIColor(UIWHITE); result = SearchDatabase(keyList); if (result && LISTOF(result)) { ThingListPtr runner; char *name; for (runner = LISTOF(result); runner; runner = runner -> next) { ObjPtr track, snapshot; real time; real frameWidth; int whichFrame; ObjPtr var; track = GetTrackedVarGroupWithinSequence(runner -> thing, observer, "Placement"); if (track) { ObjPtr var; ObjPtr snap; ObjPtr *joints; ObjPtr *keyframes; int k; int nJoints; int nKeyframes; real loc[3]; real vw[3]; real l1[3], l2[3]; int botFrame, topFrame; Quaternion qrot; /*Quaternion for rotation*/ var = GetVar(track, KEYFRAMES); if (!var) continue; nKeyframes = DIMS(var)[0]; keyframes = ELEMENTS(var); for (k = 0; k < nKeyframes; ++k) { char str[20]; snap = GetVar(keyframes[k], SNAPSHOT); var = GetVar(snap, LOCATION); Array2CArray(loc, var); sprintf(str, "%d", k); DrawSpaceString(loc[0], loc[1], loc[2], str); var = GetVar(keyframes[k], WORLDVELOCITY); if (var) { Array2CArray(vw, var); DrawSpaceLine(loc[0], loc[1], loc[2], loc[0] + vw[0] * 5.0, loc[1] + vw[1] * 5.0, loc[2] + vw[2] * 5.0); } } /*Get the joints*/ MakeVar(track, JOINTS); var = GetVar(track, JOINTS); if (!var) continue; nJoints = DIMS(var)[0]; joints = ELEMENTS(var); /*Find the range of keyframes*/ var = GetIntVar("DrawObserver", keyframes[0], WHICHFRAME); if (!var) return ObjTrue; botFrame = GetInt(var); var = GetIntVar("DrawObserver", keyframes[nKeyframes - 1], WHICHFRAME); if (!var) return ObjTrue; topFrame = GetInt(var); if (topFrame > botFrame) { TweenObserverPlacement(track, botFrame, qrot, l1); for (k = 0; k < topFrame; ++k) { TweenObserverPlacement(track, k + 1, qrot, l2); SetUIColor(k & 1 ? UIWHITE : UICYAN); DrawSpaceLine(l1[0], l1[1], l1[2], l2[0], l2[1], l2[2]); l1[0] = l2[0]; l1[1] = l2[1]; l1[2] = l2[2]; } } } } } return ObjTrue; } static ObjPtr ForAllSpaceObjects(object, routine) ObjPtr object; FuncTyp routine; /*Does routine on a space and its contents*/ { ObjPtr contents; ObjPtr lights; (*routine)(object); /*Now check my CONTENTS*/ contents = GetVar(object, CONTENTS); if (contents) { ForAllObjects(contents, routine); } /*Now check the lights*/ lights = GetVar(object, LIGHTS); if (lights) { ForAllObjects(lights, routine); } /*DIK also observer and renderer and clock*/ return ObjTrue; } ObjPtr WakeObserver(observer, lateness) ObjPtr observer; double lateness; /*Wakes an observer and makes it continue rotation*/ { ObjPtr var; Bool wakeAgain = false; /*True iff wake again*/ int x, y; real phi; DoNotDisturb(observer, MARKTIME); /*See if we need to spin*/ var = GetVar(observer, ROTSPEED); if (var && 0.0 != (phi = GetReal(var))) { real axis[3]; /*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; } /*See if we want to fly*/ else if (GetPredicate(observer, FLYING)) { #ifdef INTERACTIVE ObjPtr var; real eyePosn[3]; real roll, dPitch, pitch; float dTime; real perspecStuff[4]; real airspeed; long mouseX, mouseY; real upVector[3]; real forwardVector[3]; real sp, cp, t; real temp[3]; dTime = lateness; /*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 && IsRealArray(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); } /*Get a new roll and dpitch value*/ mouseX = getvaluator(MOUSEX); mouseY = getvaluator(MOUSEY); roll = (((real) mouseX) / (real) scrWidth) * 2 * MAXROLL - MAXROLL; dPitch = (((real) mouseY) / (real) scrHeight) * 2 * MAXDPITCH - MAXDPITCH; /*Update eye position based on airspeed*/ /*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)); /*Rotate around the up vector*/ phi = roll * ROLLYAWFACTOR * dTime; GetUpVector(upVector, observer); RotateObserver(observer, upVector, phi, false); GetAdjustedVectors(forwardVector, upVector, observer); /*Move eye*/ eyePosn[0] += forwardVector[0] * airspeed * dTime; eyePosn[1] += forwardVector[1] * airspeed * dTime; eyePosn[2] += forwardVector[2] * airspeed * dTime; var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); ImInvalid(observer); wakeAgain = true; #endif } if (wakeAgain) { WakeMe(observer, MARKTIME, Clock() + 0.001); } return ObjTrue; } #ifdef HAVE_PROTOTYPES 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() + 0.001); } } #ifdef HAVE_PROTOTYPES void RecordObserver(ObjPtr observer) #else void RecordObserver(observer) ObjPtr observer; #endif /*Records the current status of an observer*/ { ObjPtr keyList, result; keyList = NewList(); PostfixList(keyList, NewSymbol(CLASSID)); PostfixList(keyList, NewInt(CLASS_SEQUENCE)); PostfixList(keyList, NewSymbol(RECORDING)); PostfixList(keyList, ObjTrue); result = SearchDatabase(keyList); if (result && LISTOF(result)) { ThingListPtr runner; char *name; runner = LISTOF(result); while (runner) { ObjPtr track, snapshot; real time; real frameWidth; int whichFrame; ObjPtr var; track = TrackVarGroupWithinSequence(runner -> thing, observer, "Placement", placementTrackClass); if (track) { ObjPtr list; MakeVar(runner -> thing, FRAMERATE); var = GetVar(runner -> thing, FRAMERATE); if (var) { frameWidth = 1.0 / GetReal(var); } else { frameWidth = 1.0 / 30.0; } var = GetVar(runner -> thing, TIME); if (var) { time = GetReal(var); } else { time = 0.0; } whichFrame = (int) floor(time / frameWidth + 0.5); list = NewList(); PostfixList(list, NewSymbol(LOCATION)); PostfixList(list, NewSymbol(ROTQUAT)); PostfixList(list, NewSymbol(FOCUSDIST)); snapshot = TakeVarsSnapshot(observer, list); InsertKeySnapshot(track, whichFrame, snapshot); SetVar(track, CHANGED, ObjTrue); } runner = runner -> next; } } } #ifdef HAVE_PROTOTYPES void LogObserver(ObjPtr observer) #else void LogObserver(observer) ObjPtr observer; #endif /*Logs an observer*/ { ObjPtr var; real temp[3]; RecordObserver(observer); if (logging) { MakeObjectName(tempStr, observer); Log("begin snapshot "); Log(tempStr); Log("\n"); LogVariable(observer, LOCATION); LogVariable(observer, ROTQUAT); LogVariable(observer, FOCUSDIST); Log("end snapshot\n"); } } #ifdef HAVE_PROTOTYPES ObjPtr MoveSpace(ObjPtr object, int x, int y, long flags) #else ObjPtr MoveSpace(object, x, y, flags) ObjPtr object; int x, y; long flags; #endif /*Does a move in a space beginning at x and y. Returns ObjTrue if it was a good move.*/ { #ifdef INTERACTIVE int d; int newX, newY; ObjPtr objects, observer, corral; ThingListPtr runner; ObjPtr tempObj; real eyePosn[3]; real up[3], forward[3], side[3]; real moveVector[3]; real uMove, vMove; real sr, cr; real sp, cp; real sy, cy; real sf; real focusDist; int left, right, bottom, top; Get2DIntBounds(object, &left, &right, &bottom, &top); d = top - bottom; corral = GetVar(object, CORRAL); SetRotationMotor(0.0, 0.0, 0.0, 1.0); SetVar(object, MOVING, ObjTrue); /*Get info about the observer*/ observer = GetObjectVar("MoveSpace", object, OBSERVER); SaveForUndo(observer); GetObserverLocation(eyePosn, observer); GetAdjustedVectors(forward, up, observer); CROSS(forward, up, side); /*Get perspective stuff for scaling movement and eyeball distance*/ tempObj = GetRealVar("MoveSpace", observer, FOCUSDIST); if (tempObj) { focusDist = GetReal(tempObj); } else { focusDist = INITEYEDIST; } if (flags & F_DOUBLECLICK) { } /*Press the contents of the space*/ else while (Mouse(&newX, &newY)) { if ((newX != x) || (newY != y)) { /*Get raw uMove and vMove*/ sf = focusDist * MOVEFACTOR; uMove = sf * ((float) (x - newX)) / (float) d; vMove = sf * ((float) (y - newY)) / (float) d; moveVector[0] = uMove * side[0] + vMove * up[0]; /*Produce a motion vector*/ moveVector[0] = uMove * side[0] + vMove * up[0]; moveVector[1] = uMove * side[1] + vMove * up[1]; moveVector[2] = uMove * side[2] + vMove * up[2]; if (flags & F_SHIFTDOWN) { int k, best = 0; float curDot, maxDot = 0.0; float rr; /*Constrain the motion axis to the nearest ortho axis*/ for (k = 0; k < 3; ++k) { curDot = moveVector[0] * Identity[k][0] + moveVector[1] * Identity[k][1] + moveVector[2] * Identity[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 * Identity[best][0]; moveVector[1] = maxDot * Identity[best][1]; moveVector[2] = maxDot * Identity[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); x = newX; y = newY; ImInvalid(observer); DrawMe(object); } } SetVar(object, MOVING, ObjFalse); if (logging) { LogObserver(observer); } #endif return ObjTrue; } #ifdef HAVE_PROTOTYPES ObjPtr RotateSpace(ObjPtr object, int x, int y, long flags) #else ObjPtr RotateSpace(object, x, y, flags) ObjPtr object; int x, y; long flags; #endif /*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; ObjPtr observer/*, observers*/; ObjPtr lights; ThingListPtr runner; #ifdef INTERACTIVE Get2DIntBounds(object, &left, &right, &bottom, &top); /*See if there are any lights to rotate*/ lights = GetVar(object, LIGHTS); if (lights) { runner = LISTOF(lights); while (runner) { if (IsSelected(runner -> thing)) { break; } runner = runner -> next; } } /*If none selected, say there are none*/ if (!runner) { lights = NULLOBJ; } /*Get the observer*/ observer = GetObjectVar("RotateSpace", object, OBSERVER); if (observer) { 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]; /*Final axis of rotation*/ ObjPtr var; /*Temporary var*/ real eyePosn[3]; /*Observer info*/ real focusDist; real focusPoint[3]; real forward[3], up[3], side[3]; /*Observer axes*/ var = GetRealVar("RotateSpace", observer, FOCUSDIST); if (var) { focusDist = GetReal(var); } else { focusDist = INITEYEDIST; } GetFocusPoint(focusPoint, observer); GetObserverLocation(eyePosn, observer); GetAdjustedVectors(forward, up, observer); CROSS(forward, up, side); NORMALIZE(side); if (flags & F_DOUBLECLICK) { if (lights) { /*Double click. Snap lights to their closest values*/ runner = LISTOF(lights); while (runner) { if (IsSelected(runner -> thing)) { /*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); ImInvalid(runner -> thing); } } runner = runner -> next; } } else { /*It was a double click. Align forward and up to the nearest orthogonal unit vector*/ real newRot[3][3]; /*New rotation matrix*/ Quaternion rotQuat; /*New quaternion rotation matrix*/ Bool set; /*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*/ SaveForUndo(observer); /*First do forward*/ biggest = 0.0; jbiggest = -1; for (j = 0; j < 3; ++j) { if (ABS(forward[j]) > biggest) { biggest = ABS(forward[j]); jbiggest = j; } } if (jbiggest == -1) { return ObjTrue; } for (j = 0; j < 3; ++j) { if (j == jbiggest) { forward[j] = forward[j] > 0 ? 1.0 : -1.0; } else { forward[j] = 0.0; } } /*Now do up*/ biggest = 0.0; jbiggest = -1; for (j = 0; j < 3; ++j) { if (ABS(up[j]) > biggest) { biggest = ABS(up[j]); jbiggest = j; } } if (jbiggest == -1) { return ObjTrue; } for (j = 0; j < 3; ++j) { if (j == jbiggest) { up[j] = up[j] > 0 ? 1.0 : -1.0; } else { up[j] = 0.0; } } /*Make sure up is not the same as forward*/ if ((ABS(forward[0]) == ABS(up[0])) && (ABS(forward[1]) == ABS(up[1])) && (ABS(forward[2]) == ABS(up[2]))) { /*Make a different up based on forward*/ up[0] = forward[1]; up[1] = forward[2]; up[2] = forward[0]; } /*Make new variables for observer*/ eyePosn[0] = focusPoint[0] - focusDist * forward[0]; eyePosn[1] = focusPoint[1] - focusDist * forward[1]; eyePosn[2] = focusPoint[2] - focusDist * forward[2]; var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); CROSS(forward, up, side); /*Make a matrix*/ newRot[0][0] = side[0]; newRot[1][0] = side[1]; newRot[2][0] = side[2]; newRot[0][1] = up[0]; newRot[1][1] = up[1]; newRot[2][1] = up[2]; newRot[0][2] = -forward[0]; newRot[1][2] = -forward[1]; newRot[2][2] = -forward[2]; MatrixToQuaternion(newRot, rotQuat); var = NewRealArray(1, 4L); CArray2Array(var, rotQuat); SetVar(observer, ROTQUAT, var); ImInvalid(observer); if (logging) { LogObserver(observer); } } ImInvalid(object); } else { /*It's a click and drag. Do the trackball stuff.*/ real Ox, Oy; /*Center of virtual trackball*/ real r; /*Radius of virtual trackball*/ real omega; /*Ratio of the click radius to the trackball radius*/ real theta; /*Angle to the click radius*/ real tao; /*Angle from the click radius to the direction*/ real phi; /*Amount the trackball is pushed*/ int newX, newY; /*New X and Y values*/ Bool firstRun = true; /*The first run*/ real lastTime = 0.0; /*The last time*/ real cumPhi = 0.0; /*Cumulative phi*/ real curTime = 0.0; /*The current time*/ int viewType; /*The view type*/ int stereoMode; /*The stereo mode*/ if (!lights) { SaveForUndo(observer); } /*Get the view type*/ var = GetVar(observer, VIEWTYPE); if (var) { viewType = GetInt(var); } else { viewType == VT_PERSPECTIVE; } var = GetVar(observer, STEREOMODE); if (var) { stereoMode = GetInt(var); } else { stereoMode == SM_MONO; } SetVar(object, ROTATING, ObjTrue); /*Calculate origin and radius of trackball*/ if (stereoMode == SM_CROSSEYED || viewType == SM_WALLEYED) { if (x < (left + right) / 2) { Ox = (left + right) / 4; } else { Ox = 3 * (left + right) / 4; } Oy = (top + bottom) / 2; } else if (stereoMode == SM_LEFTONTOP) { Ox = (left + right) / 2; if (y < YSTEREO) { Oy = YSTEREO / 2; } else { Oy = YSTEREO * 3 / 2 + YBLANK; } } else if (stereoMode == SM_RIGHTONTOP) { Ox = (left + right) / 2; if (y < YSTEREO) { Oy = YSTEREO / 2; } else { Oy = YSTEREO * 3 / 2 + YBLANK; } } else { 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*/ real dr; /*Click axis distance*/ real sw, cw; /*Sin and cosine of omega*/ real st, ct; /*Sin and cosine of theta*/ real sp, cp; /*Sin and cosine of phi*/ real a1x, a1y, a1z; /*Temporary values for axis of rotation*/ real a2x, a2y, a2z; /*Temporary values for axis of rotation*/ real t; /*1-cos phi*/ dr = rsqrt(SQUARE((float) x) + SQUARE((float) y)); omega = f(dr / r); /*Calculate theta*/ theta = ratan2((float) (y), (float) (x)); /*Calculate tao as offset from theta*/ tao = ratan2((float) (newY - y), (float) (newX - x)) - theta; /*Calculate phi simplistically*/ phi = rsqrt(SQUARE((float) (newX - x)) + SQUARE((float) (newY - y))) / r; /*Calculate sin and cos of the angles for speed*/ sw = rsin(omega); cw = rcos(omega); st = rsin(theta); ct = rcos(theta); sp = rsin(phi); cp = rcos(phi); t = 1.0 - cp; /*Calculate the axis of rotation*/ /*First the motion from origin component*/ a1x = -rsin(tao); a1y = rcos(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 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; cumPhi = 0.0; } } /*Calculate axis based on combinations of forward, up, and side vectors*/ axis[0] = 0.0; axis[1] = 0.0; axis[2] = 0.0; axis[0] += ax * side[0]; axis[1] += ax * side[1]; axis[2] += ax * side[2]; axis[0] += ay * up[0]; axis[1] += ay * up[1]; axis[2] += ay * up[2]; axis[0] -= az * forward[0]; axis[1] -= az * forward[1]; axis[2] -= az * forward[2]; /*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; ImInvalid(observer); DrawMe(object); } else { firstRun = true; motorOn = false; } } SetVar(object, ROTATING, false); } 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 (IsSelected(runner -> thing)) { /*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*/ LogObserver(observer); } } if (!lights) { if (GetPrefTruth(PREF_ROTINERTIA) && motorOn) { SetRotationMotor((real) rotSpeed, axis[0], axis[1], axis[2]); } else { SetRotationMotor((real) 0.0, axis[0], axis[1], axis[2]); } } } #endif return ObjTrue; } static ObjPtr FingerSpace(space, x, y, flags) ObjPtr space; int x, y; long flags; /*Does a 3-D finger in a space*/ { int left, right, bottom, top; ObjPtr contents; ThingListPtr runner; FuncTyp method; Get2DIntBounds(space, &left, &right, &bottom, &top); StartSpace(space, left, right, bottom, top, PICKSPACE, 0); /*Pick the contents of the space*/ contents = GetVar(space, CONTENTS); runner = LISTOF(contents); while (runner) { if (IsObject(runner -> thing)) { PickObject(runner -> thing, PR_CENTER); method = GetMethod(runner -> thing, PICKPOINT); if (method) { (*method)(runner -> thing); } EndPickObject(); } runner = runner -> next; } StopSpace(PICKSPACE); if (0 == (flags & F_EXTEND)) { DeselectAll(); } if (spacePickedObject) { #if 0 if ((bestPickVertex) && (method = GetMethod(spacePickedObject, SELECTSELPOINT))) { if (!IsSelected(spacePickedObject)) { Select(spacePickedObject, true); } (*method)(spacePickedObject, bestPickVertex, flags); } else #endif { /*Select or deselect as a whole*/ if ((flags & F_EXTEND) && IsSelected(spacePickedObject)) { Select(spacePickedObject, false); } else { Select(spacePickedObject, true); } } return ObjTrue; } return ObjFalse; } static ObjPtr TurnDialSpace(space, whichDial, delta, flags) ObjPtr space; int whichDial; real delta; long flags; /*Turns dial whichDial in space space by delta*/ { ObjPtr var; ObjPtr observer; real axis[3]; real phi; real mv; real eyePosn[3]; real forward[3], up[3], side[3]; real clips[2]; phi = delta * DIALROTFACTOR * M_PI_2; mv = delta * DIALMOVEFACTOR * M_PI_2; observer = GetObjectVar("TurnDialSpace", space, OBSERVER); if (observer) { GetObserverLocation(eyePosn, observer); GetAdjustedVectors(forward, up, observer); CROSS(up, forward, side); switch (whichDial) { case 7: /*Motion in x direction*/ if (flags & F_OPTIONDOWN) { eyePosn[0] -= mv; } else { eyePosn[0] += side[0] * mv; eyePosn[1] += side[1] * mv; eyePosn[2] += side[2] * mv; } var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); ImInvalid(observer); break; case 6: /*Rotation about y*/ if (flags & F_OPTIONDOWN) { axis[0] = 0.0; axis[1] = 1.0; axis[2] = 0.0; RotateObserver(observer, axis, phi, false); } else { RotateObserver(observer, up, phi, false); } break; case 5: /*Motion in y direction*/ if (flags & F_OPTIONDOWN) { eyePosn[1] -= mv; } else { eyePosn[0] -= up[0] * mv; eyePosn[1] -= up[1] * mv; eyePosn[2] -= up[2] * mv; } var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); ImInvalid(observer); break; case 4: /*Rotation to top/bottom*/ if (flags & F_OPTIONDOWN) { axis[0] = -1.0; axis[1] = 0.0; axis[2] = 0.0; RotateObserver(observer, axis, phi, false); } else { RotateObserver(observer, side, phi, false); } break; case 3: /*Motion in z direction, or forward*/ if (flags & F_OPTIONDOWN) { eyePosn[2] -= mv; } else { eyePosn[0] += forward[0] * mv; eyePosn[1] += forward[1] * mv; eyePosn[2] += forward[2] * mv; var = GetVar(observer, FOCUSDIST); if (var) { SetVar(observer, FOCUSDIST, NewReal(GetReal(var) - mv)); } var = GetVar(observer, VIEWCLIP); if (var) { Array2CArray(clips, var); clips[0] -= mv; clips[1] -= mv; var = NewRealArray(1, 2L); CArray2Array(var, clips); SetVar(observer, VIEWCLIP, var); } } var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); ImInvalid(observer); break; case 2: /*Rotation about z*/ if (flags & F_OPTIONDOWN) { axis[0] = 0.0; axis[1] = 0.0; axis[2] = 1.0; RotateObserver(observer, axis, -phi, false); } else { RotateObserver(observer, forward, phi, false); } break; case 0: /*Change clipping plane*/ var = GetVar(observer, VIEWCLIP); if (var) { Array2CArray(clips, var); if (flags & F_OPTIONDOWN) { clips[1] += mv; if (clips[1] < clips[0] + 0.001) clips[1] = clips[0] + 0.001; } else { clips[0] += mv; if (clips[1] < clips[0] + 0.001) clips[0] = clips[1] - 0.001; } var = NewRealArray(1, 2L); CArray2Array(var, clips); SetVar(observer, VIEWCLIP, var); ImInvalid(observer); } break; case 1: /*Change angle or focus width*/ if (flags & F_OPTIONDOWN) { /*Change binocular spacing*/ var = GetVar(observer, BINOCULARITY); if (var) { real val; val = GetReal(var) + delta * DIALBINOCFACTOR; if (val < 0.0) val = 0.0; SetVar(observer, BINOCULARITY, NewReal(val)); ImInvalid(observer); } } else { /*Change angle or field width*/ Bool isOrtho = false; MakeVar(observer, VIEWTYPE); var = GetVar(observer, VIEWTYPE); if (var) { if (GetInt(var) == VT_ORTHOGRAPHIC) { isOrtho = true; } } var = GetVar(observer, VIEWFIELD); if (var) { if (isOrtho) { real val; val = GetReal(var) + delta * DIALWIDTHFACTOR; if (val < 0.001) val = 0.001; SetVar(observer, VIEWFIELD, NewReal(val)); } else { real val; val = GetReal(var) + delta * DIALANGLEFACTOR * 180.0; if (val < 0.1) val = 0.1; if (val > 179.0) val = 179.0; SetVar(observer, VIEWFIELD, NewReal(val)); } ImInvalid(observer); } } break; } } return ObjTrue; } static ObjPtr PressSpace(object, x, y, flags) ObjPtr object; int x, y; long flags; /*Does a press in a space beginning at x and y. Returns true iff the press really was in the space.*/ { #ifdef INTERACTIVE int left, right, bottom, top; ObjPtr tool; int spaceTool; /*Get the space tool*/ tool = GetIntVar("PressSpace", object, EDITTOOL); if (!tool) { return ObjFalse; } spaceTool = GetInt(tool); Get2DIntBounds(object, &left, &right, &bottom, &top); if (x >= left && x <= right && y >= bottom && y <= top) { if (TOOL(flags) == T_HELP) { ContextHelp(object); return ObjTrue; } /*It's a click in the space*/ switch (spaceTool) { case ST_FLYING: { /*Toggle flying*/ Bool flying; ObjPtr observer; MakeMeCurrent(object); observer = GetObjectVar("PressSpace", object, OBSERVER); if (!observer) return ObjFalse; flying = GetPredicate(observer, FLYING); flying = flying ? false : true; if (flying) { WakeMe(observer, MARKTIME, Clock() + 0.001); } else { DoNotDisturb(observer, MARKTIME); } SetVar(observer, FLYING, flying ? ObjTrue : ObjFalse); } return ObjTrue; break; case ST_FINGER: MakeMeCurrent(object); if (TOOL(flags) == T_ROTATE) { return RotateSpace(object, x, y, flags); } else { ObjPtr test; test = FingerSpace(object, x, y, flags); if (IsTrue(test)) { MoveSpace(object, x, y, flags); return ObjTrue; } } break; case ST_METER: { ObjPtr test; test = FingerSpace(object, x, y, flags); if (IsTrue(test) && bestPickVertex) { ObjPtr var; real *elements; var = NewRealArray(1, 3L); elements = ELEMENTS(var); elements[0] = bestPickVertex -> position[0]; elements[1] = bestPickVertex -> position[1]; elements[2] = bestPickVertex -> position[2]; SetVar(object, METERLOCATION, var); return ObjTrue; } } break; } return ObjFalse; } else #endif { return ObjFalse; } } int dsn = 0; #ifdef HAVE_PROTOTYPES void DrawSpaceContents(ObjPtr space, int left, int right, int bottom, int top, int viewType) #else void DrawSpaceContents(space, left, right, bottom, top, viewType) ObjPtr space; int left, right, bottom, top; int viewType; #endif { #ifdef GRAPHICS ObjPtr contents; Bool anyTransparent; /*True iff any objects are transparent*/ ThingListPtr drawList; /*List of objects to draw*/ ObjPtr frontPanel; int prefDrawMoving; /*Preferred draw moving style*/ ObjPtr var; Bool isCached; ObjPtr cache; if (windowSystem == WS_GLWINDOWS) { frontPanel = GetVar(space, FRONTPANEL); if (frontPanel && (GetVar(frontPanel, BACKGROUND) || GetPredicate(frontPanel, TEMPOBSCURED))) { /*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); /*Figure out the range of drawing qualities to use.*/ if (interactiveMoving) { /*It's interactive. Figure out what quality to use*/ prefDrawMoving = GetPrefInteger(PREF_DRAWMOVING); if (prefDrawMoving == DM_SKELETON) { minDrawingQuality = DQ_MIN; maxDrawingQuality = DQ_OUTLINE; } else if (prefDrawMoving == DM_FULL) { minDrawingQuality = DQ_MIN; maxDrawingQuality = DQ_MAX; } else if (prefDrawMoving == DM_AUTO) { var = GetVar(space, DRAWINGTIME); if (var) { long drawingTime; /*There is a drawing time recorded. Get it*/ drawingTime = GetInt(var); if (drawingTime < MAXMOVETIME) { /*It's fast enough, might as well do it fully*/ minDrawingQuality = DQ_MIN; maxDrawingQuality = DQ_MAX; } else { /*Do outline*/ minDrawingQuality = DQ_MIN; maxDrawingQuality = DQ_OUTLINE; } } else { /*Assume we have to do full drawing*/ minDrawingQuality = DQ_MIN; maxDrawingQuality = DQ_MAX; } } } else { /*Not interactive. Just use full quality*/ minDrawingQuality = DQ_MIN; maxDrawingQuality = DQ_MAX; } /*Draw the contents of the space*/ contents = GetVar(space, CONTENTS); /*First draw opaque objects*/ anyTransparent = false; drawList = LISTOF(contents); drawingTransparent = false; while (drawList) { if (IsObject(drawList -> thing)) { if (GetPredicate(drawList -> thing, ISTRANSPARENT)) { anyTransparent = true; } qualityDrawnSoFar = DQ_MIN; if (maxDrawingQuality >= DQ_FULL && GetPredicate(drawList -> thing, CACHEGRAPHICS)) { cacheMade = false; MakeVar(drawList -> thing, CACHEDOPAQUE); if (cacheMade) { cache = GetVar(drawList -> thing, CACHEDOPAQUE); if (cache) { OpenGraphObj(cache); } else { cache = NewOpenGraphObj(); SetVar(drawList -> thing, CACHEDOPAQUE, cache); } DrawVisObject(drawList -> thing); CloseGraphObj(cache); DrawGraphObj(cache); } else { /*Just draw it*/ cache = GetVar(drawList -> thing, CACHEDOPAQUE); if (cache) { DrawGraphObj(cache); } else { DrawVisObject(drawList -> thing); } } } else { DrawVisObject(drawList -> thing); } if (maxDrawingQuality < DQ_FULL) { NeedToDrawFullQuality(drawList -> thing); } } drawList = drawList -> next; } /*Now draw transparent objects*/ if (anyTransparent && rgbp && maxDrawingQuality >= DQ_FULL) { BeginMask(true, true, true, false); blendfunction(BF_ONE, BF_ONE); drawingTransparent = true; drawList = LISTOF(contents); while (drawList) { minDrawingQuality = DQ_FULL; maxDrawingQuality = DQ_FULL; if (IsObject(drawList -> thing)) { qualityDrawnSoFar = DQ_MIN; if (maxDrawingQuality >= DQ_FULL && GetPredicate(drawList -> thing, CACHEGRAPHICS)) { cacheMade = false; MakeVar(drawList -> thing, CACHEDTRANSPARENT); if (cacheMade) { cache = GetVar(drawList -> thing, CACHEDTRANSPARENT); if (cache) { OpenGraphObj(cache); } else { cache = NewOpenGraphObj(); SetVar(drawList -> thing, CACHEDTRANSPARENT, cache); } DrawVisObject(drawList -> thing); CloseGraphObj(cache); DrawGraphObj(cache); } else { /*Just draw it*/ cache = GetVar(drawList -> thing, CACHEDTRANSPARENT); if (cache) { DrawGraphObj(cache); } else { DrawVisObject(drawList -> thing); } } } else { DrawVisObject(drawList -> thing); } if (maxDrawingQuality < DQ_FULL) { NeedToDrawFullQuality(drawList -> thing); } } drawList = drawList -> next; } blendfunction(BF_ONE, BF_ZERO); EndMask(); drawingTransparent = false; } if (maxDrawingQuality >= DQ_FULL) { /*End the timing of the full drawing*/ SetVar(space, DRAWINGTIME, NewInt(GetPictureTime())); } StopSpace(DRAWSPACE); #endif } #ifdef HAVE_PROTOTYPES 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 phi around axis. If constrain, constrains axis as a side effect.*/ { ObjPtr var; real focusPoint[3]; real location[3]; real forwardVector[3]; real focusDist; Quaternion rotQuat; Quaternion deltaRotQuat; Quaternion newRot; /*Now that there's a new delta, rotate and redraw*/ 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] * Identity[k][0] + axis[1] * Identity[k][1] + axis[2] * Identity[k][2]; if (ABS(curDot) > ABS(maxDot)) { /*It's a better choice*/ maxDot = curDot; best = k; } } /*Now we have a best match*/ axis[0] = Identity[best][0]; axis[1] = Identity[best][1]; axis[2] = Identity[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; } /*Get current focus point*/ GetFocusPoint(focusPoint, observer); /*Now make the change rotation quaternion*/ RotToQuaternion(axis, phi, deltaRotQuat); /*Apply it to the rotation quaternion*/ MakeVar(observer, ROTQUAT); var = GetVar(observer, ROTQUAT); if (!var) { ReportError("RotateObserver", "No rotation quaternion"); return; } Array2CArray(rotQuat, var); QMult(rotQuat, deltaRotQuat, newRot); var = NewRealArray(1, 4L); CArray2Array(var, newRot); SetVar(observer, ROTQUAT, var); /*Get new forward vector*/ GetForwardVector(forwardVector, observer); var = GetVar(observer, FOCUSDIST); if (var) { focusDist = GetReal(var); } else { focusDist = INITEYEDIST; } /*Make a new location*/ location[0] = focusPoint[0] - forwardVector[0] * focusDist; location[1] = focusPoint[1] - forwardVector[1] * focusDist; location[2] = focusPoint[2] - forwardVector[2] * focusDist; /*Put back the location vector*/ var = NewRealArray(1, 3L); CArray2Array(var, location); SetVar(observer, LOCATION, var); ImInvalid(observer); return true; } void PrintMatrix(real m[4][4]) /*Prints a matrix*/ { printf("%10g %10g %10g %10g\n", m[0][0], m[0][1], m[0][2], m[0][3]); printf("%10g %10g %10g %10g\n", m[1][0], m[1][1], m[1][2], m[1][3]); printf("%10g %10g %10g %10g\n", m[2][0], m[2][1], m[2][2], m[2][3]); printf("%10g %10g %10g %10g\n", m[3][0], m[3][1], m[3][2], m[3][3]); } #ifdef HAVE_PROTOTYPES void RotToQuaternion(real axis[3], real amount, Quaternion r) #else void RotToQuaternion(axis, amount, r) real axis[3]; real amount; Quaternion r; #endif /*Converts a rotation by amount around axis to a quaternion*/ { real absVal, multiplier1, multiplier2; absVal = sqrt(SQUARE(axis[0]) + SQUARE(axis[1]) + SQUARE(axis[2])); if (absVal > 0.0) { multiplier1 = rsin(amount * 0.5) / absVal; multiplier2 = rcos(amount * 0.5); r[0] = multiplier2; r[1] = axis[0] * multiplier1; r[2] = axis[1] * multiplier1; r[3] = axis[2] * multiplier1; } else { r[0] = 1.0; r[1] = 0.0; r[2] = 0.0; r[3] = 0.0; } } #ifdef HAVE_PROTOTYPES void QuaternionToRot(Quaternion r, real axis[3], real *amount) #else void QuaternionToRot(r, axis, amount) real axis[3]; real *amount; Quaternion r; #endif /*Converts a quaternion to a rotation by amount around axis*/ { real absVal, multiplier1, multiplier2; absVal = sqrt(SQUARE(r[1]) + SQUARE(r[2]) + SQUARE(r[3])); if (absVal > 0.0) { *amount = racos(r[0]) * 2; axis[0] = r[1] / absVal; axis[1] = r[2] / absVal; axis[2] = r[3] / absVal; } else { *amount = 0; axis[0] = 0.0; axis[1] = 0.0; axis[2] = 1.0; } } #ifdef HAVE_PROTOTYPES void MatrixToQuaternion(real m[3][3], Quaternion q) #else void MatrixToQuaternion(m, q) real m[3][3]; Quaternion q; #endif /*Converts a matrix (rotation only) to a quaternion*/ { real greatestSquare; char whichGreatest; real square; real dividend; #undef w #undef x #undef y #undef z #define w q[0] #define x q[1] #define y q[2] #define z q[3] /*Solving along diagonal, see which is greatest*/ greatestSquare = 0.0; whichGreatest = ' '; /*Test solving for x*/ if ((square = 1.0 - m[2][2] + m[0][0] - m[1][1]) > greatestSquare) { greatestSquare = square; whichGreatest = 'x'; } /*Test solving for y*/ if ((square = 1.0 - m[0][0] + m[1][1] - m[2][2]) > greatestSquare) { greatestSquare = square; whichGreatest = 'y'; } /*Test solving for z*/ if ((square = 1.0 - m[1][1] + m[2][2] - m[0][0]) > greatestSquare) { greatestSquare = square; whichGreatest = 'z'; } if (whichGreatest == 'x') { /*Arbitrarily choose +x*/ x = sqrt(greatestSquare) * 0.5; dividend = 1.0 / (4.0 * x); /*Calculate y*/ y = (m[0][1] + m[1][0]) * dividend; /*Calculate z*/ z = (m[0][2] + m[2][0]) * dividend; /*Calculate w*/ w = 2.0 * (2.0 * y * z - m[2][1]) * dividend; } else if (whichGreatest == 'y') { /*Arbitrarily choose +y*/ y = sqrt(greatestSquare) * 0.5; dividend = 1.0 / (4.0 * y); /*Calculate x*/ x = (m[0][1] + m[1][0]) * dividend; /*Calculate z*/ z = (m[2][1] + m[1][2]) * dividend; /*Calculate w*/ w = 2.0 * (2.0 * x * z - m[0][2]) * dividend; } else if (whichGreatest == 'z') { /*Arbitrarily choose +z*/ z = sqrt(greatestSquare) * 0.5; dividend = 1.0 / (4.0 * z); /*Calculate x*/ x = (m[0][2] + m[2][0]) * dividend; /*Calculate y*/ y = (m[2][1] + m[1][2]) * dividend; /*Calculate w*/ w = 2.0 * (2.0 * x * y - m[1][0]) * dividend; } else { /*It has to be [1, [0 0 0]]*/ q[0] = 1.0; q[1] = 0.0; q[2] = 0.0; q[3] = 0.0; } #undef w #undef x #undef y #undef z } void QInvert(Quaternion s, Quaternion r) /*Inverts quaternion s into result r, assuming |s| == 1*/ { r[0] = s[0]; r[1] = -s[1]; r[2] = -s[2]; r[3] = -s[3]; } void QMult(Quaternion q1, Quaternion q2, Quaternion r) /*Multiplies two quaternions giving a third*/ { real qp[3]; r[0] = q1[0] * q2[0] - (q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3]); CROSS(&(q1[1]), &(q2[1]), qp); r[1] = q1[0] * q2[1] + q2[0] * q1[1] + qp[0]; r[2] = q1[0] * q2[2] + q2[0] * q1[2] + qp[1]; r[3] = q1[0] * q2[3] + q2[0] * q1[3] + qp[2]; } void QDouble(Quaternion q1, Quaternion q2, Quaternion r) /*Does the Double operation, but not like [Shoemake, 1985]*/ { Quaternion qtemp1, qtemp2; #if 0 /*This is from [Shoemake, 1985]. I don't understand the logic, and also, it doesn't work.*/ QMult(q1, q2, qtemp1); QMult(qtemp1, q2, qtemp2); r[0] = qtemp2[0] * 2.0 - q1[0]; r[1] = qtemp2[1] * 2.0 - q1[1]; r[2] = qtemp2[2] * 2.0 - q1[2]; r[3] = qtemp2[3] * 2.0 - q1[3]; #else /*This, however, works fine*/ QInvert(q1, qtemp1); QMult(q2, qtemp1, qtemp2); QMult(qtemp2, q2, r); #endif } void Slerp(Quaternion q1, Quaternion q2, real u, Quaternion r) /*Does the slerp operation in [Shoemake 1985]*/ { real theta, costheta, w1, w2, sintheta; costheta = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3]; theta = racos(costheta); sintheta = rsin(theta); if (sintheta > 0.0) { w1 = rsin((1.0 - u) * theta) / sintheta; w2 = rsin(u * theta) / sintheta; } else { /*They're the same quaternion, so who cares?*/ w1 = 1.0; w2 = 0.0; } r[0] = w1 * q1[0] + w2 * q2[0]; r[1] = w1 * q1[1] + w2 * q2[1]; r[2] = w1 * q1[2] + w2 * q2[2]; r[3] = w1 * q1[3] + w2 * q2[3]; } #ifdef HAVE_PROTOTYPES void QBisect(Quaternion q1, Quaternion q2, Quaternion r) #else void QBisect(q1, q2, r) Quaternion q1, q2, r; #endif /*Does the Bisect operation in [Shoemake, 1985]*/ { real absVal; #if 1 r[0] = q1[0] + q2[0]; r[1] = q1[1] + q2[1]; r[2] = q1[2] + q2[2]; r[3] = q1[3] + q2[3]; absVal = sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]); r[0] /= absVal; r[1] /= absVal; r[2] /= absVal; r[3] /= absVal; #else Slerp(q1, q2, 0.5, r); #endif } #ifdef HAVE_PROTOTYPES void QRot(real v[3], Quaternion q, real r[3]) #else void QRot(v, q, r) real v[3]; Quaternion q; real r[3]; #endif /*Rotates v by q into r*/ { Quaternion qInv, qTemp; Quaternion qVec, qRes; QInvert(q, qInv); qVec[0] = 0.0; qVec[1] = v[0]; qVec[2] = v[1]; qVec[3] = v[2]; QMult(qInv, qVec, qTemp); QMult(qTemp, q, qRes); r[0] = qRes[1]; r[1] = qRes[2]; r[2] = qRes[3]; } void QDelta(Quaternion q1, Quaternion q2, Quaternion r) /*Put in r the quaternion such that q1 r = q2*/ { Quaternion q1inv; QInvert(q1, q1inv); QMult(q1inv, q2, r); } TestObserver() /*Does an observer test in the current window*/ { ObjPtr observer; real *forward, *up; if (selWinInfo) { observer = FindObserver(selWinInfo); if (observer) { ObjPtr var; real *elements; real side[3], m[4][4]; Quaternion q, qInv, qTest, qVec; var = GetVar(observer, LOCATION); if (var) { forward = (real *) ELEMENTS(var); printf("Location = [%g %g %g]\n", forward[0], forward[1], forward[2]); } var = GetVar(observer, FORWARDVECTOR); if (var) { forward = (real *) ELEMENTS(var); printf("Forward = [%g %g %g]\n", forward[0], forward[1], forward[2]); } var = GetVar(observer, UPVECTOR); if (var) { up = (real *) ELEMENTS(var); printf("Up = [%g %g %g]\n", up[0], up[1], up[2]); } /* generate the vector side from the * 2 vectors view forward and world up */ CROSS(forward, up, side); NORMALIZE(side); m[0][0] = side[0]; m[1][0] = side[1]; m[2][0] = side[2]; m[3][0] = 0.0; m[0][1] = up[0]; m[1][1] = up[1]; m[2][1] = up[2]; m[3][1] = 0.0; m[0][2] = -forward[0]; m[1][2] = -forward[1]; m[2][2] = -forward[2]; m[3][2] = 0.0; m[0][3] = 0.0; m[1][3] = 0.0; m[2][3] = 0.0; m[3][3] = 1.0; #if 0 printf("Lookat matrix = \n"); PrintMatrix(m); /*Now try to make quaternion*/ MatrixToQuaternion(m, q); printf("Rotation quaternion = %g %g %g %g\n", q[0], q[1], q[2], q[3]); printf("Absolute value squared = %g\n", q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); QInvert(q, qInv); printf("Inverted = %g %g %g %g\n", qInv[0], qInv[1], qInv[2], qInv[3]); qVec[0] = 0.0; qVec[1] = 0.0; qVec[2] = 0.0; qVec[3] = -1.0; QMult(qInv, qVec, qTest); QMult(qTest, q, qVec); printf("Rotated forward = %g %g %g %g\n", qVec[0], qVec[1], qVec[2], qVec[3]); qVec[0] = 0.0; qVec[1] = 0.0; qVec[2] = 1.0; qVec[3] = 0.0; QMult(qInv, qVec, qTest); QMult(qTest, q, qVec); printf("Rotated up = %g %g %g %g\n", qVec[0], qVec[1], qVec[2], qVec[3]); #endif } } } #ifdef HAVE_PROTOTYPES 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*/ { ThingListPtr runner; Matrix rotDelta; real axis[3]; float t; float sp, cp; /*Sin and cosine of phi*/ runner = LISTOF(lights); while (runner) { if (IsSelected(runner -> thing)) { /*Rotate the light*/ ObjPtr var; real location[3]; real newLoc[3]; float tv[3]; Quaternion qRot, qInv; 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); if (GetPredicate(runner -> thing, LIGHTBYOBSERVER)) { var = GetVar(observer, ROTQUAT); if (var) { Array2CArray(qRot, var); QRot(location, qRot, location); } } /*Test elevation*/ tv[0] = location[1]; tv[1] = -location[0]; tv[2] = 0.0; if (constrain) { float dot1, dot2; float rr; /*Constrain the axis to the azimuth or elevation*/ /*Test azimuth*/ dot1 = axis[2]; /*Test elevation*/ 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] = 0.0; axis[1] = 0.0; axis[2] = 1.0; 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]; NORM3(newLoc); if (GetPredicate(runner -> thing, LIGHTBYOBSERVER)) { QInvert(qRot, qInv); QRot(newLoc, qRot, newLoc); } var = NewRealArray(1, 3L); CArray2Array(var, newLoc); SetVar(runner -> thing, LOCATION, var); ImInvalid(runner -> thing); } runner = runner -> next; } return true; } int spaceDraw = 1; ObjPtr SpacePanelBoundsInvalid(object, changeCount) ObjPtr object; unsigned long changeCount; /*For a space panel, tests to see if it needs drawing. Returns NULLOBJ if it does not array[4] giving bounds if it does ObjTrue it it needs to be redrawn but does not know where Contents are assumed to be shifted in the bounds of owner */ { ObjPtr contents; ObjPtr myBounds; real boundsElements[4]; MakeVar(object, APPEARANCE); MakeVar(object, CHANGEDBOUNDS); if (GetVarChangeCount(object, CHANGEDBOUNDS) > changeCount) { /*Object is not good, so return the bounds*/ myBounds = GetVar(object, CHANGEDBOUNDS); return myBounds; } myBounds = GetVar(object, BOUNDS); MakeVar(object, CONTENTS); contents = GetVar(object, CONTENTS); if (contents && IsList(contents)) { /*Still, maybe some of the contents need to be drawn*/ real testElements[4]; real myBoundsElements[4]; ObjPtr test; ThingListPtr runner; MakeVar(object, BOUNDS); myBounds = GetVar(object, BOUNDS); Array2CArray(myBoundsElements, myBounds); runner = LISTOF(contents); while (runner) { test = BoundsInvalid(runner -> thing, changeCount); if (test) { /*Hey, the kid needs redrawing*/ return myBounds; } runner = runner -> next; } } return NULLOBJ; } ObjPtr SpaceBoundsInvalid(object, changeCount) ObjPtr object; unsigned long changeCount; /*For a space, tests to see if it needs drawing. Returns NULLOBJ if it does not array[4] giving bounds if it does ObjTrue it it needs to be redrawn but does not know where */ { ObjPtr contents; ObjPtr lights, observer, renderer; ObjPtr myBounds; real boundsElements[4]; real testElements[4]; ObjPtr test; real myBoundsElements[4]; ObjPtr scrollBar; Bool firstTime = true; Bool doubleNoBounds = false; ObjPtr retVal = NULLOBJ; ObjPtr time; MakeVar(object, TIME); MakeVar(object, APPEARANCE); MakeVar(object, BOUNDS); myBounds = GetFixedArrayVar("SpaceBoundsInvalid", object, BOUNDS, 1, 4L); Array2CArray(myBoundsElements, myBounds); if (GetVarChangeCount(object, CHANGEDBOUNDS) > changeCount) { /*Object is not good, so return the bounds*/ ObjPtr myBounds; MakeVar(object, CHANGEDBOUNDS); myBounds = GetVar(object, CHANGEDBOUNDS); if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1 && DIMS(myBounds)[0] == 4) { retVal = myBounds; } else { retVal = ObjTrue; } } time = GetVar(object, TIME); if (time) { spaceTime = GetReal(time); } SetAllDatasetTimes(spaceTime); MakeVar(object, CONTENTS); contents = GetVar(object, CONTENTS); if (contents && IsList(contents)) { /*Still, maybe some of the contents need to be drawn*/ ThingListPtr runner; runner = LISTOF(contents); while (runner) { test = BoundsInvalid(runner -> thing, changeCount); if (test) { /*Hey, the kid needs redrawing*/ retVal = myBounds; } runner = runner -> next; } } MakeVar(object, LIGHTS); lights = GetVar(object, LIGHTS); if (lights && IsList(lights)) { /*Still, maybe some of the lights need to be drawn*/ ThingListPtr runner; runner = LISTOF(lights); while (runner) { test = BoundsInvalid(runner -> thing, changeCount); if (test) { /*Hey, the light needs redrawing*/ retVal = myBounds; } runner = runner -> next; } } MakeVar(object, OBSERVER); observer = GetVar(object, OBSERVER); if (observer) { test = BoundsInvalid(observer, changeCount); if (test) { /*Hey, the observer needs redrawing*/ retVal = myBounds; } } MakeVar(object, RENDERER); renderer = GetVar(object, RENDERER); if (renderer) { test = BoundsInvalid(renderer, changeCount); if (test) { /*Hey, the renderer needs redrawing*/ retVal = myBounds; } } return retVal; } ObjPtr DrawSpace(object) ObjPtr object; /*Draws a space and everything it contains*/ { #ifdef GRAPHICS 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 var; /*A random variable*/ int renderType, filterType; /*Types of renderers and filters*/ int viewType; /*Type of view*/ int stereoMode; /*Stereo mode*/ #if 0 printf("DrawSpace %d\n", spaceDraw++); #endif /*Get render type*/ MakeVar(object, RENDERTYPE); var = GetIntVar("DrawSpace", object, RENDERTYPE); if (var) { renderType = GetInt(var); } else { renderType = hasRGB ? RT_RGB_HARDWARE : RT_CMAP_HARDWARE; } if (renderType == RT_NONE) { /*Don't render*/ return ObjTrue; } /*Get filter type*/ MakeVar(object, FILTERTYPE); var = GetIntVar("DrawSpace", object, FILTERTYPE); if (var) { filterType = GetInt(var); } else { filterType = FT_NONE; } Get2DIntBounds(object, &left, &right, &bottom, &top); if (windowSystem == WS_GLWINDOWS) { if (IsDrawingRestricted(left, right, bottom, top)) return ObjFalse; } /*Update the rotation matrix and fly through*/ observer = GetObjectVar("DrawSpace", object, OBSERVER); MakeVar(observer, VIEWTYPE); var = GetVar(observer, VIEWTYPE); if (var) { viewType = GetInt(var); } else { viewType = VT_PERSPECTIVE; } MakeVar(observer, STEREOMODE); var = GetVar(observer, STEREOMODE); if (var) { stereoMode = GetInt(var); } else { stereoMode = SM_MONO; } if (drawingMode == DRAW_SCREEN) { switch(stereoMode) { case SM_MONO: DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); break; case SM_CROSSEYED: DrawSpaceContents(object, left, right, bottom, top, VIEW_XLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_XRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); break; case SM_EVENODD: DrawSpaceContents(object, left, right, bottom, top, VIEW_EOLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_EORIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); break; case SM_ODDEVEN: DrawSpaceContents(object, left, right, bottom, top, VIEW_OELEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_OERIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); break; case SM_WALLEYED: DrawSpaceContents(object, left, right, bottom, top, VIEW_WLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_WRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); break; case SM_LEFTONTOP: DrawSpaceContents(object, left, right, bottom, top, VIEW_CLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_CRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); break; case SM_REDCYAN: if (rgbp) { DrawSpaceContents(object, left, right, bottom, top, VIEW_RCLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_RCRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } else { DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } break; case SM_CYANRED: if (rgbp) { DrawSpaceContents(object, left, right, bottom, top, VIEW_CRLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_CRRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } else { DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } break; case SM_REDGREEN: if (rgbp) { DrawSpaceContents(object, left, right, bottom, top, VIEW_RGLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_RGRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } else { DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } break; case SM_GREENRED: if (rgbp) { DrawSpaceContents(object, left, right, bottom, top, VIEW_GRLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); DrawSpaceContents(object, left, right, bottom, top, VIEW_GRRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } else { DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0)); } 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; Pixel *imageBuffer; 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); /*Create an image buffer*/ imageBuffer = (Pixel *) Alloc(SCRWIDTH * SCRHEIGHT * sizeof(Pixel)); if (imageBuffer) { lrectread((Screencoord) left, (Screencoord) bottom, (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) 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; } EraseAll(); lrectwrite((Screencoord) left, (Screencoord) bottom, (Screencoord) left + xdd - 1, (Screencoord) bottom + ydd - 1, (unsigned long *) imageBuffer); SAFEFREE(imageBuffer); } } if (rgbp && drawingQuality == DQ_FULL && filterType == FT_4AVERAGE) { /*Average the pixels in the window*/ int s; register int y, x; int xSize, ySize; Pixel *imageBuffer; if (right - left > SCRWIDTH) right = left + SCRWIDTH; if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT; xSize = (right - left); ySize = (top - bottom); /*Create an image buffer*/ imageBuffer = (Pixel *) Alloc(SCRWIDTH * SCRHEIGHT * sizeof(Pixel)); if (imageBuffer) { lrectread((Screencoord) left, (Screencoord) bottom, (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) 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((Screencoord) left, (Screencoord) bottom, (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) imageBuffer); SAFEFREE(imageBuffer); } } } else if (drawingMode == DRAW_POSTSCRIPT && psFile) { /*Just save the image to a PostScript file*/ register int y, x; int xSize, ySize; Pixel *imageBuffer; if (right - left > SCRWIDTH) right = left + SCRWIDTH; if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT; xSize = (right - left); ySize = (top - bottom); /*Create an image buffer*/ imageBuffer = (Pixel *) Alloc(xSize * ySize * sizeof(Pixel)); if (imageBuffer) { lrectread((Screencoord) left, (Screencoord) bottom, (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) imageBuffer); SavePSImage(psFile, imageBuffer, left, right, bottom, top); SAFEFREE(imageBuffer); } } #endif return ObjTrue; } static ObjPtr KeyDownSpace(object, key, flags) ObjPtr object; int key; long flags; /*Does a keydown in a space. Returns true iff the press really was in the space.*/ { ObjPtr retVal, tool, var; /*Get the space tool*/ tool = GetIntVar("KeyDownSpace", object, EDITTOOL); if (tool && GetInt(tool) == ST_FLYING) { ObjPtr observer; observer = GetObjectVar("KeyDownSpace", object, OBSERVER); if (!observer) { return ObjFalse; } switch(key) { case '0': SetVar(observer, AIRSPEED, NewReal(0.0)); return ObjTrue; case FK_UP_ARROW: var = GetVar(observer, AIRSPEED); if (var) { SetVar(observer, AIRSPEED, NewReal(AIRSPEEDFACTOR + GetReal(var))); } else { SetVar(observer, AIRSPEED, NewReal(AIRSPEEDFACTOR)); } return ObjTrue; case FK_DOWN_ARROW: var = GetVar(observer, AIRSPEED); if (var) { SetVar(observer, AIRSPEED, NewReal(GetReal(var) - AIRSPEEDFACTOR)); } else { SetVar(observer, AIRSPEED, NewReal(-AIRSPEEDFACTOR)); } return ObjTrue; } } return ObjFalse; } 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 object is a template, make a new copy*/ if (GetPredicate(object, TEMPLATEP)) { FuncTyp method; method = GetMethodSurely("AddObjToSpace", object, CLONE); if (method) { object = (*method)(object); } SetVar(object, TEMPLATEP, ObjFalse); SetVar(object, SELECTED, ObjFalse); } if (IsIcon(object)) { /*It's an icon; got to find a REPOBJ*/ object = GetVar(object, REPOBJ); if (!object) { return false; } } if (IsFile(object)) { /*It's a file. Whine*/ WarnUser(CW_CANNOTVISFILE); return false; } if (IsVisObj(object) && visType) { /*Go down to dataset level if visObject*/ repObj = GetVar(object, MAINDATASET); if (!repObj) { repObj = GetVar(object, REPOBJ); } object = repObj; } if (!IsVisObj(object)) { /*It's not a visualization yet. Gotta find one*/ /*Always apply some filters*/ repObj = MainFilters(object); object = NewVis(repObj, visType); if (!object) { WarnUser(CW_CANNOTVISERROR); return false; } if (OptionDown()) { SetVar(object, HIDDEN, ObjTrue); } } else { repObj = GetVar(object, MAINDATASET); if (!repObj) { repObj = GetVar(object, REPOBJ); } } 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 (OptionDown()) { SetVar(object, HIDDEN, ObjTrue); } DropIconInCorral(corral, icon); } method = GetMethod(object, DROPPEDINSPACE); if (method) { (*method)(object, space); } /*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*/ { SetVar(space, OBSERVER, observer); 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; if (GetPredicate(controller, ONEONLY) && (controllerClass = GetVar(controller, CLASSID)) && IsInt(controllerClass)) { int cc; /*First see if there is already a controller there*/ cc = GetInt(controllerClass); contents = GetListVar("AddControllerToSpace", corral, CONTENTS); if (!contents) return false; list = LISTOF(contents); while (list) { ObjPtr foundController; foundController = GetVar(list -> thing, REPOBJ); if (IsController(foundController) && IntVarEql(foundController, CLASSID, cc)) { 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.")); } SetVar(icon, SPACE, space); SetVar(icon, REPOBJ, controller); method = GetMethod(controller, BINDTOSPACE); if (method) { (*method)(controller, space); } SetVar(icon, ICONLOC, loc); /*Make the controller know about this space*/ spaces = GetListVar("AddControllerToSpace", controller, SPACES); if (!spaces) return false; PrefixList(spaces, space); ImInvalid(controller); DropIconInCorral(corral, icon); return true; } ObjPtr NewSpace(left, right, bottom, top) int left, right, bottom, top; /*Makes a new Space with bounds left, right, bottom, top*/ { ObjPtr retVal; retVal = NewObject(spaceClass, 0); Set2DIntBounds(retVal, left, right, bottom, top); SetVar(retVal, CONTENTS, NewList()); return retVal; } #ifdef HAVE_PROTOTYPES 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 = floor(t / 3600.0); i = n / 12.0; n = n - i * 12; if (n < 1.00) n += 12.00; break; case 'H': /*Unrestricted hours*/ n = t / 3600.0; break; case 'i': /*Hours, [0,24)*/ n = floor(t / 3600.0); i = n / 24.0; n = n - i * 24; break; case 'a': /*am or pm*/ n = t / 3600.0; i = n; i = i % 24; sprintf(s, "%s", i >= 12 ? "pm" : "am"); ++f; goto dontsprintf; break; case 'A': /*AM or PM*/ n = t / 3600.0; i = n / 24.0; i = i % 24; sprintf(s, "%s", i >= 12 ? "PM" : "AM"); ++f; goto dontsprintf; break; case 'M': /*Unrestricted minutes*/ n = t / 60.0; break; case 'm': /*Minutes, 0 to 59*/ n = floor(t / 60.0); i = n / 60.0; n = n - i * 60; break; case 's': /*Seconds, 0 to 59*/ i = t / 60.0; n = t - i * 60; n = floor(n); break; case 'S': /*Unrestricted seconds*/ case 't': case 'T': /*Just a timestep*/ n = t; break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': *temp++ = *f++; *temp = 0; sprintf(s, tempStr, t); goto dontsprintf; break; case 'x': case 'X': case 'd': case 'D': *temp++ = *f++; *temp = 0; sprintf(s, tempStr, (int) t); goto dontsprintf; 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; } void ChangeClockForDataset(window) WinInfoPtr window; /*Changes a clock in window for globalDataset*/ { ObjPtr space, clock; ObjPtr timeBounds; real oldtb[2], tb[2]; ObjPtr contents; ObjPtr repObj; ThingListPtr runner; space = FindSpace(window); if (!space) { ReportError("ChangeClockForDataset", "No space in window"); return; } clock = GetObjectVar("ChangeClockForDataset", space, CLOCK); if (!clock) { return; } timeBounds = GetVar(clock, TIMEBOUNDS); if (timeBounds) { Array2CArray(oldtb, timeBounds); } else { oldtb[0] = oldtb[1] = missingData; } contents = GetListVar("ChangeClockForDataset", space, CONTENTS); if (!contents) { return; } tb[0] = tb[1] = missingData; runner = LISTOF(contents); while (runner) { repObj = GetVar(runner -> thing, MAINDATASET); if (!repObj) repObj = GetObjectVar("ChangeClockForDataset", runner -> thing, REPOBJ); if (repObj) { ObjPtr objTime; real otb[2]; MakeVar(repObj, TIMEBOUNDS); objTime = GetVar(repObj, TIMEBOUNDS); if (objTime) { Array2CArray(otb, objTime); if (tb[0] == missingData) { tb[0] = otb[0]; } else { tb[0] = MIN(tb[0], otb[0]); } if (tb[1] == missingData) { tb[1] = otb[1]; } else { tb[1] = MAX(tb[1], otb[1]); } } } runner = runner -> next; } if (tb[1] != oldtb[1] || tb[0] != oldtb[0]) { if (tb[1] != missingData && tb[0] != missingData) { timeBounds = NewRealArray(1, 2L); CArray2Array(timeBounds, tb); SetVar(clock, TIMEBOUNDS, timeBounds); ImInvalid(clock); } else { SetVar(clock, TIMEBOUNDS, NULLOBJ); } } } ObjPtr MakeClockTimeBounds(clock) ObjPtr clock; /*Makes a clock's time bounds for the first time*/ { ForAllVisWindows(ChangeClockForDataset); SetVar(clock, TIMEBOUNDS, GetVar(clock, TIMEBOUNDS)); return ObjTrue; } static ObjPtr MakeSpaceFilterType(space) ObjPtr space; /*Makes a space's filter type*/ { ObjPtr renderer; renderer = GetObjectVar("MakeSpaceFilterType", space, RENDERER); if (!renderer) { return ObjFalse; } SetVar(space, FILTERTYPE, GetVar(renderer, FILTERTYPE)); return ObjTrue; } static ObjPtr MakeSpaceRenderType(space) ObjPtr space; /*Makes a space's RENDERTYPE. Returns ObjTrue if it had an effect, ObjFalse otherwise.*/ { ObjPtr renderer; ObjPtr spaceRenderType, renderType; renderer = GetObjectVar("MakeSpaceRenderType", space, RENDERER); if (!renderer) { return ObjFalse; } spaceRenderType = GetVar(space, RENDERTYPE); renderType = GetVar(renderer, RENDERTYPE); if (!Equal(spaceRenderType, renderType)) { WinInfoPtr window; window = (WinInfoPtr) GetWindowVar("MakeSpaceRenderType", space, OWNERWINDOW); if (!window) return ObjTrue; switch(GetInt(renderType)) { case RT_RGB_HARDWARE: DeferMessage((ObjPtr) window, SETTORGBMODE); break; case RT_CMAP_HARDWARE: DeferMessage((ObjPtr) window, SETTOCMAPMODE); break; } } SetVar(space, RENDERTYPE, renderType); return ObjTrue; } 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); } } 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)); 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 (IsIcon(dropObj)) { 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 ObjFalse; 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 (IsClock(repObj)) { } else if (IsPalette(repObj)) { AddPaletteToSpacePanel(repObj, panel, space, x, y); } /*Drop remaining icons*/ runner = restIcons; while (runner) { repObj = GetVar(runner -> thing, REPOBJ); if (IsClock(repObj)) { } 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]); } runner = runner -> next; } StopPanel(); return ObjTrue; } else { return ObjFalse; } } #ifdef HAVE_PROTOTYPES 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)); ImInvalid(clock); } static ObjPtr MarkClockTime(clock, lateness) ObjPtr clock; double lateness; /*Marks time in a clock after a delay of lateness*/ { ObjPtr curTime; ObjPtr var; ObjPtr timeBounds; real deltaTime, deltaTimePer; int deltaTimeUnits, deltaTimePerUnits; real time; real tb[2]; ObjPtr name; ObjPtr whichDialog; WinInfoPtr dialog; ObjPtr spaces; ThingListPtr list; Bool wrap; ObjPtr runSpeed, runControl; DoNotDisturb(clock, MARKTIME); runControl = GetVar(clock, RUNCONTROL); runSpeed = GetVar(clock, RUNSPEED); if (!runSpeed) { return ObjTrue; } /*Get the delta time and delta time per units*/ var = GetVar(clock, DTIMEUNITS); if (var) { deltaTimeUnits = GetInt(var); } else { deltaTimeUnits = 0; } var = GetVar(clock, DTIMEPERUNITS); if (var) { deltaTimePerUnits = GetInt(var); } else { deltaTimePerUnits = 0; } MakeVar(clock, DTIME); var = GetVar(clock, DTIME); if (!var || !IsReal(var)) { /*Obviously don't want to do anything*/ if (runControl) { InhibitLogging(true); SetValue(runControl, NewInt(RC_STOP)); InhibitLogging(false); } DoNotDisturb(clock, MARKTIME); return ObjTrue; } deltaTime = GetReal(var); var = GetVar(clock, DTIMEPER); if (var) { deltaTime /= GetReal(var); } if (deltaTimePerUnits) { deltaTime = deltaTime * fps; } wrap = GetPredicate(clock, CYCLECLOCK); if (deltaTime == 0.0) { return ObjTrue; } switch (GetInt(runSpeed)) { case RC_STOP: return ObjTrue; case RC_FORWARD: break; case RC_REVERSE: deltaTime = -deltaTime; break; case RC_FAST_FORWARD: deltaTime = 3.0 * deltaTime; break; case RC_FAST_REVERSE: deltaTime = -3.0 * deltaTime; break; } if (lateness <= 0.0) { return ObjTrue; } MakeVar(clock, TIME); curTime = GetVar(clock, TIME); if (curTime) { time = GetReal(curTime); } else { time = 0.0; } /*Increment the time by the elapsed time*/ if (deltaTimeUnits) { ObjPtr timeSteps; MakeVar(clock, TIMESTEPS); timeSteps = GetVar(clock, TIMESTEPS); if (timeSteps) { real index, newTime; index = SearchFuzzyReal(timeSteps, time); index += deltaTime * lateness; newTime = FuzzyRealIndex(timeSteps, index); if (newTime != time) { time = newTime; } else { if (runControl) { InhibitLogging(true); SetValue(runControl, NewInt(RC_STOP)); InhibitLogging(false); } } } } else { time += deltaTime * lateness; } /*Check against time bounds*/ MakeVar(clock, TIMEBOUNDS); timeBounds = GetVar(clock, TIMEBOUNDS); if (timeBounds && IsRealArray(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]; /*Another wakeup call*/ WakeMe(clock, MARKTIME, Clock() + 0.001); } else { time = tb[1]; if (runControl) { InhibitLogging(true); SetValue(runControl, NewInt(RC_STOP)); InhibitLogging(false); } } } else if (deltaTime < 0.0 && time < tb[0]) { if (wrap) { time = tb[1]; /*Another wakeup call*/ WakeMe(clock, MARKTIME, Clock() + 0.001); } else { time = tb[0]; if (runControl) { InhibitLogging(true); SetValue(runControl, NewInt(RC_STOP)); InhibitLogging(false); } } } else { /*Another wakeup call*/ WakeMe(clock, MARKTIME, Clock() + 0.001); } /*Now change the clock*/ InhibitLogging(true); SetClock(clock, time); InhibitLogging(false); return ObjTrue; } #ifdef HAVE_PROTOTYPES 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 ObjPtr ChangeRunControl(radioGroup) ObjPtr radioGroup; /*Changes the run control of a clock according to a newly pressed button*/ { ObjPtr clock; real loValue, hiValue; ObjPtr var; int value; var = GetValue(radioGroup); if (var) value = GetInt(var); else value = RC_STOP; clock = GetObjectVar("ChangeClockSpeed", radioGroup, REPOBJ); if (!clock) return ObjFalse; DoNotDisturb(clock, MARKTIME); SetVar(clock, RUNSPEED, NewInt(value)); WakeMe(clock, MARKTIME, Clock() + 0.001); return ObjTrue; } static ObjPtr MakeClockTimeSteps(clock) ObjPtr clock; /*Makes a clock's TIMESTEPS variable*/ { long k; ObjPtr datasets; ObjPtr totalTimeSteps = NULLOBJ; MakeVar(clock, DATASETS); datasets = GetVar(clock, DATASETS); if (datasets) { for (k = 0; k < DIMS(datasets)[0]; ++k) { ObjPtr timeSteps; ObjPtr element; element = GetObjectElement(datasets, &k); MakeVar(element, TIMESTEPS); timeSteps = GetVar(element, TIMESTEPS); if (timeSteps) { if (!totalTimeSteps) { totalTimeSteps = timeSteps; } else { totalTimeSteps = MergeRealArrays(totalTimeSteps, timeSteps); } } } } totalTimeSteps = Uniq(totalTimeSteps); SetVar(clock, TIMESTEPS, totalTimeSteps); return ObjTrue; } static ObjPtr MakeClockDTime(clock) ObjPtr clock; /*Method to make a clock's DTIME*/ { real dt; ObjPtr datasets; Bool stepSet = false; real minTimeStep; long nTimeSteps = 0; long k; /*Calculate minimum time step*/ MakeVar(clock, DATASETS); datasets = GetVar(clock, DATASETS); if (datasets) { ObjPtr element; for (k = 0; k < DIMS(datasets)[0]; ++k) { ObjPtr timeSteps; element = GetObjectElement(datasets, &k); MakeVar(element, TIMESTEPS); timeSteps = GetVar(element, TIMESTEPS); if (timeSteps) { ObjPtr deltas; deltas = SortArray(Uniq(RealArrayDeltas(timeSteps))); /*Set the step only if there is one*/ nTimeSteps = DIMS(timeSteps)[0]; if (DIMS(timeSteps)[0] > 1) { if (stepSet) { minTimeStep = MIN(minTimeStep, *((real *) ELEMENTS(deltas))); } else { minTimeStep = *((real *) ELEMENTS(deltas)); stepSet = true; } } } } } /*Estimate dt*/ if (nTimeSteps > 1) { k = nTimeSteps / 20.0; if (k < 1) k = 1; else if (k > 10) k = 10; dt = minTimeStep * (real) k; } else { dt = 1.0; } SetVar(clock, DTIME, NewReal(dt)); } static ObjPtr ShowClockControls(clock, windowName) ObjPtr clock; ObjPtr 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, WINUI + WINFIXEDSIZE); if (!dialogExists) { int left, right, bottom, top; 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 button, checkBox, radioGroup, control, titleBox; ObjPtr timeBounds; ObjPtr time; ObjPtr textBox; char readoutText[40]; ObjPtr genericHelp; real dt, mdt; int left, right, bottom, top; left = MAJORBORDER; right = CLWINWIDTH - MAJORBORDER; top = CLWINHEIGHT - MAJORBORDER; MakeVar(clock, TIME); time = GetVar(clock, TIME); genericHelp = NewString("This controls the rate at which the clock \ advances through time. When the forward play button is pushed, the clock will \ begin to advance forward the number of seconds or time steps given by the first \ text box for every number of seconds or frames given by the second time box.\n\ \n\ The easiest way to understand this is to read the text boxes and check boxes as \ a complete sentence. Examples:\n\n\ \"Advance 0.25 second(s) every 1 second(s).\"\n\ This causes the clock to step forward at a rate of one-quarter second for every \ second that passes in the real world. In other words, the clock will step at one-quarter \ real time. When recording on videodisc, this will result in a one-quarter real time \ display when the videodisc is played back.\n\ \n\ \"Advance 5 time step(s) every 1 second(s).\"\n\ This causes the clock to step forward through five timesteps of the data for every \ second. If the timesteps are unevenly spaced, the clock will speed up and slow down \ as neccessary to ensure that 5 timesteps are displayed every second.\n\ \n\ \"Advance 0.3 second(s) every 1 frame(s).\"\n\ This causes the clock to step forward three seconds for every frames. A frame is \ the smallest unit of time that can be displayed given the limitations of the graphics \ device. Videodiscs have a fixed frame rate of 30 frames per second (or 25 for PAL systems). \ The interactive display has no fixed frame rate; it varies with how much time it takes to produce \ a picture. \n\ \n\ To see one time step after another as fast as they can be displayed, set the controls to \ \"Advance 1 time step(s) every 1 frame(s).\""); /*Add a time control*/ control = NewTimeControl(left, right, top - CLTCHEIGHT, top, "Time Control"); PrefixList(contents, control); SetVar(control, PARENT, panel); AssocDirectControlWithVar(control, clock, TIME); ReinitController(clock); top -= CLTCHEIGHT + MINORBORDER; /*Add a title box*/ left = MINORBORDER; bottom = MINORBORDER; right = CLWINWIDTH - MINORBORDER; titleBox = NewTitleBox(left, right, bottom, top, "Animation"); PrefixList(contents, titleBox); SetVar(titleBox, PARENT, panel); top -= TITLEBOXTOP + MINORBORDER; left += MINORBORDER; right -= MINORBORDER; MakeVar(clock, DTIME); var = GetVar(clock, DTIME); if (var) { dt = GetReal(var); } else { SetVar(clock, DTIME, NewReal(0.0)); printf("Shouldn't have to set clock's dtime\n"); dt = 0.0; } bottom = top - EDITBOXHEIGHT; /*Create the Advance text box*/ textBox = NewTextBox(left, left + (right - left) / 4, bottom + (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN, top - (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN, PLAIN, "Advance legend", "Advance"); SetVar(textBox, PARENT, panel); PrefixList(contents, textBox); SetTextAlign(textBox, CENTERALIGN); /*Create the dTime editable text box*/ PrintNumber(readoutText, dt); textBox = NewTextBox(left + (right - left) / 4, left + 2 * (right - left) / 4, bottom, top, EDITABLE + WITH_PIT + ONE_LINE, "Delta Time Box", readoutText); SetVar(textBox, PARENT, panel); PrefixList(contents, textBox); AssocTextRealControlWithVar(textBox, clock, DTIME, 0.0, plusInf, 0); SetVar(textBox, HELPSTRING, genericHelp); /*Create the seconds and timesteps box*/ radioGroup = NewRadioButtonGroup("Delta Time Units"); PrefixList(contents, radioGroup); SetVar(radioGroup, PARENT, panel); button = NewRadioButton(left + (right - left) / 4, left + 2 * (right - left) / 4, bottom - CHECKBOXSPACING - CHECKBOXHEIGHT, bottom - CHECKBOXSPACING, "second(s)"); AddRadioButton(radioGroup, button); button = NewRadioButton(left + (right - left) / 4, left + 2 * (right - left) / 4, bottom - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, bottom - CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "time step(s)"); AddRadioButton(radioGroup, button); var = GetVar(clock, DTIMEUNITS); if (!var) SetVar(clock, DTIMEUNITS, NewInt(0)); AssocDirectControlWithVar(radioGroup, clock, DTIMEUNITS); SetVar(radioGroup, HELPSTRING, genericHelp); /*Create the Every text box*/ textBox = NewTextBox(left + 2 * (right - left) / 4, left + 3 * (right - left) / 4, bottom + (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN, top - (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN, PLAIN, "Every legend", "every"); SetVar(textBox, PARENT, panel); PrefixList(contents, textBox); SetTextAlign(textBox, CENTERALIGN); /*Create the dTimePer editable text box*/ var = GetVar(clock, DTIMEPER); if (!var) { SetVar(clock, DTIMEPER, NewReal(1.0)); printf("Shouldn't have to set dtimeper\n"); } PrintNumber(readoutText, var ? GetReal(var) : 1.0); textBox = NewTextBox(left + 3 * (right - left) / 4, right, bottom, top, EDITABLE + WITH_PIT + ONE_LINE, "Delta Time Per Box", readoutText); SetVar(textBox, PARENT, panel); PrefixList(contents, textBox); AssocTextRealControlWithVar(textBox, clock, DTIMEPER, 0.0, plusInf, TR_NE_BOTTOM); SetVar(textBox, HELPSTRING, genericHelp); /*Create the seconds and frames box*/ radioGroup = NewRadioButtonGroup("Delta Time Per"); PrefixList(contents, radioGroup); SetVar(radioGroup, PARENT, panel); SetVar(radioGroup, HELPSTRING, genericHelp); button = NewRadioButton(left + 3 * (right - left) / 4, right, bottom - CHECKBOXSPACING - CHECKBOXHEIGHT, bottom - CHECKBOXSPACING, "second(s)"); AddRadioButton(radioGroup, button); button = NewRadioButton(left + 3 * (right - left) / 4, right, bottom - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, bottom - CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "frame (s)"); AddRadioButton(radioGroup, button); var = GetVar(clock, DTIMEPERUNITS); if (!var) SetVar(clock, DTIMEPERUNITS, NewInt(0)); AssocDirectControlWithVar(radioGroup, clock, DTIMEPERUNITS); top = bottom - MAJORBORDER - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING; /*Create the cycle time radio group*/ radioGroup = NewRadioButtonGroup("Cycle the clock"); SetVar(radioGroup, PARENT, panel); PrefixList(contents, radioGroup); AssocDirectControlWithVar(radioGroup, clock, CYCLECLOCK); 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")); /*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); top -= MAJORBORDER + CHECKBOXHEIGHT; /*Create the icon buttons*/ radioGroup = NewRadioButtonGroup("Speed Control"); SetVar(radioGroup, PARENT, panel); SetVar(radioGroup, REPOBJ, clock); 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", BS_PLAIN); SetVar(button, REPOBJ, clock); SetVar(button, HELPSTRING, NewString("This button stops the animation.")); 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", BS_PLAIN); SetVar(button, REPOBJ, clock); SetVar(button, HELPSTRING, NewString("This button sets time moving forward at the rate specified above.")); 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", BS_PLAIN); SetVar(button, REPOBJ, clock); SetVar(button, HELPSTRING, NewString("This button sets time moving backward at the rate specified above.")); 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", BS_PLAIN); SetVar(button, REPOBJ, clock); SetVar(button, HELPSTRING, NewString("This button sets time moving forward at three times the rate specified above.")); AddRadioButton(radioGroup, button); /*Fast 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", BS_PLAIN); SetVar(button, REPOBJ, clock); SetVar(button, HELPSTRING, NewString("This button sets time moving backward at three times the rate specified above.")); AddRadioButton(radioGroup, button); var = GetVar(clock, RUNSPEED); if (var) { SetValue(radioGroup, var); } else { SetValue(radioGroup, NewInt(RC_STOP)); } SetMethod(radioGroup, CHANGEDVALUE, ChangeRunControl); SetVar(clock, RUNCONTROL, radioGroup); } } return (ObjPtr) clockWindow; } static ObjPtr MakePerspecControlAppearance(control) ObjPtr control; /*Makes a perspec control's appearance*/ { ObjPtr observer; FuncTyp method; ObjPtr var; int viewType; real perspecValue[4]; /*Get the observer*/ observer = GetObjectVar("MakePerspecControlAppearance", control, REPOBJ); if (!observer) { return ObjFalse; } var = GetVar(observer, FOCUSDIST); if (var) { perspecValue[0] = GetReal(var); } else { perspecValue[0] = INITEYEDIST; } var = GetVar(observer, VIEWFIELD); if (var) { perspecValue[1] = GetReal(var); } else { perspecValue[1] = INITAOV; } var = GetVar(observer, VIEWCLIP); if (var) { Array2CArray(&(perspecValue[2]), var); } else { perspecValue[2] = INITNEARCLIP; perspecValue[3] = INITFARCLIP; } if (perspecValue[2] < MINCLIP) perspecValue[2] = MINCLIP; if (perspecValue[3] < MINCLIP) perspecValue[3] = MINCLIP; var = NewRealArray(1, 4L); CArray2Array(var, perspecValue); method = GetMethod(control, CHANGEDVALUE); SetMethod(control, CHANGEDVALUE, (FuncTyp) 0); InhibitLogging(true); SetValue(control, var); InhibitLogging(false); SetMethod(control, CHANGEDVALUE, method); var = GetIntVar("ChangeViewType", observer, VIEWTYPE); if (!var) { return ObjFalse; } viewType = GetInt(var); if (viewType == VT_ORTHOGRAPHIC) { if (MakePerspecOrtho(control, true)) { InhibitLogging(true); ChangedValue(control); InhibitLogging(false); } } else { if (MakePerspecOrtho(control, false)) { InhibitLogging(true); ChangedValue(control); InhibitLogging(false); } } SetVar(control, APPEARANCE, ObjTrue); return ObjTrue; } static ObjPtr ChangePerspective(object) ObjPtr object; /*Change value for a perspective control*/ { ObjPtr val; ObjPtr repObj; real oldFocusDist; real newPerspec[4]; repObj = GetObjectVar("ChangePerspective", object, REPOBJ); if (!repObj) { return ObjFalse; } val = GetRealVar("ChangePerspective", repObj, FOCUSDIST); if (!val) { return ObjFalse; } oldFocusDist = GetReal(val); val = GetFixedArrayVar("ChangePerspective", object, VALUE, 1, 4L); if (!val) { return ObjFalse; } Array2CArray(newPerspec, val); /*Change the observer's bits*/ SetVar(repObj, VIEWFIELD, NewReal(newPerspec[1])); val = NewRealArray(1, 2L); CArray2Array(val, &(newPerspec[2])); SetVar(repObj, VIEWCLIP, val); /*See if field distance has changed*/ if (oldFocusDist != newPerspec[0]) { /*If so, change LOCATION accordingly*/ real posn[3]; real focus[3]; real forward[3]; ObjPtr var; GetFocusPoint(focus, repObj); GetObserverLocation(forward, repObj); forward[0] = focus[0] - forward[0]; forward[1] = focus[1] - forward[1]; forward[2] = focus[2] - forward[2]; NORMALIZE(forward); posn[0] = focus[0] - forward[0] * newPerspec[0]; posn[1] = focus[1] - forward[1] * newPerspec[0]; posn[2] = focus[2] - forward[2] * newPerspec[0]; SetVar(repObj, FOCUSDIST, NewReal(newPerspec[0])); var = NewRealArray(1, 3L); CArray2Array(var, posn); SetVar(repObj, LOCATION, var); } ImInvalid(repObj); return ObjTrue; } 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); ImInvalid(controller); 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); ImInvalid(repObj); return ObjTrue; } ObjPtr ResetPosition(button) ObjPtr button; /*Resets an observer's position*/ { ObjPtr observer, var; real focusDist; real forward[3]; real eyePosn[3]; int i; observer = GetObjectVar("ResetPosition", button, REPOBJ); if (!observer) { return ObjFalse; } focusDist = INITEYEDIST; var = GetRealVar("ResetPosition", observer, FOCUSDIST); if (var) { focusDist = GetReal(var); } GetForwardVector(forward, observer); /*Derive the eye position from 0,0,0 through eye distance*/ eyePosn[0] = -forward[0] * focusDist; eyePosn[1] = -forward[1] * focusDist; eyePosn[2] = -forward[2] * focusDist; var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); ImInvalid(observer); return ObjTrue; } ObjPtr ResetRotation(button) ObjPtr button; /*Resets an observer's rotation*/ { ObjPtr observer; real focusPoint[3]; real eyePosn[3]; ObjPtr var; real focusDist; Quaternion rotQuat; observer = GetObjectVar("ResetRotation", button, REPOBJ); if (!observer) { return ObjFalse; } focusDist = INITEYEDIST; var = GetRealVar("ResetRotation", observer, FOCUSDIST); if (var) { focusDist = GetReal(var); } GetFocusPoint(focusPoint, observer); rotQuat[0] = 1.0; rotQuat[1] = 0.0; rotQuat[2] = 0.0; rotQuat[3] = 0.0; var = NewRealArray(1, 4L); CArray2Array(var, rotQuat); SetVar(observer, ROTQUAT, var); eyePosn[0] = focusPoint[0]; eyePosn[1] = focusPoint[1]; eyePosn[2] = focusPoint[2] + focusDist; var = NewRealArray(1, 3L); CArray2Array(var, eyePosn); SetVar(observer, LOCATION, var); ImInvalid(observer); return ObjTrue; } static ObjPtr ChangeObserverLookat(control) ObjPtr control; /*Changes an observers's lookat point*/ { ObjPtr repObj, var; NameTyp whichVar; long whichIndex; long nIndices; long k; real *el1, *el2; real minMax[2]; real value; char *s; real forward[3], location[3], up[3], side[3], lookat[3], focusDist; real newRot[3][3]; Quaternion rotQuat; repObj = GetVarSurely("ChangeObserverLookat", control, REPOBJ); if (!repObj) { return ObjFalse; } var = GetIntVar("ChangeObserverLookat", control, WHICHINDEX); if (!var) { return ObjFalse; } whichIndex = GetInt(var); var = GetRealVar("ChangeObserverLookat", repObj, FOCUSDIST); if (!var) { return ObjFalse; } focusDist = GetReal(var); GetForwardVector(forward, repObj); GetUpVector(up, repObj); GetObserverLocation(location, repObj); minMax[0] = minusInf; minMax[1] = plusInf; var = GetValue(control); if ((!var) || (!IsString(var))) { return ObjFalse; } s = GetString(var); if (ParseReal(&value, s)) { if (value == missingData) { { WarnUser(CW_MISSINGERROR); return ObjFalse; } } else { { if (value <= minMax[0]) { DeferMessage(control, RANGEALERT); return ObjFalse; } } { if (value >= minMax[1]) { DeferMessage(control, RANGEALERT); return ObjFalse; } } } /*Dropped through, it must be OK*/ if (GetPredicate(repObj, USESPACECOORDS)) { real *elements; ObjPtr var; /*Have to convert into observer coords*/ var = GetFixedArrayVar("MakeObserverLocationAppearance", repObj, SPACEORIGIN, 1, 3L); if (var) { elements = ELEMENTS(var); value -= elements[whichIndex]; } var = GetFixedArrayVar("MakeObserverLocationAppearance", repObj, SPACESCALE, 1, 3L); if (var) { elements = ELEMENTS(var); value /= elements[whichIndex]; } } } else { WarnUser(CW_NUMBERERROR); return ObjFalse; } /*Now use the new value to make a new lookat*/ lookat[0] = location[0] + forward[0] * focusDist; lookat[1] = location[1] + forward[1] * focusDist; lookat[2] = location[2] + forward[2] * focusDist; lookat[whichIndex] = value; forward[0] = lookat[0] - location[0]; forward[1] = lookat[1] - location[1]; forward[2] = lookat[2] - location[2]; focusDist = sqrt(SQUARE(forward[0]) + SQUARE(forward[1]) + SQUARE(forward[2])); if (focusDist < 0.0) { WarnUser(CW_LOOKATERROR); return ObjFalse; } /*Make a new forward, up, and distance*/ forward[0] /= focusDist; forward[1] /= focusDist; forward[2] /= focusDist; CROSS(forward, up, side); if (side[0] == 0.0 && side[1] == 0.0 && side[2] == 0.0) { side[0] = forward[1]; side[1] = forward[2]; side[2] = forward[0]; } NORMALIZE(side); CROSS(side, forward, up); NORMALIZE(up); /*Make a matrix*/ newRot[0][0] = side[0]; newRot[1][0] = side[1]; newRot[2][0] = side[2]; newRot[0][1] = up[0]; newRot[1][1] = up[1]; newRot[2][1] = up[2]; newRot[0][2] = -forward[0]; newRot[1][2] = -forward[1]; newRot[2][2] = -forward[2]; MatrixToQuaternion(newRot, rotQuat); var = NewRealArray(1, 4L); CArray2Array(var, rotQuat); SetVar(repObj, ROTQUAT, var); SetVar(repObj, FOCUSDIST, NewReal(focusDist)); ImInvalid(repObj); SetVar(control, APPEARANCE, ObjTrue); return ObjTrue; } static ObjPtr MakeObserverLookatAppearance(control) ObjPtr control; /*Gets an observer's location and makes appearance*/ { real *location, forward[3], focusDist; ObjPtr repObj; ObjPtr var; real value; int whichAxis; FuncTyp method; real observerCoord; repObj = GetVar(control, REPOBJ); if (!repObj) { return ObjFalse; } MakeVar(repObj, LOCATION); var = GetFixedArrayVar("MakeObserverLookatAppearance", repObj, LOCATION, 1, 3L); if (!var) { return ObjFalse; } location = ELEMENTS(var); GetForwardVector(forward, repObj); MakeVar(repObj, FOCUSDIST); var = GetRealVar("MakeObserverLookatAppearance", repObj, FOCUSDIST); if (!var) { return ObjFalse; } focusDist = GetReal(var); var = GetIntVar("MakeObserverLookatAppearance", control, WHICHINDEX); if (var) { whichAxis = GetInt(var); } else { whichAxis = 0; } observerCoord = location[whichAxis] + forward[whichAxis] * focusDist; if (GetPredicate(repObj, USESPACECOORDS)) { real *elements; /*Have to convert into space coords*/ var = GetFixedArrayVar("MakeObserverLookatAppearance", repObj, SPACESCALE, 1, 3L); if (var) { elements = ELEMENTS(var); observerCoord *= elements[whichAxis]; } var = GetFixedArrayVar("MakeObserverLookatAppearance", repObj, SPACEORIGIN, 1, 3L); if (var) { elements = ELEMENTS(var); observerCoord += elements[whichAxis]; } } PrintNumber(tempStr, observerCoord); method = GetMethod(control, CHANGEDVALUE); SetMethod(control, CHANGEDVALUE, (FuncTyp) 0); InhibitLogging(true); SetValue(control, NewString(tempStr)); InhibitLogging(false); SetMethod(control, CHANGEDVALUE,