DIRECTORY Basics, BasicTime, CedarProcess, FS, Histograms, IdleBackdoor, Imager, ImagerBackdoor, ImagerBox, ImagerFont, ImagerPath, ImagerPixelArray, ImagerPixelMap, ImagerPrivate, ImagerTerminal, ImagerTransformation, IO, Menus, PrincOps, Process, Random, Real, RealFns, Rope, Terminal, TerminalFace, ThisMachine, TIPUser, Vector2, ViewerClasses, ViewerOps; StarField: CEDAR PROGRAM IMPORTS BasicTime, CedarProcess, FS, Histograms, IdleBackdoor, Imager, ImagerBackdoor, ImagerBox, ImagerFont, ImagerPixelMap, ImagerTerminal, ImagerTransformation, IO, Process, Random, Real, RealFns, Rope, 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 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]]; Kind: 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]; 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; dDzw: REAL _ 0.1; slovershoot: REAL _ 1.5; upTextMin: Milliseconds _ 10*OneSecond; upTextMax: Milliseconds _ 60*OneSecond; downTextMin: Milliseconds _ 1*OneSecond; downTextMax: Milliseconds _ 6*OneSecond; blankTimeoutMS: Milliseconds _ 15*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; Dzw: REAL; slowFactor: REAL _ 0.95; avgDt: REAL--Milliseconds-- _ OneSecond; decay: REAL _ 0.1; hold: REAL _ 1.0 - decay; fullAngle: REAL--degrees per step at left/right edge-- _ 1.0; yaw, pitch: REAL--degrees-- _ 0; rotMat: Mat _ idMat; msTillBlank: Milliseconds _ 0; 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] = { [] _ 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]]; }; 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 { kindSel: REAL = rs.ChooseInt[1, 100]/100.0 * kindWeights[Back]; SELECT kindSel FROM > kindWeights[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; }; > kindWeights[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; }; > kindWeights[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; }; > kindWeights[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; }; 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; }; }; 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; dp.yw _ rotMat[Y][X]*xw + rotMat[Y][Y]*yw + rotMat[Y][Z]*zw; dp.zw _ rotMat[Z][X]*xw + rotMat[Z][Y]*yw + rotMat[Z][Z]*zw - Dzw; 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]; 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]]; Dzw _ initialDzw; slowFactor _ 1 - (Dzw)/(slovershoot * scale*rw/startRp); IF NOT useXOR THEN [bufferContext, bufferPM] _ MakeBuffer[xp0, yp0, xp1, yp1]; PickText[T _ 0]; giveContext[DrawInit]; oldP _ BasicTime.GetClockPulses[]; yaw _ pitch _ 0; rotMat _ idMat; kindWeights _ [0, 0, 0, 0, 1]; FOR i: INT _ 0, i+1 DO newP: BasicTime.Pulses; oldYaw: REAL = yaw; oldPitch: REAL = pitch; oldSpeed: REAL = Dzw; doStick: BOOL _ FALSE; stickPos: Imager.VEC; IF NOT inViewers THEN TRUSTED { mousePos: Terminal.Position = vt.GetMousePosition[]; {moved: BOOL = mousePos # oldMousePos; IF moved THEN { vt.SetBWCursorPosition[[mousePos.x-8, mousePos.y-8]]; oldMousePos _ mousePos; }; IF (doStick _ TerminalFace.keyboard[Yellow] = down) THEN { stickPos _ [mousePos.x, yp1 - mousePos.y]; }; IF doStick OR moved THEN { IF msTillBlank <= 0 THEN { vt.SetBWCursorPattern[bullseyeCursor]; }; msTillBlank _ blankTimeoutMS; }; }} ELSE { IF (doStick _ stickViewers) THEN { stickPos _ viewerMouse; }; }; IF TerminalFace.keyboard[A] = down THEN Dzw _ Dzw + dDzw; IF TerminalFace.keyboard[D] = down THEN Dzw _ Dzw - dDzw; IF (doStick _ TerminalFace.keyboard[Yellow] = down) THEN { yaw _ (stickPos.x - xo) * angleScale; pitch _ (stickPos.y - yo) * angleScale; IF yaw # oldYaw OR pitch # oldPitch THEN { rotMat _ RotateMat[Y, Z, X, pitch, RotateMat[X, Z, Y, yaw, idMat]]; }; }; IF yaw # oldYaw OR pitch # oldPitch OR Dzw # oldSpeed 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; lrWgt: REAL = height * depth * tanHalfFOVX * MAX[0.0, -Dzw]; tbWgt: REAL = width * depth * tanHalfFOVY * MAX[0.0, -Dzw]; kindWeights[Left] _ lrWgt; IF yaw < 0 THEN kindWeights[Left] _ kindWeights[Left] + yawWgt; kindWeights[Right] _ kindWeights[Left] + lrWgt; IF yaw > 0 THEN kindWeights[Right] _ kindWeights[Right] + yawWgt; kindWeights[Bottom] _ kindWeights[Right] + tbWgt; IF pitch < 0 THEN kindWeights[Bottom] _ kindWeights[Bottom] + pitchWgt; kindWeights[Top] _ kindWeights[Bottom] + tbWgt; IF pitch > 0 THEN kindWeights[Top] _ kindWeights[Top] + pitchWgt; kindWeights[Back] _ kindWeights[Top] + width * height * MAX[0.0, Dzw]; IF kindWeights[Back] = 0.0 THEN kindWeights[Back] _ 1.0; }; IF dying THEN Dzw _ Dzw * slowFactor 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; }; kindWeights: ARRAY Kind 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[]; }; 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; }; 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]; }; 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[]; }. 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!@#$%~&*()+|{}^:"<>? ZStarField.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 22, 1986 8:35:30 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 SizePeriod[]; ส!u– "cedar" style˜code™Jšœ ฯmœ1™žœ˜UK˜—šžœ˜ Kšœ*˜*Kšœžœžœ˜'—Kšœžœ˜ K˜ Kšœ! œ œ œ  œ žœžœB œ œ œ  œฃœLžœ˜’Kšœžœ˜K˜—K˜šฃ œžœžœžœ˜"Kšœžœžœ ˜—K˜Kšฃœžœ žœžœžœžœžœ9žœžœขœ žœ ˜ฑK˜šฃœžœ žœ˜ Kšœ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šœ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˜š ฃœžœ žœžœžœ˜1K˜7—K˜š ฃœžœžœžœžœ˜