DIRECTORY Basics, BasicTime, CedarProcess, DebuggerSwap, FS, Histograms, IdleBackdoor, Imager, ImagerBackdoor, ImagerBox, ImagerFont, ImagerPath, ImagerPixelArray, ImagerPixelMap, ImagerPrivate, ImagerTerminal, ImagerTransformation, IO, Menus, PrincOps, Process, Random, Real, RealFns, Rope, RuntimeError, Terminal, TerminalDefs, TerminalFace, ThisMachine, TIPUser, Vector2, ViewerClasses, ViewerOps; StarField: CEDAR PROGRAM IMPORTS BasicTime, CedarProcess, DebuggerSwap, FS, Histograms, IdleBackdoor, Imager, ImagerBackdoor, ImagerBox, ImagerFont, ImagerPixelMap, ImagerTerminal, ImagerTransformation, IO, Process, Random, Real, RealFns, Rope, RuntimeError, Terminal, TerminalFace, ThisMachine, TIPUser, ViewerOps = { ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; PixelMap: TYPE = ImagerPixelMap.PixelMap; PixelArray: TYPE = ImagerPixelArray.PixelArray; Font: TYPE = ImagerFont.Font; Transformation: TYPE = ImagerTransformation.Transformation; Index: TYPE = NAT; StarData: TYPE = REF StarDataSeq; StarDataSeq: TYPE = RECORD [stars: SEQUENCE size: NAT OF Star]; Star: TYPE = RECORD [xw, yw, zw, xp, yp, rp: REAL, ci: NAT _ 0]; StarPtr: TYPE = LONG POINTER TO Star; stars: StarData _ NIL; Mat: TYPE = ARRAY Dim OF Vec; Vec: TYPE = ARRAY Dim OF REAL; Dim: TYPE = {X, Y, Z}; idMat: Mat = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]; Face: TYPE = {Left, Right, Bottom, Top, Back}; ImageCache: TYPE = REF ImageCacheRep; ImageCacheRep: TYPE = RECORD [ length: NAT _ 0, images: SEQUENCE size: NAT OF CachedImage]; CachedImage: TYPE = RECORD [ rpMax, d: REAL, di: INTEGER, image: PixelMap]; TextData: TYPE = RECORD [ texts: TextList, numTexts: NAT, totalProbability: REAL]; TextList: TYPE = LIST OF Text; Text: TYPE = RECORD [ text: ROPE, bounds: Imager.Box, cumProb: REAL ]; Request: TYPE = REF RequestRep; RequestRep: TYPE = RECORD [ proc: PROC [data: REF ANY, context: Imager.Context], data: REF ANY]; smallReal: REAL = Real.SmallestNormalizedNumber * 16; td: TextData; imageCache: ImageCache _ NIL; font: Font _ NIL; toUnit: Transformation; rs: Random.RandomStream _ Random.Create[seed: -1]; Milliseconds: TYPE = INT; OneSecond: Milliseconds = 1000; maxStars: NAT _ 200; minStars: NAT _ 5; histDtMin: Milliseconds _ 10; histDtMax: Milliseconds _ 70; goalDtOffsetLow: Milliseconds; goalDtOffsetHigh: Milliseconds; goalNDtProduct: INT; FOVX: REAL _ 45; edgeDegrees: REAL _ 65; hither: REAL _ 0.01; border: REAL _ 1.0; viewerStartRp: REAL _ 0.7; vtStartRp: REAL _ 0.7; viewerRpMin: REAL _ 1.0; vtRpMin: REAL _ 1.0; rw: REAL _ 1; initialDzw: REAL _ 5; dv: REAL _ 0.05; upTextMin: Milliseconds _ 10*OneSecond; upTextMax: Milliseconds _ 60*OneSecond; downTextMin: Milliseconds _ 1*OneSecond; downTextMax: Milliseconds _ 6*OneSecond; blankTimeoutMS: Milliseconds _ 60*OneSecond; usePeriod: BOOL _ FALSE; squareThresh: REAL _ 1.25; useXOR: BOOL _ TRUE; histPerf: BOOL _ FALSE; pickRandomNs: BOOL _ FALSE; pickPeriod: NAT _ 10; runningPriority: CedarProcess.Priority _ background; nStars: NAT _ minStars; v: Vec; avgDt: REAL--Milliseconds-- _ OneSecond; decay: REAL _ 0.1; hold: REAL _ 1.0 - decay; fullAngle: REAL--degrees per step at left/right edge-- _ 1.0; rollRate: REAL--degrees per step-- _ 1.0; yaw, pitch, roll: REAL--degrees-- _ 0; rotMat: Mat _ idMat; msTillBlank: Milliseconds _ 0; blank: BOOL _ TRUE; pausePeriod: Process.Ticks _ 0; retraces: NAT _ 1; machineName: ROPE _ ThisMachine.Name[]; sfvcFlavor: ATOM _ $StarFieldViewerClass; sfvc: ViewerClasses.ViewerClass _ NEW [ViewerClasses.ViewerClassRec _ [ flavor: sfvcFlavor, notify: NotifySFV, paint: PaintSFV, tipTable: TIPUser.InstantiateNewTIPTable["StarFielder.TIP"] ]]; sfv: Viewer; hists: ARRAY BOOL--useXOR-- OF Histograms.Histogram _ ALL[NIL]; okToGo: BOOL _ FALSE; going: BOOL _ FALSE; SetGoal: PROC [n1, dt1, n2, dt2, dd: REAL] = { o: REAL; goalNDtProduct _ Real.RoundLI[(dt1 - dt2)/(1.0/n1 - 1.0/n2)]; o _ (dt1*n1 - dt2*n2)/(n1 - n2); goalDtOffsetLow _ Real.RoundLI[o - dd/2]; goalDtOffsetHigh _ Real.RoundLI[o + dd/2]; }; NotifySFV: PROC [self: Viewer, input: LIST OF REF ANY] = { FOR input _ input, input.rest WHILE input # NIL DO WITH input.first SELECT FROM a: ATOM => SELECT a FROM $Start => {okToGo _ TRUE; IF NOT going THEN TRUSTED {Process.Detach[FORK Viewit[]]}}; $Stop => okToGo _ FALSE; $StartStick => stickViewers _ TRUE; $StopStick => stickViewers _ FALSE; ENDCASE => ERROR; z: TIPUser.TIPScreenCoords => viewerMouse _ [z.mouseX, z.mouseY]; ENDCASE => ERROR; ENDLOOP; }; stickViewers: BOOL _ FALSE; viewerMouse: Imager.VEC; PaintSFV: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE] --ViewerClasses.PaintProc-- = { IF whatChanged = NIL THEN sfvPM _ PixelMapFromViewer[self] ELSE WITH whatChanged SELECT FROM r: Request => r.proc[r.data, context]; ENDCASE => ERROR; }; sfvPM: PixelMap; icConsumer: PROC [context: Imager.Context, pm: PixelMap]; Satisfy: PROC [data: REF ANY, context: Imager.Context] = { icConsumer[context, sfvPM]; }; Viewit: PROC = { r: Request; GiveContext: PROC [to: PROC [context: Imager.Context, pm: PixelMap]] = { TRUSTED {icConsumer _ to}; ViewerOps.PaintViewer[viewer: sfv, hint: client, clearClient: FALSE, whatChanged: r]; }; TRUSTED { CedarProcess.SetPriority[runningPriority]; r _ NEW [RequestRep _ [Satisfy, NIL]]}; going _ TRUE; sfvPM _ PixelMapFromViewer[sfv]; Dewit[giveContext: GiveContext, xp0: 0, yp0: 0, xp1: sfv.cw, yp1: sfv.ch, FOVX: FOVX, edgeDegrees: edgeDegrees, hither: hither, border: border, startRp: viewerStartRp, rpMin: viewerRpMin, Stop: StopViewing, background: Imager.black, vt: Terminal.Current[], inViewers: TRUE]; going _ FALSE; }; StopViewing: PROC RETURNS [BOOL] = {RETURN [NOT okToGo]}; Staridle: PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] --Buttons.ButtonProc-- = {Sleepit[NOT control]}; Sleepit: PROC [logout: BOOL] = { ENABLE RuntimeError.UNCAUGHT => IF logout THEN DebuggerSwap.WorryCallDebugger["Uncaught SIGNAL or ERROR while Idle"]; [] _ IdleBackdoor.UseAlternateVT[vtProc: DoForVT, logout: logout]; }; vtContext: Imager.Context; vtPM: PixelMap; GiveVTContext: PROC [to: PROC [context: Imager.Context, pm: PixelMap]] = {to[vtContext, vtPM]}; quitFilter: IdleBackdoor.KeyFilter _ [first: Q, last: Q, pass: ALL[TRUE]]; QuitKey: PROC RETURNS [stop: BOOL] = { stop _ IdleBackdoor.KeyTyped[quitFilter]}; xCursor: Terminal.BWCursorBitmap = [ 8001H, 4002H, 2004H, 1008H, 0810H, 0420H, 0240H, 0180H, 0180H, 0240H, 0420H, 0810H, 1008H, 2004H, 4002H, 8001H]; bullseyeCursor: Terminal.BWCursorBitmap = [ 001700B, 007760B, 014630B, 030614B, 060606B, 040602B, 140603B, 177177B, 177177B, 140603B, 040602B, 060606B, 030614B, 014630B, 007760B, 001700B]; blankCursor: Terminal.BWCursorBitmap = ALL[0]; DoForVT: PROC [vt: Terminal.Virtual] = { Doit: PROC = { Dewit[giveContext: GiveVTContext, xp0: 0, yp0: 0, xp1: vt.bwWidth, yp1: vt.bwHeight, FOVX: FOVX, edgeDegrees: edgeDegrees, hither: hither, border: border, startRp: vtStartRp, rpMin: vtRpMin, inViewers: FALSE, Stop: QuitKey, background: Imager.white, vt: vt]; }; vt.Select[]; [] _ vt.SetBWBitmapState[allocated]; [vtContext, vtPM] _ ContextAndPMFromVT[vt]; [] _ vt.SetBWBitmapState[displayed]; {fb: Terminal.FrameBuffer = vt.GetBWFrameBuffer[]; vt.SetMousePosition[[fb.width/2, fb.height/2]]; vt.SetBWCursorPosition[[fb.width/2, fb.height/2]]; vt.SetBWCursorPattern[blankCursor]; }; CedarProcess.DoWithPriority[runningPriority, Doit]; [] _ vt.SetBWBitmapState[none]; }; Draw: PROC [context: Imager.Context, pm: PixelMap, index: Index] = { xc: REAL _ stars[index].xp; yc: REAL _ stars[index].yp; r: REAL _ stars[index].rp; IF usePeriod THEN { res: REAL _ IF r < 1.5 THEN 0.25 ELSE IF r < 3 THEN 0.5 ELSE 1.0; n: INT _ Real.RoundLI[r/res]; rr: REAL _ n*res; nHalves: INT _ Real.RoundLI[r*2]; d: REAL _ IF (nHalves MOD 2) = 0 THEN 0.0 ELSE 0.5; size: Transformation _ ImagerTransformation.Concat[toUnit, ImagerTransformation.Scale[rr]]; Doit: PROC = { context.SetXY[[xc, yc]]; context.ConcatT[size]; context.SetFont[font]; context.ShowChar['.]; }; xc _ Real.RoundLI[xc-d]+d; yc _ Real.RoundLI[yc-d]+d; context.DoSave[Doit]; } ELSE IF imageCache # NIL AND imageCache.length # 0 AND r <= imageCache[imageCache.length-1].rpMax THEN { ci: NAT _ stars[index].ci; xlated: PixelMap; x: INTEGER _ Real.RoundI[xc]; y: INTEGER _ Real.RoundI[yc]; WHILE ci > 0 AND imageCache[ci-1 ].rpMax >= r DO ci _ ci - 1 ENDLOOP; WHILE imageCache[ci].rpMax < r DO ci _ ci + 1 ENDLOOP; stars[index].ci _ ci; xlated _ imageCache[ci].image.ShiftMap[pm.sSize - y + imageCache[ci].di - imageCache[ci].image.sSize, x - imageCache[ci].di]; pm.Transfer[source: xlated, function: [xor, null]]; } ELSE { d: REAL; n: INT; path: ImagerPath.PathProc = { moveTo[[xc-r, yc]]; arcTo[[xc+r, yc], [xc-r, yc]]; }; n _ Real.RoundLI[r*2]; d _ IF (n MOD 2) = 0 THEN 0.0 ELSE 0.5; xc _ Real.RoundLI[xc-d]+d; yc _ Real.RoundLI[yc-d]+d; IF r < squareThresh THEN Imager.MaskRectangle[context, [xc-r, yc-r, r*2, r*2]] ELSE Imager.MaskFill[context, path]; }; }; upText, downText, T: Milliseconds _ 0; curText: Text; cto: Vector2.VEC; cts: REAL; PMContext: PROC [pm: PixelMap] RETURNS [context: Imager.Context] = { bm: ImagerBackdoor.Bitmap _ NEW [ImagerBackdoor.BitmapRep _ [ ref: pm.refRep.ref, base: pm.refRep.pointer, wordsPerLine: pm.refRep.rast, width: pm.refRep.rast*Basics.bitsPerWord, height: pm.refRep.lines]]; IF pm.refRep.lgBitsPerPixel # 0 OR pm.sMin # 0 OR pm.fMin # 0 THEN ERROR; context _ ImagerBackdoor.BitmapContext[bm]; Imager.ConcatT[context, ImagerTransformation.Invert[ImagerBackdoor.GetT[context]]]; }; Dewit: PROC [ giveContext: PROC [to: PROC [context: Imager.Context, pm: PixelMap]], xp0, yp0, xp1, yp1, FOVX, edgeDegrees, hither, border, startRp, rpMin: REAL, inViewers: BOOL, Stop: PROC RETURNS [BOOL], background: Imager.Color, vt: Terminal.Virtual] = { halfDxp: REAL = (xp1 - xp0)/2; halfDyp: REAL = (yp1 - yp0)/2; halfFOVX: REAL = FOVX/2; sinHalfFOVX: REAL = RealFns.SinDeg[halfFOVX]; cosHalfFOVX: REAL = RealFns.CosDeg[halfFOVX]; tanHalfFOVX: REAL = RealFns.TanDeg[halfFOVX]; tanHalfFOVY: REAL = (halfDyp / halfDxp) * tanHalfFOVX; halfFOVY: REAL = RealFns.ArcTanDeg[tanHalfFOVY, 1.0]; sinHalfFOVY: REAL = RealFns.SinDeg[halfFOVY]; cosHalfFOVY: REAL = RealFns.CosDeg[halfFOVY]; scale: REAL = halfDxp / tanHalfFOVX; angleScale: REAL--degrees per pixel-- = fullAngle / halfDxp; xo: REAL = xp0 + halfDxp; yo: REAL = yp0 + halfDyp; depth: REAL = scale*rw/startRp; width: REAL = depth * tanHalfFOVX * 2.0; height: REAL = depth * tanHalfFOVY * 2.0; yon: REAL = depth * 1.3; PickText: PROC [T: Milliseconds] = { p: REAL _ Choose[0, td.totalProbability*0.999]; tl: TextList; FOR tl _ td.texts, tl.rest WHILE p > tl.first.cumProb DO NULL ENDLOOP; curText _ tl.first; upText _ T + rs.ChooseInt[upTextMin, upTextMax]; downText _ upText + rs.ChooseInt[downTextMin, downTextMax]; cts _ (xp1 - xp0)/(curText.bounds.xmax - curText.bounds.xmin)/2; cto _ [ x: Choose[ xp0 - cts*curText.bounds.xmin, xp1 - cts*curText.bounds.xmax], y: Choose[ yp0 - cts*curText.bounds.ymin, yp1 - cts*curText.bounds.ymax]]; }; DrawText: PROC [context: Imager.Context, pm: PixelMap] = { InnerDoit: PROC = { context.SetXY[cto]; context.TranslateT[cto]; context.ScaleT[cts]; context.SetFont[font]; context.ShowRope[curText.text]; }; Imager.DoSave[context, InnerDoit]; }; PickNew: PROC [index: Index, pickLast: BOOL _ FALSE] = TRUSTED { dp: StarPtr = @stars[index]; dp.ci _ 0; IF pickLast THEN { prepareToDie _ FALSE; dying _ TRUE; dp.yw _ dp.xw _ 0; dp.zw _ depth; } ELSE { faceSel: REAL = rs.ChooseInt[1, 100]/100.0 * faceWeights[Back]; SELECT faceSel FROM > faceWeights[Top] => { halfDxw, halfDyw: REAL; halfDxw _ depth * tanHalfFOVX * border; halfDyw _ depth * tanHalfFOVY * border; dp.xw _ Choose[-halfDxw, halfDxw]; dp.yw _ Choose[-halfDyw, halfDyw]; dp.zw _ depth; }; > faceWeights[Bottom] => { radius: REAL = (1.0 - rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]/1E6)*depth; angle: REAL = Choose[-halfFOVX, halfFOVX]; yz: REAL = radius*RealFns.CosDeg[angle]; dp.xw _ radius*RealFns.SinDeg[angle]; dp.yw _ yz * sinHalfFOVY; dp.zw _ yz * cosHalfFOVY + hither; }; > faceWeights[Right] => { radius: REAL = (1.0 - rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]/1E6)*depth; angle: REAL = Choose[-halfFOVX, halfFOVX]; yz: REAL = radius*RealFns.CosDeg[angle]; dp.xw _ radius*RealFns.SinDeg[angle]; dp.yw _-yz * sinHalfFOVY; dp.zw _ yz * cosHalfFOVY + hither; }; > faceWeights[Left] => { radius: REAL = (1.0 - rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]/1E6)*depth; angle: REAL = Choose[-halfFOVY, halfFOVY]; xz: REAL = radius*RealFns.CosDeg[angle]; dp.yw _ radius*RealFns.SinDeg[angle]; dp.xw _ xz * sinHalfFOVX; dp.zw _ xz * cosHalfFOVX + hither; }; ENDCASE => { radius: REAL = (1.0 - rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]*rs.ChooseInt[0, 100]/1E6)*depth; angle: REAL = Choose[-halfFOVY, halfFOVY]; xz: REAL = radius*RealFns.CosDeg[angle]; dp.yw _ radius*RealFns.SinDeg[angle]; dp.xw _-xz * sinHalfFOVX; dp.zw _ xz * cosHalfFOVX + hither; }; }; ToPort[dp]; }; ToPort: PROC [dp: StarPtr] = TRUSTED { dp.xp _ (dp.xw/dp.zw)*scale + xo; dp.yp _ (dp.yw/dp.zw)*scale + yo; dp.rp _ MAX[rpMin, (rw/dp.zw)*scale]; }; DrawInit: PROC [context: Imager.Context, pm: PixelMap] = { Imager.SetColor[context, background]; Imager.MaskRectangle[context, [xp0, yp0, xp1 - xp0, yp1 - yp0]]; Imager.SetColor[context, ImagerBackdoor.invert]; FOR index: Index IN [0 .. nStars) DO PickNew[index]; Draw[context, pm, index]; ENDLOOP; }; Update: PROC [index: Index] RETURNS [clip: BOOL] = TRUSTED { dp: StarPtr = @stars[index]; xw: REAL = dp.xw; yw: REAL = dp.yw; zw: REAL = dp.zw; dp.xw _ rotMat[X][X]*xw + rotMat[X][Y]*yw + rotMat[X][Z]*zw - v[X]; dp.yw _ rotMat[Y][X]*xw + rotMat[Y][Y]*yw + rotMat[Y][Z]*zw - v[Y]; dp.zw _ rotMat[Z][X]*xw + rotMat[Z][Y]*yw + rotMat[Z][Z]*zw - v[Z]; clip _ FALSE; IF dp.zw < hither THEN clip _ TRUE ELSE IF dp.zw > yon THEN clip _ TRUE ELSE { ToPort[dp]; IF dp.xp - dp.rp > xp1 OR dp.xp + dp.rp < xp0 OR dp.yp - dp.rp > yp1 OR dp.yp + dp.rp < yp0 THEN clip _ TRUE; }; }; DrawDelta: PROC [context: Imager.Context, pm: PixelMap] _ IF useXOR THEN DrawDeltaByXOR ELSE DrawDeltaBuffered; prevUp: BOOL _ FALSE; DrawDeltaByXOR: PROC [context: Imager.Context, pm: PixelMap] = { shouldUp: BOOL _ (T >= upText) AND (T < downText); max: NAT _ MAX[nStars, newN]; index: Index _ 0; Imager.SetColor[context, ImagerBackdoor.invert]; IF blank # (msTillBlank <= 0) THEN { blank _ NOT blank; context.MaskVector[[xo, yp0], [xo, yp1]]; context.MaskVector[[xp0, yo], [xp1, yo]]; }; FOR index _ 0, index + 1 WHILE index < nStars DO need: BOOL _ TRUE; WHILE need DO Draw[context, pm, index]; need _ FALSE; IF Update[index].clip THEN { IF nStars > newN THEN { IF index < nStars - 1 THEN { stars[index] _ stars[nStars-1]; need _ TRUE; }; nStars _ nStars - 1; } ELSE PickNew[index]; }; ENDLOOP; IF index < nStars THEN Draw[context, pm, index]; ENDLOOP; FOR index _ nStars, index+1 WHILE index < newN DO nStars _ index + 1; PickNew[index, prepareToDie]; IF index < nStars THEN Draw[context, pm, index]; ENDLOOP; IF shouldUp # prevUp THEN DrawText[context, pm]; prevUp _ shouldUp; IF T >= downText THEN PickText[T]; }; DrawDeltaBuffered: PROC [context: Imager.Context, pm: PixelMap] = { index: Index _ 0; Imager.SetColor[bufferContext, background]; Imager.MaskRectangle[bufferContext, [0, 0, pm.fSize, pm.sSize]]; Imager.SetColor[bufferContext, ImagerBackdoor.invert]; FOR index _ 0, index + 1 WHILE index < nStars DO need: BOOL _ TRUE; WHILE need DO need _ FALSE; IF Update[index].clip THEN { IF nStars > newN THEN { IF index < nStars - 1 THEN { stars[index] _ stars[nStars-1]; need _ TRUE; }; nStars _ nStars - 1; } ELSE PickNew[index]; }; ENDLOOP; IF index < nStars THEN Draw[bufferContext, bufferPM, index]; ENDLOOP; FOR index _ nStars, index+1 WHILE index < newN DO nStars _ index + 1; PickNew[index, prepareToDie]; IF index < nStars THEN Draw[bufferContext, bufferPM, index]; ENDLOOP; IF (T >= upText) AND (T < downText) THEN DrawText[bufferContext, bufferPM]; pm.Transfer[bufferPM]; IF T >= downText THEN PickText[T]; }; newN: NAT _ nStars _ 1; prepareToDie, dying: BOOL _ FALSE; bufferPM: PixelMap; bufferContext: Imager.Context; oldP: BasicTime.Pulses; choice: NAT _ 0; oldMousePos: Terminal.Position _ vt.GetMousePosition[]; stars _ NEW [StarDataSeq[maxStars]]; v _ [0, 0, initialDzw]; IF NOT useXOR THEN [bufferContext, bufferPM] _ MakeBuffer[xp0, yp0, xp1, yp1]; PickText[T _ 0]; giveContext[DrawInit]; oldP _ BasicTime.GetClockPulses[]; yaw _ pitch _ roll _ 0; rotMat _ idMat; faceWeights _ [0, 0, 0, 0, 1]; blank _ TRUE; msTillBlank _ 0; FOR i: INT _ 0, i+1 DO newP: BasicTime.Pulses; oldYaw: REAL = yaw; oldPitch: REAL = pitch; oldRoll: REAL = roll; oldV: Vec = v; doStick: BOOL _ FALSE; stickPos: Imager.VEC; IF NOT inViewers THEN { mousePos: Terminal.Position = vt.GetMousePosition[]; {moved: BOOL = mousePos # oldMousePos; IF moved THEN { vt.SetBWCursorPosition[[mousePos.x-8, mousePos.y-8]]; oldMousePos _ mousePos; }; IF (doStick _ KeyDown[Yellow]) THEN { stickPos _ [mousePos.x, yp1 - mousePos.y]; }; IF KeyDown[Red] THEN roll _ rollRate ELSE IF KeyDown[Blue] THEN roll _ -rollRate ELSE roll _ 0.0; IF doStick OR moved THEN { IF msTillBlank <= 0 THEN { vt.SetBWCursorPattern[bullseyeCursor]; }; msTillBlank _ blankTimeoutMS; }; }} ELSE { IF (doStick _ stickViewers) THEN { stickPos _ viewerMouse; }; }; IF doStick THEN { yaw _ (stickPos.x - xo) * angleScale; pitch _ (stickPos.y - yo) * angleScale; }; IF yaw # oldYaw OR pitch # oldPitch OR roll # oldRoll THEN { rotMat _ RotateMat[X, Y, Z, roll, RotateMat[Y, Z, X, pitch, RotateMat[X, Z, Y, yaw, idMat]]]; }; IF KeyDown[A] THEN v[Z] _ v[Z] + dv; IF KeyDown[D] THEN v[Z] _ v[Z] - dv; IF yaw # oldYaw OR pitch # oldPitch OR v # oldV THEN { depth2: REAL = depth * depth; yawWgt: REAL = height * depth2 * RealFns.SinDeg[ABS[yaw]] / 3.0; pitchWgt: REAL = width * depth2 * RealFns.SinDeg[ABS[pitch]] / 3.0; yf: Face = IF yaw < 0 THEN Left ELSE Right; pf: Face = IF pitch < 0 THEN Bottom ELSE Top; full: REAL = width*height*v[Z]; --since only the Z component of v can be non-zero, we optimize like this: --faceWeights[Left] _ faceWeights[Right] _ faceWeights[Bottom] _ faceWeights[Top] _ full*-0.25; faceWeights[Back] _ full; faceWeights[yf] _ faceWeights[yf] + yawWgt; faceWeights[pf] _ faceWeights[pf] + pitchWgt; faceWeights[Left] _ MAX[0.0, faceWeights[Left]]; FOR f: Face IN (Face.FIRST .. Face.LAST] DO faceWeights[f] _ faceWeights[f.PRED] + MAX[0.0, faceWeights[f]]; ENDLOOP; IF faceWeights[Back] = 0.0 THEN faceWeights[Back] _ 1.0; }; IF dying THEN NULL ELSE IF NOT prepareToDie THEN prepareToDie _ Stop[]; giveContext[DrawDelta]; IF nStars = 0 THEN EXIT; IF pausePeriod # 0 THEN Process.Pause[pausePeriod]; FOR i: NAT IN [0 .. retraces) DO Terminal.WaitForBWVerticalRetrace[vt]; ENDLOOP; newP _ BasicTime.GetClockPulses[]; IF newP > oldP THEN { Dt: Milliseconds _ BasicTime.PulsesToMicroseconds[newP - oldP]/1000; q: Milliseconds _ goalNDtProduct/nStars; goalDtLow: Milliseconds _ q + goalDtOffsetLow; goalDtHigh: Milliseconds _ q + goalDtOffsetHigh; IF histPerf THEN hists[useXOR].ChangeTransformed[x: nStars, y: MIN[histDtMax, MAX[histDtMin, Dt]]]; avgDt _ hold*avgDt + decay*Dt; IF pickRandomNs THEN {IF (choice _ choice + 1) MOD pickPeriod = 1 THEN newN _ rs.ChooseInt[minStars, maxStars] ELSE newN _ nStars} ELSE newN _ IF prepareToDie THEN nStars + 1 ELSE IF dying THEN 0 ELSE IF avgDt < goalDtLow THEN MIN[maxStars, nStars + 1] ELSE IF avgDt > goalDtHigh THEN MAX[minStars, nStars - 1] ELSE nStars; IF (NOT inViewers) AND msTillBlank > 0 THEN { msTillBlank _ msTillBlank - Dt; IF msTillBlank <= 0 THEN { vt.SetBWCursorPattern[blankCursor]; }; }; T _ T + Dt}; oldP _ newP; ENDLOOP; }; faceWeights: ARRAY Face OF REAL--cumulative volume estimates-- _ [0, 0, 0, 0, 1]; MakeBuffer: PROC [xmin, ymin, xmax, ymax: REAL] RETURNS [context: Imager.Context, pm: PixelMap] = { ixmin, iymin, ixmax, iymax: INT; ixmin _ Floor[xmin]; iymin _ Floor[ymin]; ixmax _ Ceiling[xmax]; iymax _ Ceiling[ymax]; pm _ ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: 0, sSize: iymax - iymin, fSize: ixmax - ixmin]]; context _ PMContext[pm]; }; ContextAndPMFromVT: PROC [vt: Terminal.Virtual] RETURNS [context: Imager.Context, pm: PixelMap] = { pm _ PixelMapFromVT[vt]; context _ ImagerTerminal.BWContext[vt, TRUE]; }; PixelMapFromVT: PROC [vt: Terminal.Virtual] RETURNS [pm: PixelMap] = { fb: Terminal.FrameBuffer _ vt.GetBWFrameBuffer[]; IF fb.bitsPerPixel # 1 THEN ERROR; pm _ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: fb.height, fSize: fb.width, refRep: NEW [ImagerPixelMap.PixelMapRep _ [ ref: fb.vm, pointer: fb.base, words: fb.vm.words, lgBitsPerPixel: 0, rast: fb.wordsPerLine, lines: fb.height]] ]; }; PixelMapFromViewer: PROC [v: Viewer] RETURNS [pm: PixelMap] = { vx1, vx2, vy1, vy2: INTEGER; height: NAT; pm _ PixelMapFromVT[Terminal.Current[]]; height _ pm.sSize; [vx1, vy1] _ ViewerOps.UserToScreenCoords[v, 0, 0]; [vx2, vy2] _ ViewerOps.UserToScreenCoords[v, v.cw, v.ch]; vy1 _ height - vy1; vy2 _ height - vy2; IF vy1 > vy2 THEN {y: INTEGER _ vy1; vy1 _ vy2; vy2 _ y}; pm _ pm.Clip[[sMin: vy1, fMin: vx1, sSize: vy2-vy1, fSize: vx2 - vx1]]; pm _ pm.ShiftMap[s: -vy1, f: -vx1]; }; CacheImages: PROC = { Do: PROC [rpMax: REAL] = { r: REAL _ rpMax*0.99; n: INT _ Real.RoundLI[r*2]; o: REAL _ IF (n MOD 2) = 0 THEN 0.0 ELSE 0.5; l: INTEGER _ Floor[o-r]; size: NAT _ Ceiling[o+r] - l; pm: PixelMap _ ImagerPixelMap.Create[0, [0, 0, size, size]]; context: Imager.Context _ PMContext[pm]; path: ImagerPath.PathProc = { moveTo[[o-r, o]]; arcTo[[o+r, o], [o-r, o]]; }; context.TranslateT[[-l, -l]]; context.SetColor[Imager.white]; context.MaskRectangle[[-l, -l, size, size]]; context.SetColor[ImagerBackdoor.invert]; context.MaskFill[path]; imageCache[imageCache.length] _ [rpMax: rpMax, d: o-l, di: -l, image: pm]; imageCache.length _ imageCache.length + 1; }; imageCache _ NEW [ImageCacheRep[6+8+9+9]]; imageCache.length _ 0; Do[0.75]; Do[1.25]; Do[RealFns.SqRt[2]]; Do[1.75]; Do[2.25]; Do[2.75]; FOR i: NAT IN [3 .. 10] DO Do[i+0.5] ENDLOOP; FOR i: NAT IN [1 .. 9] DO Do[11+2*i] ENDLOOP; FOR i: NAT IN [1 .. 9] DO Do[32.5+5*i] ENDLOOP; }; Floor: PROC [r: REAL] RETURNS [i: INT] = { d: INT _ 1 - Real.Fix[r]; i _ Real.Fix[r+d]-d}; Ceiling: PROC [r: REAL] RETURNS [i: INT] = { d: INT _ 1 + Real.Fix[r]; i _ Real.Fix[r-d]+d}; ShowCache: PROC = { space: INT _ Real.RoundLI[2.5*imageCache[imageCache.length-1].rpMax]; s, f: INT _ space; FOR i: NAT IN [0 .. imageCache.length) DO xlated: PixelMap _ imageCache[i].image.ShiftMap[s, f]; sfvPM.Transfer[xlated]; f _ f + space; IF f + space > sfvPM.fSize THEN {f _ space; s _ s + space}; ENDLOOP; }; SizePeriod: PROC = { bounds: ImagerFont.Extents; aspectRatio: REAL; bounds _ ImagerFont.BoundingBox[font, [0, ORD['.]]]; toUnit _ ImagerTransformation.Concat[ ImagerTransformation.Translate[ [-(bounds.leftExtent+bounds.rightExtent)/2, -(bounds.descent+bounds.ascent)/2]], ImagerTransformation.Scale2[ [2/(bounds.rightExtent-bounds.leftExtent), 2/(bounds.ascent-bounds.descent)]]]; aspectRatio _ (bounds.ascent-bounds.descent) / (bounds.rightExtent-bounds.leftExtent); IF aspectRatio < 0.999 OR aspectRatio > 1.001 THEN ERROR; }; ReadTextData: PROC [fileName: ROPE] RETURNS [td: TextData] = { from: IO.STREAM _ FS.StreamOpen[fileName]; last: TextList _ NIL; td _ [ texts: NIL, numTexts: 0, totalProbability: 0]; DO prob: REAL; text: ROPE; this: TextList; [] _ from.SkipWhitespace[]; IF from.EndOf[] THEN EXIT; prob _ from.GetReal[]; text _ from.GetRopeLiteral[]; text _ Replace[text, "", machineName]; td.numTexts _ td.numTexts + 1; this _ LIST[ [ text: text, bounds: ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[font, text]], cumProb: td.totalProbability _ td.totalProbability + prob] ]; IF last = NIL THEN td.texts _ this ELSE last.rest _ this; last _ this; ENDLOOP; from.Close[]; }; KeyDown: PROC [key: TerminalDefs.KeyName] RETURNS [down: BOOL] = TRUSTED INLINE {down _ TerminalFace.keyboard[key] = down}; Choose: PROC [min, max: REAL] RETURNS [r: REAL] = {r _ min + (rs.ChooseInt[0, 10000]/1.0E4) * (max-min)}; Replace: PROC [in, what, with: ROPE] RETURNS [new: ROPE] = { start, len: INT; ousLen: INT _ what.Length[]; new _ in; WHILE (start _ new.Index[s2: what]) < (len _ new.Length[]) DO new _ new.Substr[len: start].Cat[with, new.Substr[start: start+ousLen, len: len - (start+ousLen)]]; ENDLOOP; }; Limit: PROC [x, lim: REAL] RETURNS [lx: REAL] = { IF ABS[x] <= slowRate THEN RETURN [x]; IF ABS[x] <= slowRegion THEN RETURN [IF x > 0 THEN slowRate ELSE -slowRate]; lx _ IF x > 0 THEN lim ELSE -lim; }; restoreRate: REAL--degrees per step-- _ 1.0; slowRate: REAL _ 0.2; slowRegion: REAL--degrees yet to slew-- _ 10; RotateMat: PROC [d1, d2, d3: Dim, degrees: REAL, m: Mat] RETURNS [rm: Mat] = { c: REAL = RealFns.CosDeg[degrees]; s: REAL = RealFns.SinDeg[degrees]; rm[d1][d1] _ c*m[d1][d1] - s*m[d2][d1]; rm[d1][d2] _ c*m[d1][d2] - s*m[d2][d2]; rm[d1][d3] _ c*m[d1][d3] - s*m[d2][d3]; rm[d2][d1] _ s*m[d1][d1] + c*m[d2][d1]; rm[d2][d2] _ s*m[d1][d2] + c*m[d2][d2]; rm[d2][d3] _ s*m[d1][d3] + c*m[d2][d3]; rm[d3] _ m[d3]; }; STP: PROC [a, b, c: Vec] RETURNS [stp: REAL] = { stp _ a[X]*b[Y]*c[Z] + a[Y]*b[Z]*c[X] + a[Z]*b[X]*c[Y] - a[Z]*b[Y]*c[X] - a[Y]*b[X]*c[Z] - a[X]*b[Z]*c[Y]; }; CreateViewer: PROC = { sfv _ ViewerOps.CreateViewer[flavor: sfvcFlavor, info: [name: "StarField"]]}; NewHistograms: PROC = { hists[FALSE] _ Histograms.Create2D[iMin: minStars, iMax: maxStars, jMin: histDtMin, jMax: histDtMax]; [] _ hists[FALSE].Show[[name: "Dt vs n (buffered)"]]; hists[TRUE] _ Histograms.Create2D[iMin: minStars, iMax: maxStars, jMin: histDtMin, jMax: histDtMax]; [] _ hists[TRUE].Show[[name: "Dt vs n (XOR)"]]; }; Start: PROC = { SetGoal[n1: 15, dt1: OneSecond/20, n2: 50, dt2: OneSecond/40, dd: OneSecond/200]; IF histPerf THEN NewHistograms[]; ViewerOps.RegisterViewerClass[flavor: sfvcFlavor, class: sfvc]; CacheImages[]; font _ ImagerFont.Find["Xerox/PressFonts/TimesRoman-MRR"]; td _ ReadTextData["Starfield.texts"]; }; Start[]; }. p'i = mi (p - ci) mi+1 = di+1mi ci+1 = ci + vi+1 vi+1 = vi + ai+1 p'i+1 = di+1 mi (p - ci - vi+1) = di+1p'i - mi+1vi+1 mi+1vi+1 = di+1mi(vi + ai+1) = di+1mivi + mi+1ai+1 At edge of viewing cone: xw = zw tan a/2 xp = (Dxp/2)(1 + xw/(zw tan a/2)) + xp0 rp = (Dxp/2)(rw/(zw tan a/2)) zw = (Dxp/2)(rw/(rp tan a/2)) { &Test: PROC [xc, yc, r: REAL] = {Imager.MaskFill[StarField.bridgeContext, Imager.ArcTo[Imager.MoveTo[[xc-r, yc]], [xc+r, yc], [xc-r, yc]]]}; &Test[0, 0, 0]} abcdefghijklmnopqrstuvwxyz1234567890-=\[]_;',./ ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%~&*()+|{}^:"<>? abcdefghijklmnopqrstuvwxyz1234567890-=\[]_;',./ ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%~&*()+|{}^:"<>? ’StarField.Mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Last Edited by: Spreitzer, May 13, 1986 2:43:46 pm PDT Mike Spreitzer September 27, 1986 6:18:26 pm PDT [ci+1].rpMax > [ci].rpMax dt1 = o + p/n1 dt2 = o + p/n2 dt1 * n1 = o * n1 + p dt2 * n2 = o * n2 + p Establish ic[ci].rpMax >= r > ic[ci-1].rpMax If we were doing a simple, physical simulation, we'd have to rotate the velocity vector also, like this: IF yaw # 0 OR pitch # 0 THEN { vx: REAL = v[X]; vy: REAL = v[Y]; v[X] _ rotMat[X][X]*vx + rotMat[X][Y]*vy + rotMat[X][Z]*v[Z]; v[Y] _ rotMat[Y][X]*vx + rotMat[Y][Y]*vy + rotMat[Y][Z]*v[Z]; v[Z] _ rotMat[Z][X]*vx + rotMat[Z][Y]*vy + rotMat[Z][Z]*v[Z]; {avx: REAL = ABS[v[X]]; avy: REAL = ABS[v[Y]]; avz: REAL = ABS[v[Z]]; small: REAL = (avx+avy+avz)*1E-5; IF avx < small THEN v[X] _ 0; IF avy < small THEN v[Y] _ 0; IF avz < small THEN v[Z] _ 0}}; But we're not; we suppose that turning includes the right accelerations to keep the ship moving in the same direction we face. wh: REAL = width*height/4; hd: REAL = height*depth/2; dw: REAL = depth*width/2; faceWeights[Left] _ -wh*v[Z] - hd*v[X]; faceWeights[Left] _ STP[ [-width/2, -height/2, depth], [-width/2, height/2, depth], v]/2; faceWeights[Right] _ hd*v[X] - wh*v[Z]; faceWeights[Right] _ STP[ [width/2, height/2, depth], [width/2, -height/2, depth], v]/2; faceWeights[Bottom] _ -wh*v[Z] - dw*v[Y]; faceWeights[Bottom] _ STP[ [width/2, -height/2, depth], [-width/2, -height/2, depth], v]/2; faceWeights[Top] _ dw*v[Y] - wh*v[Z]; faceWeights[Top] _ STP[ [-width/2, height/2, depth], [width/2, height/2, depth], v]/2; faceWeights[Back] _ STP[[width, 0, 0], [0, height, 0], v]; SizePeriod[]; ส$k– "cedar" style˜code™Jšœ ฯmœ1™žœ˜UK˜—šžœ˜ Kšœ*˜*Kšœžœžœ˜'—Kšœžœ˜ K˜ Kšœ! œ œ œ  œ žœžœB œ œ œ  œฃœLžœ˜’Kšœžœ˜K˜—K˜šฃ œžœžœžœ˜"Kšœžœžœ ˜—K˜Kšฃœžœ žœžœžœžœžœ9žœžœขœ žœ ˜ฑK˜šฃœžœ žœ˜ KšžœžœžœžœG˜uKšœB˜BK˜—K˜K˜K˜šฃ œžœžœ+˜HK˜—K˜Kšœ?žœžœ˜JK˜šฃœžœžœžœ˜&Kšœ*˜*—K˜šœ$˜$K˜K˜K˜K˜—K˜šœ+˜+K˜#K˜#K˜#K˜$—K˜Kšœ'žœ˜.K˜šฃœžœ˜(šฃœžœ˜Kšœ# œ œ œ œžœžœB œ  œ œ œžœฃœ-˜‚K˜—K˜ Kšœ$˜$K˜+Kšœ$˜$Kšœ2˜2K˜/Kšœ2˜2Kšœ#˜#K˜Kšœ3˜3Kšœ˜K˜—K˜šฃœžœ:˜DKšœžœ˜Kšœžœ˜Kšœžœ˜šžœ žœ˜Kšœžœžœ žœžœžœžœžœ˜AKšœžœ˜Kšœžœ ˜Kšœ žœ˜!Kš œžœžœ žœžœžœ˜3Kšœ[˜[šฃœžœ˜K˜K˜K˜K˜K˜—K˜K˜K˜Kšœ˜—šžœžœžœžœžœ' œžœ˜hKšœžœ˜K˜Kšœžœ˜Kšœžœ˜Kšœ œ œ™,Kš žœžœ œ žœ žœ˜EKšžœ œžœ žœ˜6Kšœ˜Kšœ}˜}Kšœ3˜3K˜—šžœ˜Kšœžœ˜Kšœžœ˜˜Kšœ˜Kšœ˜K˜—Kšœ˜Kš œžœžœžœžœ˜'K˜K˜šžœ˜Kšžœ6˜:Kšžœ ˜$—K˜—Kšœ˜—K˜Kšœ&˜&K˜Kšœ žœ˜Kšœžœ˜ K˜šฃ œžœžœ˜Dšœžœ˜=K˜Kšœ˜Kšœ˜K˜)Kšœ˜—Kš žœžœ žœ žœžœ˜IKšœ+˜+KšœS˜SK˜—K˜šฃœž˜ šœ˜Kšœ žœžœ*˜EKšœ œ œ œ œžœ% œ œžœ˜LKš œ žœฃœžœžœžœ4˜]—Kšœ˜Kš œกœ œžœ œ œ˜Kš œกœ œžœ œ œ˜Kšœ œžœžœ˜Kšœ  œžœ œ˜-Kšœ  œžœ œ˜-Kšœ  œžœ œ˜-Kšœ  œžœกœ œกœ œ œ˜6Kšœ œžœ œ˜5Kšœ  œžœ œ˜-Kšœ  œžœ œ˜-Kš œžœกœ œ  œ˜$Kšœ žขœกœ œ˜Kšœžœžœžœ˜*Kšœžœ˜˜Kšœžœ˜ K˜ K˜—šž˜Kšœžœ˜ Kšœžœ˜ K˜K˜Kšžœžœžœ˜K˜K˜K˜4Kšœ˜šœžœ˜K˜ KšœI˜IKšœ=˜=—Kšžœžœžœžœ˜9K˜ Kšžœ˜—K˜ K˜—K˜šฃœžœžœžœ˜>Kšœžœžœ,˜<—K˜š ฃœžœ žœžœžœ˜1K˜7—K˜š ฃœžœžœžœžœ˜