<> <> DIRECTORY Imager, ImagerBackdoor, ImagerDevice, ImagerPixelMap, ImagerRaster, ImagerTerminal, Random, Terminal, ViewerBlast, ViewerOps; ViewerBlastImpl: CEDAR PROGRAM IMPORTS Imager, ImagerBackdoor, ImagerPixelMap, ImagerRaster, ImagerTerminal, Random, Terminal, ViewerOps EXPORTS ViewerBlast = BEGIN OPEN ViewerBlast; rows, cols: INTEGER = 8; Piece: TYPE = RECORD [ bmw: ImagerPixelMap.DeviceRectangle, --where this piece is in screen bitmap loc: IntPt, --screen coords of center of piece speed: IntPt --in screen coords ]; screenBitmap: PixelMap _ ScreenPixelMap[]; screenBox: IntBox _ [0, 0, screenBitmap.fSize, screenBitmap.sSize]; screenContext: Context _ ImagerTerminal.BWContext[Terminal.Current[], TRUE]; ScreenPixelMap: PUBLIC PROC RETURNS [pm: PixelMap] = TRUSTED { fb: Terminal.FrameBuffer _ Terminal.Current[].GetBWFrameBuffer[]; IF fb.bitsPerPixel # 1 THEN ERROR; pm _ ImagerPixelMap.CreateFrameBuffer[ pointer: fb.base, words: LONG[fb.wordsPerLine] * fb.height, lgBitsPerPixel: 0, rast: fb.wordsPerLine, lines: fb.height, ref: fb.vm]; }; RayToBox: PROC [from, to: IntPt, bloated: IntBox] RETURNS [edge: IntPt] = BEGIN dx: INTEGER _ to.x - from.x; dy: INTEGER _ to.y - from.y; IF dy = 0 THEN RETURN [ [x: IF dx = 0 THEN ERROR ELSE IF dx > 0 THEN bloated.xmin + bloated.dx ELSE bloated.xmin, y: to.y]]; IF dx = 0 THEN RETURN [ [x: to.x, y: IF dy = 0 THEN ERROR ELSE IF dy > 0 THEN bloated.ymin + bloated.dy ELSE bloated.ymin]]; edge.y _ ((INT[IF dx > 0 THEN bloated.xmin + bloated.dx ELSE bloated.xmin] - from.x) * dy)/dx + from.y; edge.y _ IF dy > 0 THEN MIN[edge.y, bloated.ymin + bloated.dy] ELSE MAX[edge.y, bloated.ymin]; edge.x _ ((INT[IF dy > 0 THEN bloated.ymin + bloated.dy ELSE bloated.ymin] - from.y) * dx)/dy + from.x; edge.x _ IF dx > 0 THEN MIN[edge.x, bloated.xmin + bloated.dx] ELSE MAX[edge.x, bloated.xmin]; END; BloatBox: PROC [box: IntBox, dx, dy: INTEGER] RETURNS [bloated: IntBox] = { RETURN [[box.xmin - dx, box.ymin - dy, box.dx + 2*dx, box.dy + 2*dy]]}; SGN: PROC [x: INTEGER] RETURNS [sx: INTEGER] = {sx _ IF x > 0 THEN 1 ELSE IF x < 0 THEN -1 ELSE 0}; Ceiling: PROC [num, den: INTEGER] RETURNS [quot: INTEGER] = {quot _ num/den; quot _ quot + SGN[num]/SGN[den]; quot _ (num - quot*den)/den + quot}; --what this actually does is round away from 0-- Blast: PUBLIC PROC [ pm: PixelMap, --bitmap coords (y measured from top) lowerLeft: IntPt, --of where initial bitmap appears on screen convergeOn: IntPt _ [], --screen point for all centers to emanate from preSteps: INTEGER _ 3, speedChooser: SpeedChooser _ NIL, initial: BOOLEAN _ TRUE, backwards: BOOLEAN _ FALSE] = BEGIN row, col: INTEGER; hcheight, hcwidth: INTEGER; step, steps: INTEGER _ 0; bloated: IntBox; edge: IntPt; pieces: ARRAY [0 .. rows) OF ARRAY [0 .. cols) OF Piece; hcheight _ pm.sSize/(rows*2); hcwidth _ pm.fSize/(cols*2); bloated _ BloatBox[screenBox, hcwidth+1, hcheight+1]; Imager.SetColor[screenContext, ImagerBackdoor.invert]; FOR row IN [0 .. rows) DO FOR col IN [0 .. cols) DO loc: IntPt; needs: INTEGER; fo: NAT _ (pm.fSize*col + cols-1)/cols; so: NAT _ (pm.sSize*row + rows-1)/rows; pieces[row][col].bmw.fMin _ pm.fOrigin + pm.fMin + fo; pieces[row][col].bmw.sMin _ pm.sOrigin + pm.sMin + so; pieces[row][col].bmw.fSize _ (pm.fSize*(col+1) + cols-1)/cols - fo; pieces[row][col].bmw.sSize _ (pm.sSize*(row+1) + rows-1)/rows - so; pieces[row][col].loc _ loc _ [ lowerLeft.x + hcwidth + fo, lowerLeft.y + pm.sSize - (hcheight + so)]; IF speedChooser = NIL THEN BEGIN edge _ RayToBox[convergeOn, loc, bloated]; pieces[row][col].speed _ [ Ceiling[loc.x - convergeOn.x, preSteps], Ceiling[loc.y - convergeOn.y, preSteps]]; END ELSE BEGIN pieces[row][col].speed _ speedChooser[row, col]; edge _ [bloated.xmin, bloated.ymin]; IF pieces[row][col].speed.x > 0 THEN edge.x _ edge.x + bloated.dx; IF pieces[row][col].speed.y > 0 THEN edge.y _ edge.y + bloated.dy; END; needs _ MIN[ IF pieces[row][col].speed.x # 0 THEN Ceiling[edge.x - loc.x, pieces[row][col].speed.x] ELSE LAST[INTEGER], IF pieces[row][col].speed.y # 0 THEN Ceiling[edge.y - loc.y, pieces[row][col].speed.y] ELSE LAST[INTEGER]]; IF needs = LAST[INTEGER] THEN ERROR; steps _ MAX[steps, needs]; ENDLOOP; ENDLOOP; IF backwards THEN BEGIN FOR row IN [0 .. rows) DO FOR col IN [0 .. cols) DO pieces[row][col].loc.x _ pieces[row][col].loc.x + pieces[row][col].speed.x * (steps); pieces[row][col].loc.y _ pieces[row][col].loc.y + pieces[row][col].speed.y * (steps); pieces[row][col].speed.x _ -pieces[row][col].speed.x; pieces[row][col].speed.y _ -pieces[row][col].speed.y; ENDLOOP; ENDLOOP; END; FOR step IN [0 .. steps) DO drawOld, drawNew: BOOLEAN; drawOld _ step # 0 OR (initial AND NOT backwards); drawNew _ step # (steps-1) OR (initial AND backwards); FOR row IN [0 .. rows) DO FOR col IN [0 .. cols) DO IF drawOld THEN DrawPixelMap[ context: screenContext, pm: pm.Clip[pieces[row][col].bmw], tx: pieces[row][col].loc.x - hcwidth, ty: pieces[row][col].loc.y + hcheight]; pieces[row][col].loc.x _ pieces[row][col].loc.x + pieces[row][col].speed.x; pieces[row][col].loc.y _ pieces[row][col].loc.y + pieces[row][col].speed.y; IF drawNew THEN DrawPixelMap[ context: screenContext, pm: pm.Clip[pieces[row][col].bmw], tx: pieces[row][col].loc.x - hcwidth, ty: pieces[row][col].loc.y + hcheight]; ENDLOOP; ENDLOOP; ENDLOOP; lastSteps _ steps; END; lastSteps: INTEGER; rate1: INTEGER _ 50; rate2: INTEGER _ 100; rs: Random.RandomStream _ Random.Create[seed: -1]; RandomSpeed: PUBLIC SpeedChooser = { speed _ [0, 0]; WHILE ABS[speed.x] < rate1 AND ABS[speed.y] < rate1 DO speed _ [rs.ChooseInt[-rate2, rate2], rs.ChooseInt[-rate2, rate2]] ENDLOOP}; Choose: PROC [min, max: INTEGER] RETURNS [choice: INTEGER] = {choice _ rs.ChooseInt[min - (1+max-min), max]; IF choice < min THEN choice _ choice - 2*min + 1}; CopyViewer: PUBLIC PROC [v: Viewer, window: IntBox _ [], windowed: BOOLEAN _ FALSE] RETURNS [pm: PixelMap, lowerLeft: IntPt] = BEGIN x, y, w, h, by: INTEGER; vpm: PixelMap; IF windowed THEN { [x, y] _ ViewerOps.UserToScreenCoords[v, window.xmin, window.ymin]; w _ window.dx; h _ window.dy; } ELSE IF v.parent = NIL THEN { x _ v.wx; y _ v.wy; w _ v.ww; h _ v.wh} ELSE { [x, y] _ ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy]; w _ v.ww; h _ v.wh; IF v.parent.class.topDownCoordSys THEN y _ y - v.wh}; by _ screenBox.ymin + screenBox.dy - (y + h); vpm _ screenBitmap.Clip[[ sMin: by, fMin: x, sSize: h, fSize: w]]; pm _ ImagerPixelMap.Create[0, [sMin: by, fMin: x, sSize: h, fSize: w]]; pm.Transfer[vpm]; lowerLeft _ [x, y]; END; BlastViewer: PROC [ v: Viewer, convergeOn: IntPt _ [], preSteps: INTEGER _ 3, speedChooser: SpeedChooser _ NIL, initial: BOOLEAN _ TRUE, backwards: BOOLEAN _ FALSE] = BEGIN left, bottom: INTEGER; pm: PixelMap; [pm, [left, bottom]] _ CopyViewer[v]; Blast[pm: pm, lowerLeft: [left, bottom], convergeOn: convergeOn, preSteps: preSteps, speedChooser: speedChooser, initial: initial, backwards: backwards]; END; PixelMapContext: PUBLIC PROC [pm: PixelMap] RETURNS [context: Context] = { device: ImagerDevice.Device _ ImagerRaster.NewBitmapDevice[pm]; context _ ImagerRaster.Create[device: device, pixelUnits: TRUE]; }; DrawPixelMap: PUBLIC PROC [context: Context, pm: PixelMap, tx, ty: INTEGER] = { Imager.MaskBits[ context: context, base: pm.refRep.pointer, wordsPerLine: pm.refRep.rast, sMin: pm.sMin, fMin: pm.fMin, sSize: pm.sSize, fSize: pm.fSize, tx: tx, ty: ty]; }; DrawBitmap: PUBLIC PROC [context: Context, bitmap: PixelMap, fOrigin, sOrigin: INTEGER] = { <> Imager.MaskBits[ context: context, base: bitmap.refRep.pointer, wordsPerLine: bitmap.refRep.rast, sMin: bitmap.sMin, fMin: bitmap.fMin, sSize: bitmap.sSize, fSize: bitmap.fSize, tx: -fOrigin, ty: sOrigin]; }; MaskPixelMap: PUBLIC PROC [context: Context, pm: PixelMap, deviceOrgS, deviceOrgF: INTEGER] = { <> Imager.MaskBits[ context: context, base: pm.refRep.pointer, wordsPerLine: pm.refRep.rast, sMin: pm.sMin, fMin: pm.fMin, sSize: pm.sSize, fSize: pm.fSize, tx: -(deviceOrgF-pm.fOrigin-pm.fMin), ty: deviceOrgS-pm.sOrigin-pm.sMin]; }; END.