ViewerBlastImpl.Mesa
Last Edited by: Spreitzer, August 7, 1983 1:53 pm
DIRECTORY Graphics, GraphicsOps, Random, ViewerBlast, ViewerOps;
ViewerBlastImpl:
CEDAR
PROGRAM
IMPORTS Graphics, GraphicsOps, Random, ViewerOps
EXPORTS ViewerBlast =
BEGIN OPEN ViewerBlast;
Context: TYPE = Graphics.Context;
rows, cols: INTEGER = 8;
pieces: ARRAY [0 .. rows) OF ARRAY [0 .. cols) OF Piece;
Piece:
TYPE =
RECORD [
bmw: IntBox,
loc: IntPt, --screen coords of center of piece
speed: IntPt];
screenBitmap: Bitmap ← GraphicsOps.ScreenBitmap[];
screenBox: IntBox ← [0, 0, screenBitmap.width, screenBitmap.height];
screenContext: Context ← Graphics.NewContext[];
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
[
bm: Bitmap,
bmw: IntBox, --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;
hcheight ← bmw.dy/(rows*2);
hcwidth ← bmw.dx/(cols*2);
bloated ← BloatBox[screenBox, hcwidth+1, hcheight+1];
Graphics.SetCP[screenContext, 0, 0];
[] ← Graphics.SetPaintMode[screenContext, invert];
FOR row
IN [0 .. rows)
DO
FOR col
IN [0 .. cols)
DO
loc: IntPt;
needs: INTEGER;
pieces[row][col].bmw.xmin ← (bmw.dx*col + cols-1)/cols;
pieces[row][col].bmw.ymin ← (bmw.dy*row + rows-1)/rows;
pieces[row][col].bmw.dx ← (bmw.dx*(col+1) + cols-1)/cols - pieces[row][col].bmw.xmin;
pieces[row][col].bmw.dy ← (bmw.dy*(row+1) + rows-1)/rows - pieces[row][col].bmw.ymin;
pieces[row][col].loc ← loc ← [
lowerLeft.x + hcwidth + pieces[row][col].bmw.xmin,
lowerLeft.y + bmw.dy - (hcheight + pieces[row][col].bmw.ymin)];
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 ←
IF pieces[row][col].speed.x = 0
THEN
IF pieces[row][col].speed.y = 0
THEN ERROR
ELSE Ceiling[edge.y - loc.y, pieces[row][col].speed.y]
ELSE
IF pieces[row][col].speed.y = 0
THEN Ceiling[edge.x - loc.x, pieces[row][col].speed.x]
ELSE
MIN[
Ceiling[edge.y - loc.y, pieces[row][col].speed.y],
Ceiling[edge.x - loc.x, pieces[row][col].speed.x]];
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 GraphicsOps.DrawBitmap[
self: screenContext,
bitmap: bm,
w: pieces[row][col].bmw.dx,
h: pieces[row][col].bmw.dy,
x: pieces[row][col].bmw.xmin,
y: pieces[row][col].bmw.ymin,
xorigin: hcwidth + pieces[row][col].bmw.xmin - pieces[row][col].loc.x,
yorigin: hcheight + pieces[row][col].bmw.ymin + pieces[row][col].loc.y];
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 GraphicsOps.DrawBitmap[
self: screenContext,
bitmap: bm,
w: pieces[row][col].bmw.dx,
h: pieces[row][col].bmw.dy,
x: pieces[row][col].bmw.xmin,
y: pieces[row][col].bmw.ymin,
xorigin: hcwidth + pieces[row][col].bmw.xmin - pieces[row][col].loc.x,
yorigin: hcheight + pieces[row][col].bmw.ymin + pieces[row][col].loc.y];
ENDLOOP;
ENDLOOP;
ENDLOOP;
lastSteps ← steps;
END;
lastSteps: INTEGER;
rate1: INTEGER ← 50;
rate2: INTEGER ← 100;
RandomSpeed:
PUBLIC SpeedChooser = {
speed ← [0, 0];
WHILE
ABS[speed.x] < rate1
AND
ABS[speed.y] < rate1
DO
speed ← [Random.Choose[-rate2, rate2], Random.Choose[-rate2, rate2]]
ENDLOOP};
Choose:
PROC [min, max:
INTEGER]
RETURNS [choice:
INTEGER] =
{choice ← Random.Choose[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 [bm: Bitmap, x, y:
INTEGER] =
BEGIN
c: Context;
w, h: INTEGER;
bm ← GraphicsOps.NewBitmap[v.ww, v.wh];
c ← GraphicsOps.NewContextFromBitmap[bm];
Graphics.SetCP[c, 0, 0];
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.coordSys = top THEN y ← y - v.wh};
GraphicsOps.DrawBitmap[self: c, bitmap: screenBitmap, w: w, h: h, x: x, y: screenBox.ymin + screenBox.dy - (y + h), xorigin: x, yorigin: screenBox.ymin + screenBox.dy - (y)];
END;
BlastViewer:
PROC
[
v: Viewer,
convergeOn: IntPt ← [],
preSteps: INTEGER ← 3,
speedChooser: SpeedChooser ← NIL,
initial: BOOLEAN ← TRUE,
backwards: BOOLEAN ← FALSE] =
BEGIN
left, bottom: INTEGER;
bm: Bitmap;
[bm, left, bottom] ← CopyViewer[v];
Blast[bm: bm, bmw: [0, 0, v.ww, v.wh], lowerLeft: [left, bottom], convergeOn: convergeOn, preSteps: preSteps, speedChooser: speedChooser, initial: initial, backwards: backwards];
END;
DrawBitmap:
PUBLIC
PROC
[
self: Context, bitmap: Bitmap,
w, h: INTEGER, -- width and height of rectangle
x, y: INTEGER ← 0, -- upper left corner of rectangle
xorigin, yorigin: INTEGER ← 0 -- point to be aligned with cp
] =
BEGIN
IF x < 0 THEN {w ← w + x; x ← 0};
IF y < 0 THEN {h ← h + y; y ← 0};
IF w <= 0 OR h <= 0 THEN RETURN;
GraphicsOps.DrawBitmap[self: self, bitmap: bitmap, w: w, h: h, x: x, y: y, xorigin: xorigin, yorigin: yorigin];
END;
END.