CDOrientImpl.mesa (part of ChipNDale)
Jacobi, May 21, 1985 11:07:24 am PDT
A package to transform Chip'N Dale rectangles and orientations.
Copyright © 1983, 1985 by Xerox Corporation. All rights reserved.
by Ch. Jacobi, May 12, 1983 5:58 pm
originally written by E. McCreight for Chipmonk, February 22, 1982
last edited by Ch. Jacobi, May 21, 1985 10:59:00 am PDT
DIRECTORY
CDOrient,
Basics,
CD,
Imager,
ImagerTransformation;
CDOrientImpl: CEDAR PROGRAM
IMPORTS Basics, CDOrient, ImagerTransformation
EXPORTS CDOrient =
BEGIN
OPEN CDOrient;
-- Orientation Transformations
-- x increases to the right, y increases upward.
-- An CD.Orientation idx is composed of two parts:
-- a clockwise rotation of (idx/2)*90 degrees,
-- followed by a reflection in x if (idx MOD 2)#0.
MapRect: PUBLIC PROC [itemInCell: CD.Rect, cellSize: CD.Position,
cellInstOrient: CD.Orientation, cellInstPos: CD.Position ← [0,0]]
RETURNS [itemInWorld: CD.Rect←TRASH] =
-- 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
SELECT cellInstOrient FROM
original =>
RETURN[CD.Rect[
x1: cellInstPos.x+itemInCell.x1,
x2: cellInstPos.x+itemInCell.x2,
y1: cellInstPos.y+itemInCell.y1,
y2: cellInstPos.y+itemInCell.y2
]];
mirrorX => -- reflection in x
RETURN[CD.Rect[
x1: cellInstPos.x+cellSize.x-itemInCell.x2,
x2: cellInstPos.x+cellSize.x-itemInCell.x1,
y1: cellInstPos.y+itemInCell.y1,
y2: cellInstPos.y+itemInCell.y2
]];
rotate90 => { -- 90 degrees clockwise
RETURN[CD.Rect[
x1: cellInstPos.x+cellSize.y-itemInCell.y2,
y1: cellInstPos.y+itemInCell.x1,
x2: cellInstPos.x+cellSize.y-itemInCell.y1,
y2: cellInstPos.y+itemInCell.x2]];
};
rotate90X => { -- 90 degrees clockwise followed by reflection in x
RETURN[CD.Rect[
x1: cellInstPos.x+itemInCell.y1,
y1: cellInstPos.y+itemInCell.x1,
x2: cellInstPos.x+itemInCell.y2,
y2: cellInstPos.y+itemInCell.x2]];
};
rotate180 => { -- 180 degrees clockwise
RETURN[CD.Rect[
x1: cellInstPos.x+cellSize.x-itemInCell.x2,
y1: cellInstPos.y+cellSize.y-itemInCell.y2,
x2: cellInstPos.x+cellSize.x-itemInCell.x1,
y2: cellInstPos.y+cellSize.y-itemInCell.y1]];
};
rotate180X => { -- 180 degrees clockwise followed by reflection in x
RETURN[CD.Rect[
x1: cellInstPos.x+itemInCell.x1,
y1: cellInstPos.y+cellSize.y-itemInCell.y2,
x2: cellInstPos.x+itemInCell.x2,
y2: cellInstPos.y+cellSize.y-itemInCell.y1]];
};
rotate270 => { -- 270 degrees clockwise
RETURN[CD.Rect[
x1: cellInstPos.x+itemInCell.y1,
y1: cellInstPos.y+cellSize.x-itemInCell.x2,
x2: cellInstPos.x+itemInCell.y2,
y2: cellInstPos.y+cellSize.x-itemInCell.x1]];
};
rotate270X => { -- 270 degrees clockwise followed by reflection in x
RETURN[CD.Rect[
x1: cellInstPos.x+cellSize.y-itemInCell.y2,
y1: cellInstPos.y+cellSize.x-itemInCell.x2,
x2: cellInstPos.x+cellSize.y-itemInCell.y1,
y2: cellInstPos.y+cellSize.x-itemInCell.x1]];
};
ENDCASE;
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;
MapPoint: PUBLIC PROC [pointInCell: CD.Position, cellSize: CD.Position,
cellInstOrient: Orientation, cellInstPos: CD.Position ← [0,0]]
RETURNS [pointInWorld: CD.Position] =
--Given an point in a prototype cell,
--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 point.
--WARNING: a position of a rect can not be mapped with MapPoint
BEGIN
SELECT cellInstOrient FROM
original =>
RETURN[CD.Position[
x: cellInstPos.x+pointInCell.x,
y: cellInstPos.y+pointInCell.y
]];
mirrorX => -- reflection in x
RETURN[CD.Position[
x: cellInstPos.x+cellSize.x-pointInCell.x,
y: cellInstPos.y+pointInCell.y
]];
rotate90 => { -- 90 degrees clockwise
RETURN[CD.Position[
x: cellInstPos.x+cellSize.y-pointInCell.y,
y: cellInstPos.y+pointInCell.x]];
};
rotate90X => { -- 90 degrees clockwise followed by reflection in x
RETURN[CD.Position[
x: cellInstPos.x+pointInCell.y,
y: cellInstPos.y+pointInCell.x]];
};
rotate180 => { -- 180 degrees clockwise
RETURN[CD.Position[
x: cellInstPos.x+cellSize.x-pointInCell.x,
y: cellInstPos.y+cellSize.y-pointInCell.y]];
};
rotate180X => { -- 180 degrees clockwise followed by reflection in x
RETURN[CD.Position[
x: cellInstPos.x+pointInCell.x,
y: cellInstPos.y+cellSize.y-pointInCell.y]];
};
rotate270 => { -- 270 degrees clockwise
RETURN[CD.Position[
x: cellInstPos.x+pointInCell.y,
y: cellInstPos.y+cellSize.x-pointInCell.x]];
};
rotate270X => { -- 270 degrees clockwise followed by reflection in x
RETURN[CD.Position[
x: cellInstPos.x+cellSize.y-pointInCell.y,
y: cellInstPos.y+cellSize.x-pointInCell.x]];
};
ENDCASE;
END;
DeMapPoint: PUBLIC PROC [pointInWorld: CD.Position, cellSize: CD.Position,
cellInstOrient: Orientation, cellInstPos: CD.Position ← [0,0]]
RETURNS [pointInCell: CD.Position] =
--Given a point in world co-ordinates
--the size of the prototype cell, 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
--co-ordinates of the point.
--WARNING: a position of a rect can not be mapped with DeMapPoint
BEGIN
inverseOrient: CD.Orientation = InverseOrient[cellInstOrient];
pointInCell ← MapPoint[
pointInCell: [
x: pointInWorld.x-cellInstPos.x,
y: pointInWorld.y-cellInstPos.y
],
cellInstOrient: inverseOrient,
cellSize: CDOrient.OrientedSize[size: cellSize, orient: inverseOrient]
];
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.
itemOrientInWorld ← composeTable[itemOrientInCell][cellOrientInWorld]
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..6] ← Basics.BITAND[orient, 6];
IF refl=0 AND rot#0 THEN rot ← 8-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[ composeTable[itemOrientInWorld][InverseOrient[cellOrientInWorld]] ]
END;
ReallyComposeOrient: 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..6] ← LOOPHOLE[Basics.BITAND[cellOrientInWorld, 6]];
IF refl1#0 AND rot2#0 THEN rot2 ← 8-rot2;
itemOrientInWorld ← Basics.BITAND[LOOPHOLE[Basics.BITAND[itemOrientInCell, 6], CARDINAL]+
rot2+LOOPHOLE[Basics.BITXOR[refl1, Basics.BITAND[cellOrientInWorld, 1]], CARDINAL], 7];
END;
CreateTransform: PUBLIC PROC [
cellSize: CD.Position,
cellInstOrient: CD.Orientation,
cellInstPos: CD.Position ← [0,0]]
RETURNS [Imager.Transformation] =
-- Given the size of a cell,
-- the position and orientation of an instance of that cell in "world"
-- co-ordinates, returns the transformation to be applied to
-- cell points to get world points.
BEGIN
RETURN [SELECT cellInstOrient FROM
original => ImagerTransformation.Create[1, 0, cellInstPos.x, 0, 1, cellInstPos.y],
1 =>   ImagerTransformation.Create[-1, 0, cellInstPos.x+cellSize.x, 0, 1, cellInstPos.y], -- reflection in x
rotate90 => ImagerTransformation.Create[0, -1, cellInstPos.x+cellSize.y, 1, 0, cellInstPos.y], -- 90 degrees clockwise
3 =>   ImagerTransformation.Create[0, 1, cellInstPos.x, 1, 0, cellInstPos.y], -- 90 degrees clockwise followed by reflection in x
rotate180 => ImagerTransformation.Create[-1, 0, cellInstPos.x+cellSize.x, 0, -1, cellInstPos.y+cellSize.y], -- 180 degrees clockwise
5 =>   ImagerTransformation.Create[1, 0, cellInstPos.x, 0, -1, cellInstPos.y+cellSize.y], -- 180 degrees clockwise followed by reflection in x
rotate270 => ImagerTransformation.Create[0, 1, cellInstPos.x, -1, 0, cellInstPos.y+cellSize.x], -- 270 degrees clockwise
7 =>   ImagerTransformation.Create[0, -1, cellInstPos.x+cellSize.y, -1, 0, cellInstPos.y+cellSize.x], -- 270 degrees clockwise followed by reflection in x
ENDCASE => ERROR
];
END;
composeTable: ARRAY CD.Orientation OF ARRAY CD.Orientation OF CD.Orientation;
FOR o1: CD.Orientation IN CD.Orientation DO
FOR o2: CD.Orientation IN CD.Orientation DO
composeTable[o1][o2] ← ReallyComposeOrient[o1, o2];
ENDLOOP;
ENDLOOP;
-- 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.