<> <> <> <> 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]; <<[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]; 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; 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 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; avg 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; 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] = { 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]; <= 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 { faceSel: REAL = rs.ChooseInt[1, 100]/100.0 * faceWeights[Back]; SELECT faceSel FROM > faceWeights[Top] => { half half half dp.xw _ Choose[-half dp.yw _ Choose[-half 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, initial 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]]]; }; <> <> <> <> <> <> <<{avx: REAL = ABS[v[X]]; avy: REAL = ABS[v[Y]]; avz: REAL = ABS[v[Z]];>> <> <> <> <> <> IF KeyDown[A] THEN v[Z] _ v[Z] + IF KeyDown[D] THEN v[Z] _ v[Z] - 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; <> <> <<[-width/2, -height/2, depth],>> <<[-width/2, height/2, depth], v]/2;>> <> <> <<[width/2, height/2, depth],>> <<[width/2, -height/2, depth], v]/2;>> <> <> <<[width/2, -height/2, depth],>> <<[-width/2, -height/2, depth], v]/2;>> <> <> <<[-width/2, height/2, depth],>> <<[width/2, height/2, depth], v]/2;>> 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 { 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; }; 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: 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[]; }. p'i = mi (p - ci) mi+1 = ci+1 = ci + vi+1 vi+1 = vi + ai+1 p'i+1 = = mi+1vi+1 = = 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]}