<> <> <> <> 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]; <<[ci+1].rpMax > [ci].rpMax>> 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; hist hist goal goal goalN 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; initial 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; slowFactor: REAL _ 0.95; avg 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; goalN <> <> o _ (dt1*n1 - dt2*n2)/(n1 - n2); goal goal }; 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]; <= r > ic[ci-1].rpMax>> 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] = { half half halfFOVX: REAL = FOVX/2; sinHalfFOVX: REAL = RealFns.SinDeg[halfFOVX]; cosHalfFOVX: REAL = RealFns.CosDeg[halfFOVX]; tanHalfFOVX: REAL = RealFns.TanDeg[halfFOVX]; tanHalfFOVY: REAL = (half halfFOVY: REAL = RealFns.ArcTanDeg[tanHalfFOVY, 1.0]; sinHalfFOVY: REAL = RealFns.SinDeg[halfFOVY]; cosHalfFOVY: REAL = RealFns.CosDeg[halfFOVY]; scale: REAL = half angleScale: REAL--degrees per pixel-- = fullAngle / half xo: REAL = xp0 + half yo: REAL = yp0 + half 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] => { half half half dp.xw _ Choose[-half dp.yw _ Choose[-half 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 - 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]]; slowFactor _ 1 - ( 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 = 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 IF TerminalFace.keyboard[D] = down THEN 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 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, - tbWgt: REAL = width * depth * tanHalfFOVY * MAX[0.0, - 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, IF kindWeights[Back] = 0.0 THEN kindWeights[Back] _ 1.0; }; IF dying THEN 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 { q: Milliseconds _ goalN goal goal IF histPerf THEN hists[useXOR].ChangeTransformed[x: nStars, y: MIN[hist avg 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 avg IF avg IF (NOT inViewers) AND msTillBlank > 0 THEN { msTillBlank _ msTillBlank - IF msTillBlank <= 0 THEN { vt.SetBWCursorPattern[blankCursor]; }; }; T _ T + 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: hist [] _ hists[FALSE].Show[[name: "Dt vs n (buffered)"]]; hists[TRUE] _ Histograms.Create2D[iMin: minStars, iMax: maxStars, jMin: hist [] _ 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 xp = ( rp = ( zw = ( { &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]}