ViewerBlastImpl.Mesa
Last Edited by: Spreitzer, June 18, 1985 1:51:13 pm PDT
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: BOOLEANTRUE,
backwards: BOOLEANFALSE] =
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: BOOLEANFALSE] 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: BOOLEANTRUE,
backwards: BOOLEANFALSE] =
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] = {
MaskPixelMap[context, bitmap, sOrigin+bitmap.sOrigin+bitmap.sMin, fOrigin+bitmap.fOrigin+bitmap.fMin]
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] = {
DrawBitmap[context, pm, deviceOrgF-pm.fOrigin-pm.fMin, deviceOrgS-pm.sOrigin-pm.sMin]
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.