CornerStitching.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Written by Mark Shand, September 12, 1983 11:40 pm
Last Edited by: Shand, October 20, 1983 6:46 pm
Last Edited by: Mccreight, November 28, 1983 5:53 pm
Last Edited by: Jacobi, February 24, 1984 12:14 pm
Rick Beach, June 6, 1985 2:06:43 pm PDT
CornerStitching: CEDAR DEFINITIONS
~ BEGIN
Rectangles and Positions
Number: TYPE = INT;
Position: TYPE = RECORD [x, y: Number];
Rect:
TYPE =
RECORD [x1, y1, x2, y2: Number];
--a Rect is called normalized if (x1>x2) OR (y1>y2) means that the Rect is empty.
--Rect's are closed: they include all the endpoints; as you expect, points have size 0.
Creating Tile Worlds.
NewTesselation: PROCEDURE RETURNS [REF Tesselation];
FreeTesselation: PROCEDURE [plane: REF Tesselation];
CoerceToTesselation: PROCEDURE [ref: REF ANY] RETURNS [REF Tesselation]; -- may raise NarrowRefFault
Modifying the Tile World.
ChangeRect: PROCEDURE [plane: REF Tesselation, rect: Rect, newvalue: REF ANY ← NIL, checkOldvalue: BOOLEAN ← TRUE, oldvalue: REF ANY ← NIL]; -- Raises TileValue ERROR if checkOldvalue AND the value of a tile in rect # oldvalue.
SafeChangeTile:
PROCEDURE [plane:
REF Tesselation, tile: TilePtr, newvalue:
REF
ANY] ~
INLINE {
-- Let a client change a tile provided it doesn't violate invariants.
TRUSTED {
IF tile.ne.value = newvalue
OR tile.sw.value = newvalue
OR (tile.en.value = newvalue AND tile.en.pos.x = tile.pos.x AND tile.en.ne.pos.x = tile.ne.pos.x)
OR (tile.ws.value = newvalue AND tile.ws.pos.x = tile.pos.x AND tile.ws.ne.pos.x = tile.ne.pos.x) THEN ERROR
};
tile.value ← newvalue
};
Tile Enumeration routines.
All Enumeration routines take a backgroundValue parameter, tiles with this value are ignored. If you want every tile, set backgroundValue to some value you know no tile has (e.g. NEW[INT]). perTile is called for each Tile found. If perTile = NIL then returns LIST OF REF Region, the list contains each tile not of backgroundValue clipped to rect. If ChangeRect is called from perTile during an Enumeration results are unpredictable!
PerTileProc: TYPE ~ PROCEDURE [tile: TilePtr, data: REF ANY] RETURNS [REF ANY];
In EnumerateArea the order of enumeration is well defined. A tile appears in the enumeration immediately after the last of its children. Where tileA is defined to be a child of tileB iff its sw stitch points to tileB. Children are order such that the southernmost is the last. For tiles touching the western and southern these rules do not apply, these tiles are parent-less. Such tiles appear at some point after all the parent-less tiles touching touching a given parent-less tile's northern and western borders have been enumerated.
EnumerateArea:
PROCEDURE [plane:
REF Tesselation, rect: Rect, perTile: PerTileProc ←
NIL, data:
REF
ANY ←
NIL, backgroundValue:
REF
ANY ←
NIL]
RETURNS [
REF
ANY];
-- If perTile = NIL then returns LIST OF REF Region;
TileAt: PROCEDURE [plane: REF Tesselation, pos: Position] RETURNS [TilePtr];
Stepping from Tile to Tile.
Co-ordinates increase from south to north, and west to east. All tile worlds are bordered by tile at 'infinity' (i.e. INT.FIRST or INT.LAST), so bounded searches never need worry about tile = NIL. The tiles at infinity are not stitched to tiles in the Tesselation, rather they are stitched to each other in the manner of the bevelled edges of a picture frame. The meanings of the Stepping routines are as follows ...
ENorthNeighbour — eastern most tile to the north
NEastNeighbour — northern most tile to the east
WSouthNeighbour — western most tile to the south
SWestNeighbour — southern most tile to the west
ENorthNeighbour:
PROCEDURE [tile: TilePtr]
RETURNS [TilePtr] ~
INLINE {
RETURN [tile.en]
};
NEastNeighbour:
PROCEDURE [tile: TilePtr]
RETURNS [TilePtr] ~
INLINE {
TRUSTED { RETURN [LOOPHOLE[tile.ne]] }
};
WSouthNeighbour:
PROCEDURE [tile: TilePtr]
RETURNS [TilePtr] ~
INLINE {
TRUSTED { RETURN [LOOPHOLE[tile.ws]] }
};
SWestNeighbour:
PROCEDURE [tile: TilePtr]
RETURNS [TilePtr] ~
INLINE {
RETURN [tile.sw]
};
Tile Enquiries
Value:
PROCEDURE [tile: TilePtr]
RETURNS [
REF
ANY] ~
INLINE {
RETURN [tile.value]
};
Area:
PROCEDURE [tile: TilePtr]
RETURNS [Rect] ~
INLINE {
RETURN [[WestEdge[tile], SouthEdge[tile], EastEdge[tile], NorthEdge[tile]]]
};
NorthEdge: PROCEDURE [t: TilePtr] RETURNS [INT] ~ INLINE {RETURN [t.en.pos.y]};
EastEdge: PROCEDURE [t: TilePtr] RETURNS [INT] ~ INLINE { TRUSTED { RETURN [t.ne.pos.x] } };
SouthEdge: PROCEDURE [t: TilePtr] RETURNS [INT] ~ INLINE {RETURN [t.pos.y]};
WestEdge: PROCEDURE [t: TilePtr] RETURNS [INT] ~ INLINE {RETURN [t.pos.x]};
Area Enquiries.
AreaEmpty: PROCEDURE [plane: REF Tesselation, rect: Rect, backgroundValue: REF ANY ← NIL] RETURNS [BOOLEAN];
ContentsBound:
PROCEDURE [plane:
REF Tesselation, rect: Rect, backgroundValue:
REF
ANY ←
NIL]
RETURNS [bBox: Rect];
-- ContentsBound returns a minimal bounding box for non-backgroundValue tiles in rect
ERROR Codes.
TileValue: ERROR;
TileDeleted: ERROR;
Types.
Well we pushed it and we broke it, I'm talking about SafeStorage reference counting. Of course it'll all be fixed come Cedar 4.4, fine but I'm leaving on the 21st of October. Anyway corner stitching is too circular so some of the stitches are pointers (idea courtesy of Mark Brown). We are careful to stitch in a way that connects every tile through a chain of REFs to the southeast tile, which we hang on to in the Tesselation record. Actually there is one benefit, now to free a Tesselation you only need drop this REF.
TilePtr: TYPE ~ REF Tile;
Tile:
PRIVATE
TYPE ~
RECORD [
Always leave tile in second direction, i.e en = NorthEast corner, facing North; ne faces East.
en, sw: REF Tile ← NIL,
ne, ws: LONG POINTER TO Tile ← NIL,
pos: Coord,
value: REF ANY ← NIL -- NIL for space tiles
];
Coord: PRIVATE TYPE ~ RECORD [x, y: INT];
Region:
TYPE ~
RECORD [
rect: Rect,
value: REF ANY ← NIL -- NIL for space tiles
];
Tesselation:
TYPE ~
RECORD [
southEast, current: REF Tile ←,
deletionCache: REF Tile ← NIL,
tilesInTesselationCount: INT ← 1
];
END.