CDOrientImpl.mesa (part of Chipndale)
A package to transform Chip'N Dale rectangles and orientations.
by Ch. Jacobi, May 12, 1983 5:58 pm
originally written by E. McCreight for Chipmonk, February 22, 1982 10:11 AM
last edited by Ch. Jacobi, September 7, 1983 9:29 am
DIRECTORY
CDOrient,
Basics,
CDInline,
CD;
CDOrientImpl: CEDAR PROGRAM
IMPORTS Basics, CDOrient
EXPORTS CDOrient =
BEGIN OPEN CDOrient;
-- Orientation Transformations
-- x increases to the right, y increases downward. Y later changed to increase upward
-- An CD.Orientation idx is composed of two parts:
-- a clockwise rotation of (idx/2)*45 degrees,
-- followed by a reflection in x if (idx MOD 2)#0.
-- Odd multiples of 45 degrees are not yet supported.
MapRect: PUBLIC PROC [itemInCell: CD.Rect, cellSize: CD.Position,
cellInstOrient: CD.Orientation, cellInstPos: CD.Position ← [0,0]]
RETURNS [itemInWorld: CD.Rect] =
-- Given an item in a prototype cell, and the
-- size of the prototype cell, both in "cell" co-ordinates, and
-- the position and orientation index of an instance of that cell in "world"
-- co-ordinates, this procedure returns the world
-- co-ordinates of the instance of the item.
BEGIN
x1, y1, x2, y2, sizeX, sizeY, t: CD.Number;
-- most common case
IF cellInstOrient=original THEN
RETURN[CD.Rect[
x1: cellInstPos.x+itemInCell.x1,
x2: cellInstPos.x+itemInCell.x2,
y1: cellInstPos.y+itemInCell.y1,
y2: cellInstPos.y+itemInCell.y2
]];
[x: sizeX, y: sizeY] ← cellSize;
[x1: x1, x2: x2, y1: y1, y2: y2] ← itemInCell;
SELECT ConcentrateOnRotate90[cellInstOrient] FROM
original => NULL;
rotate90 => { -- 90 degrees clockwise
t ← y1;
y1 ← x1;
x1 ← sizeY-y2;
y2 ← x2;
x2 ← sizeY-t;
sizeX ← sizeY;
};
rotate180 => { -- 180 degrees clockwise
t ← sizeX-x1; x1 ← sizeX-x2; x2 ← t;
t ← sizeY-y1; y1 ← sizeY-y2; y2 ← t;
};
rotate270 => { -- 270 degrees clockwise
t ← x1;
x1 ← y1;
y1 ← sizeX-x2;
x2 ← y2;
y2 ← sizeX-t;
sizeX ← sizeY;
};
ENDCASE;
IF IncludesMirrorX[cellInstOrient] THEN { -- mirror in x
t ← sizeX-x1; x1 ← sizeX-x2; x2 ← t
};
itemInWorld ← [x1: cellInstPos.x+x1, -- translate by cellInstPos
y1: cellInstPos.y+y1, x2: cellInstPos.x+x2,
y2: cellInstPos.y+y2];
END;
DeMapRect: PUBLIC PROC [itemInWorld: CD.Rect, cellSize: CD.Position,
cellInstOrient: CD.Orientation, cellInstPos: CD.Position ← [0,0]]
RETURNS [itemInCell: CD.Rect] =
BEGIN
-- Given an item in world co-ordinates, and the
-- size of a prototype cell, both in "cell" co-ordinates, and
-- the position and orientation index of an instance of that cell in "world"
-- co-ordinates, this procedure returns the cell prototype
-- co-ordinates of that item.
inverseOrient: CD.Orientation = InverseOrient[cellInstOrient];
itemInCell ← MapRect[
itemInCell: [
x1: itemInWorld.x1-cellInstPos.x,
y1: itemInWorld.y1-cellInstPos.y,
x2: itemInWorld.x2-cellInstPos.x,
y2: itemInWorld.y2-cellInstPos.y
],
cellInstOrient: inverseOrient,
cellSize: CDOrient.OrientedSize[size: cellSize, orient: inverseOrient]
];
END;
MapPosition: PUBLIC PROC [itemInCell: CD.Rect, cellSize: CD.Position,
cellInstOrient: CD.Orientation, cellInstPos: CD.Position ← [0,0]]
RETURNS [posInWorld: CD.Position] =
-- Given an position in a prototype cell, and the
-- size of the prototype cell, both in "cell" co-ordinates, and
-- the position and orientation index of an instance of that cell in "world"
-- co-ordinates, this procedure returns the world
-- co-ordinates of the position.
-- This procedure is only speed up from MapRect
BEGIN
x1, y1, x2, y2, sizeX, sizeY, t: CD.Number;
IF cellInstOrient=original THEN -- most common case
RETURN[[
x: cellInstPos.x+itemInCell.x1,
y: cellInstPos.y+itemInCell.y1
]];
[x: sizeX, y: sizeY] ← cellSize;
[x1: x1, x2: x2, y1: y1, y2: y2] ← itemInCell;
SELECT ConcentrateOnRotate90[cellInstOrient] FROM
original => NULL;
rotate90 => { -- 90 degrees clockwise
t ← y1;
y1 ← x1;
x1 ← sizeY-y2;
x2 ← sizeY-t;
sizeX ← sizeY;
};
rotate180 => { -- 180 degrees clockwise
t ← sizeX-x1; x1 ← sizeX-x2; x2 ← t;
y1 ← sizeY-y2;
};
rotate270 => { -- 270 degrees clockwise
x1 ← y1;
y1 ← sizeX-x2;
x2 ← y2;
sizeX ← sizeY;
};
ENDCASE;
IF IncludesMirrorX[cellInstOrient] THEN {x1 ← sizeX-x2};
posInWorld ← [x: cellInstPos.x+x1, y: cellInstPos.y+y1];
END;
ComposeOrient: PUBLIC PROC [
itemOrientInCell, cellOrientInWorld: CD.Orientation]
RETURNS [itemOrientInWorld: CD.Orientation] =
BEGIN
-- This procedure produces the composite orientation
-- obtained by first doing itemOrientInCell and then
-- doing cellOrientInWorld. It uses the observation
-- that a reflection in x followed by a z-degree clockwise
-- rotation is the same as a (360-z)-degree clockwise rotation
-- followed by a reflection in x. Thus if itemOrientInCell
-- contains a final reflection, then cellOrientInWorld's rotation
-- operates in reverse.
refl1: [0..1] ~ Basics.BITAND[itemOrientInCell, 1];
rot2: [0..14] ← LOOPHOLE[Basics.BITAND[cellOrientInWorld, 14]];
IF refl1#0 AND rot2#0 THEN rot2 ← 16-rot2;
itemOrientInWorld ← Basics.BITAND[LOOPHOLE[Basics.BITAND[itemOrientInCell, 14], CARDINAL]+
rot2+LOOPHOLE[Basics.BITXOR[refl1, Basics.BITAND[cellOrientInWorld, 1]], CARDINAL], 15];
END;
InverseOrient: PUBLIC PROC [orient: CD.Orientation]
RETURNS [inverse: CD.Orientation] =
BEGIN
-- For all orientationIndexes o1, ComposeOrient[o1, InverseOrient[o1]] = 0.
-- A reflection in x followed by a z-degree clockwise
-- rotation is the same as a (360-z)-degree clockwise rotation
-- followed by a reflection in x. Thus if orient
-- contains a final reflection, then the inverse rotation
-- operates in the same direction as the forward one.
refl: [0..1] ~ Basics.BITAND[orient, 1];
rot: [0..14] ← Basics.BITAND[orient, 14];
IF refl=0 AND rot#0 THEN rot ← 16-rot;
inverse ← rot+refl;
END;
DecomposeOrient: PUBLIC PROC [
itemOrientInWorld, cellOrientInWorld: CD.Orientation]
RETURNS [itemOrientInCell: CD.Orientation] =
BEGIN
-- For all orientationIndexes o1 and o2,
-- DecomposeOrient[cellOrientInWorld: o1, itemOrientInWorld:
-- ComposeOrient[cellOrientInWorld: o1, itemOrientInCell: o2]] = o2.
RETURN[ComposeOrient[
itemOrientInCell: itemOrientInWorld,
cellOrientInWorld: InverseOrient[cellOrientInWorld]]
];
END;
-- Certification tests, can be commented out for speed of startup
FOR o1: CD.Orientation IN CD.Orientation DO
IF ComposeOrient[o1, InverseOrient[o1]] # 0 THEN ERROR;
FOR o2: CD.Orientation IN CD.Orientation DO
IF DecomposeOrient[cellOrientInWorld: o1, itemOrientInWorld:
ComposeOrient[cellOrientInWorld: o1, itemOrientInCell: o2]] # o2 THEN ERROR;
ENDLOOP;
ENDLOOP;
END.