copy -c /ivy/jacobi/temp/CSImpl.mesa ← CSImpl.mesa
Jacobi, January 30, 1986 6:46:52 pm PST
Copyright © 1983, 1986 by Xerox Corporation. All rights reserved.
Written by Shand, September 12, 1983 11:40 pm PDT
Last Edited by: McCreight, November 28, 1983 5:53 pm
Last Edited by: Jacobi, February 23, 1984 3:59 pm
Last Edited by: Shand, August 6, 1984 4:16:51 am PDT
Last Edited by: Jacobi, January 20, 1986 4:13:45 pm PST
Last Edited by: Beretta, February 13, 1985 11:46:26 am PST
DIRECTORY
Commander, CS, IO, Process, SafeStorage;
CSImpl:
CEDAR
MONITOR
IMPORTS Commander, CS, IO, Process, SafeStorage
EXPORTS CS =
BEGIN
OPEN CS;
UseOfAlreadyDeletedTile: ERROR = CODE;
callErr: ERROR = CODE;
-- Co-ordinates at "infinity"
neLimit: Pos = [x: Number.LAST, y: Number.LAST];
nwLimit: Pos = [x: Number.FIRST, y: Number.LAST];
swLimit: Pos = [x: Number.FIRST, y: Number.FIRST];
seLimit: Pos = [x: Number.LAST, y: Number.FIRST];
allSpace: Rect = [x1: Number.FIRST+1, y1: Number.FIRST, x2: Number.LAST-1, y2: Number.LAST-1];
maxSpace: Rect = [x1: allSpace.x1+1, y1: allSpace.y1+1, x2: allSpace.x2-1, y2: allSpace.y2-1];
empty: Rect = [x1: Number.LAST, y1: Number.LAST, x2: Number.FIRST, y2: Number.FIRST];
-- House-keeping tile values to flag deleted tiles and tiles at "infinity"
PrivateRec: TYPE = RECORD[notused: REF];
guard: REF = NEW[PrivateRec←[$CSGuard]];
deleted: REF = NEW[PrivateRec←[$CSDeleted]];
-- Border Tiles (shared by all tesselations)
northLimit: Tile = NEW[TileRec ← [pos: nwLimit, value: guard]];
northBuffer: Tile = NEW[TileRec ← [pos: [allSpace.x1, allSpace.y2], value: guard]];
southLimit: Tile = NEW[TileRec ← [pos: swLimit, value: guard]];
eastLimit: Tile = NEW[TileRec ← [pos: seLimit, value: guard]];
westLimit: Tile = NEW[TileRec ← [pos: swLimit, value: guard]];
cache: Tile ← NIL;
InitTesselationBorderTiles:
PROC =
BEGIN
-- The stitching is not quite kosher at Guard corners.
-- Think of the guard tile as having bevelled ends
-- which fit together like a picture-frame and are stitched accordingly.
-- North-East
northLimit.nE ← P[eastLimit];
eastLimit.eN ← northLimit;
-- South-East
southLimit.eN ← eastLimit;
southLimit.nE ← P[eastLimit];
eastLimit.wS ← P[southLimit];
eastLimit.sW ← southLimit;
-- North-West
westLimit.nE ← P[northLimit];
westLimit.eN ← northLimit;
northLimit.sW ← westLimit;
northLimit.wS ← P[westLimit];
-- South-West
southLimit.sW ← westLimit;
westLimit.wS ← P[southLimit];
-- northBuffer
northBuffer.eN ← northLimit;
northBuffer.nE ← P[eastLimit];
northBuffer.sW ← westLimit;
northBuffer.wS ← NIL;
END;
DumpCache:
PUBLIC
ENTRY
PROC =
BEGIN
ENABLE UNWIND => NULL;
IF cache#
NIL
THEN {
lag: Tile ← cache;
FOR tc: Tile ← cache.eN, tc.eN
WHILE tc#
NIL
DO
lag.eN ← NIL; lag ← tc
ENDLOOP;
cache ← NIL
};
END;
NewTile:
ENTRY
PROC
RETURNS [tile: Tile] =
BEGIN
ENABLE UNWIND => NULL;
tile ← InternalNewTile[];
END;
InternalNewTile:
INTERNAL
PROC
RETURNS [tile: Tile] =
INLINE
BEGIN
IF cache#
NIL
THEN {
tile ← cache;
cache ← cache.eN;
}
ELSE tile ← NEW[TileRec];
END;
DisposeTile:
ENTRY
PROC [tile: Tile] =
--caller must check that it is not a current tile
--and cache the tile, this is faster than going through the garbage collector
BEGIN
ENABLE UNWIND => NULL;
tile.sW ← NIL; tile.nE ← tile.wS ← NIL; -- prevent potential circularity.
tile.value ← deleted;
tile.eN ← cache; cache ← tile;
END;
Setup:
INTERNAL
PROC [plane: Tesselation] =
BEGIN
eastSpace: Tile = InternalNewTile[];
centreSpace: Tile = InternalNewTile[];
eastSpace^ ← [
eN: northBuffer, nE: P[eastLimit], sW: centreSpace, wS: P[southLimit],
pos: [allSpace.x2, allSpace.y1],
value: guard];
centreSpace^ ← [
eN: northBuffer, nE: P[eastSpace], sW: westLimit, wS: P[southLimit],
pos: [allSpace.x1, allSpace.y1],
value: NIL];
plane^ ← [southEast: eastSpace, current: centreSpace, data: NIL, stopFlag: plane.stopFlag, tileCount: 1];
IF plane.stopFlag=NIL THEN plane.stopFlag ← NEW[BOOL←FALSE];
END;
NewTesselation:
PUBLIC
ENTRY
PROC [data:
REF←
NIL, stopFlag:
REF
BOOL←
NIL]
RETURNS [plane: Tesselation] =
BEGIN
plane ← NEW[CS.TesselationRec←[[], FALSE, NIL, NIL, 1, stopFlag, data]];
Setup[plane];
##SafeStorage.EnableFinalization[plane];
END;
ResetTesselation:
PUBLIC
PROC [plane: Tesselation] =
--This is to help the garbage collector (by NILing all REFs).
BEGIN
CacheTileList:
PROC [header: Tile] = {
WHILE header#
NIL
DO
t: Tile ← header; header ← header.eN; DisposeTile[t];
ENDLOOP;
};
CacheIt: TileProc = {
-- Depends on fact that Enumeration proceeds NE to SW,
-- so eN ref may be clobbered by caching process.
tile.value ← deleted; tile.eN ← header; header ← tile;
};
MySetup:
ENTRY
PROC [plane: Tesselation] = {
Setup[plane];
};
header: Tile ← NIL;
IF plane#
NIL
THEN {
EnumerateArea[plane, maxSpace, CacheIt, NIL, deleted];
CacheTileList[header];
MySetup[plane];
};
END;
CompToN:
PROC [t: Tile]
RETURNS [
BOOL] =
INLINE {
--tile is compatible with its north neighbour (except maybe range problems)
RETURN [t.value=t.eN.value AND t.pos.x=t.eN.pos.x AND NE[t].pos.x=NE[t.eN].pos.x]
};
ChangeRect:
PUBLIC
PROC [plane: Tesselation, rect: Rect, new:
REF
ANY ←
NIL] =
--we dont check left and right border with state
--state.rect left and right must be pre-split
BEGIN
SplitHorizontal:
PROC [plane: Tesselation, splitY, x1, x2: Number] =
INLINE
--only splits if value # new
--split must be in legal range; x1<x2
BEGIN
tile: Tile ← Find[plane.current, x1, splitY-1];
--tile rides from west to east, containing the point below the split line
plane.current ← tile;
DO
--Assert: tile.pos.x<x2
IF NEdge[tile]=splitY
THEN {
tile ← NE[tile];
IF tile.pos.x>=x2 THEN EXIT;
}
ELSE
IF tile.value=new
THEN {
IF EEdge[tile]>=x2 THEN EXIT;
tile ← RightOf[tile, splitY-1];
}
ELSE {
[] ← NSSplit[plane, tile, splitY];
tile ← NE[tile];
IF tile.pos.x>=x2 THEN EXIT;
}
ENDLOOP
END;
SplitHorizontal: PROC [plane: Tesselation, splitY, x1, x2: Number] = INLINE
--only splits if value # new
--split must be in legal range; x1<x2
BEGIN
tile: Tile;
plane.current ← tile ← Find[plane.current, x1, splitY];
DO
--Assert: tile.pos.x<x2
IF tile.pos.y=splitY THEN {
--correct but slowish: tile ← RightOf[tile, splitY]
--east-west property tells: its faster to move eastwards
IF EEdge[tile]>=x2 THEN RETURN;
tile ← WS[tile];
WHILE NEdge[tile]<=splitY DO
tile ← NE[tile];
IF tile.pos.x>=x2 THEN RETURN;
ENDLOOP
}
ELSE IF tile.value=new THEN {
IF EEdge[tile]>=x2 THEN RETURN;
tile ← RightOf[tile, splitY];
}
ELSE tile ← NSSplit[plane, tile, splitY].north;
ENDLOOP
END;
tile: Tile;
rect.x1 ← MAX[maxSpace.x1, rect.x1];
rect.x2 ← MIN[maxSpace.x2, rect.x2];
rect.y1 ← MAX[maxSpace.y1, rect.y1];
rect.y2 ← MIN[maxSpace.y2, rect.y2];
IF rect.x1>=rect.x2 OR rect.y1>=rect.y2 THEN RETURN;
IF plane.immute OR rect.x1<plane.state.r.x1 OR rect.y1<plane.state.r.y1 OR rect.x2>plane.state.r.x2 OR rect.y2>plane.state.r.y2 THEN ERROR callErr;
SplitHorizontal[plane, rect.y1, rect.x1, rect.x2];
SplitHorizontal[plane, rect.y2, rect.x1, rect.x2];
-- change values.
-- tile containing nw corner neighbour (but inside area of interest)
tile ← Find[plane.current, rect.x1, rect.y2-1];
DO
--I tile touches west border
--I all northern tiles are already handled
-- split out left part of tile if necessary.
IF tile.value#new
AND WEdge[tile]<rect.x1
THEN {
--wont enter with e-w border problems
left: Tile;
tile ← EWSplit[plane, tile, rect.x1].east;
left ← tile.sW;
-- make sure we keep outside tile in order NS-wise.
IF CompToN[left] THEN left ← NSMerge[plane, left, left.eN];
IF CompToN[
WS[left]]
THEN
IF WS[left].pos.y>=plane.state.r.y1 THEN [] ← NSMerge[plane, WS[left], left];
};
DO
--loop rides tile eastwards and changes value until border is hit
IF tile.value#new
THEN {
-- right border
IF EEdge[tile]>rect.x2
THEN {
--wont enter with e-w border problems
right: Tile ← EWSplit[plane, tile, rect.x2].east;
IF CompToN[right] THEN right ← NSMerge[plane, right, right.eN];
IF CompToN[
WS[right]]
THEN
IF WS[right].pos.y>=plane.state.r.y1 THEN [] ← NSMerge[plane, WS[right], right];
};
tile ← RestrictedMyChangeTile[plane, tile, new]; --returns northernmost tile
IF WEdge[tile]>rect.x1 THEN ERROR;
};
--I tile has value
IF EEdge[tile]>=rect.x2 THEN EXIT;
tile ← NE[tile];
WHILE tile.pos.y>=rect.y2 DO tile ← WS[tile] ENDLOOP; -- EMM
--I tile.s <=rect.y2
ENDLOOP;
--I tile touches west border
--I tile touches east border and all northern tiles are already handled
IF WEdge[tile]>rect.x1 THEN ERROR;
IF SEdge[tile]<=rect.y1 THEN EXIT;
--set tile back up and left; remember, the northern tiles are merged already
tile ← BelowOf[tile, rect.x1];
ENDLOOP
END;
ChangeTile:
PUBLIC
PROC [plane: Tesselation, tile: Tile, new:
REF ←
NIL] =
TRUSTED BEGIN
IF plane.immute OR tile.pos.x<plane.state.r.x1 OR tile.pos.y<plane.state.r.y1 OR NE[tile].pos.x>plane.state.r.x2 OR EN[tile].pos.y>plane.state.r.y2 THEN ERROR callErr;
[] ← ChangeTile[plane, tile, new];
END;
RestrictedMyChangeTile:
PROC [plane: Tesselation, tile: Tile, new:
REF]
RETURNS [Tile] =
-- returns the northernmost tile intersecting the changed region.
-- (there is exactly one such tile! maximal-EW-property)
-- immute-test and area-test done by caller
-- restricts splits and joins to plane's state
-- it is allowed to change the value to itself! that cleans up the upper part of the border
BEGIN
n: Number = NEdge[tile];
e: Number = EEdge[tile];
s: Number = SEdge[tile];
w: Number = WEdge[tile];
tWest, tEast, tNorth: Tile;
--checks
IF s<plane.state.r.y1 THEN ERROR;
IF w<plane.state.r.x1
THEN
IF s<plane.state.yf THEN ERROR;
IF e>plane.state.r.x2
THEN
IF s<plane.state.ys THEN ERROR;
tile.value ← new;
-- restore maximal East-West strip property
-- split up tiles that horizontally abut any of the tile's four corners,
-- but extend beyond the corner in a North-South direction
--northwest corner treated with the west edge..
--northeast corner
tEast ← NE[tile];
IF tEast.value=new
AND NEdge[tEast]>n
THEN {
IF EEdge[tEast]<=plane.state.r.x2
OR n>=plane.state.ys
THEN
[] ← NSSplit[plane, tEast, n];
};
--southwest corner
tWest ← tile.sW;
IF tWest.value=new
AND SEdge[tWest]<s
THEN {
IF tWest.pos.x>=plane.state.r.x1
OR s>=plane.state.yf
THEN
[] ← NSSplit[plane, tWest, s];
};
--southeast corner
tEast ← WS[tile];
WHILE EEdge[tEast]<=e DO tEast ← NE[tEast] ENDLOOP;
-- analogous to split of northwest corner.
IF tEast.value=new
AND NEdge[tEast]>s
THEN {
IF EEdge[tEast]<=plane.state.r.x2
OR s>=plane.state.ys
THEN
[] ← NSSplit[plane, tEast, s];
};
-- convert the West and East adjacent tiles to maximal East-West strips
-- run South to North along the West edge
-- splitting North-South, merging East-West; easy since tile is not yet split
tWest ← tile.sW;
WHILE tWest.pos.y<n
DO
IF tWest.value#new OR NEdge[tWest]<plane.state.r.y1 THEN tWest ← tWest.eN
ELSE {
IF w=plane.state.r.x1
AND SEdge[tWest]<plane.state.yf
THEN {
IF NEdge[tWest]<=plane.state.yf OR NEdge[tile]<=plane.state.yf THEN {tWest ← tWest.eN; LOOP};
tile ← NSSplit[plane, tile, plane.state.yf];
};
IF SEdge[tWest]<SEdge[tile] THEN tWest ← NSSplit[plane, tWest, SEdge[tile]]
ELSE IF SEdge[tWest]>SEdge[tile] THEN tile ← NSSplit[plane, tile, SEdge[tWest]];
IF NEdge[tWest]<NEdge[tile] THEN tile ← NSSplit[plane, tile, NEdge[tWest]]
ELSE {
--NEdge[tWest]>=NEdge[tile]
IF NEdge[tWest]>NEdge[tile] THEN [] ← NSSplit[plane, tWest, NEdge[tile]];
[] ← EWMerge[plane, tWest, tile];
tile ← tWest;
EXIT
};
[] ← EWMerge[plane, tWest, NE[tWest]];
tWest ← tile.sW;
};
ENDLOOP;
-- now any maximal-EW-property violations of tile are confined to its Eastern border.
-- NS merges at the northern and southern borders may be pending.
-- run North to South along the East edge
-- splitting inside (west of edge) North-South any tile necessary
tEast ← NE[tile];
WHILE tEast.pos.y>s
DO
tile ← tEast.sW;
IF (tEast.value=new
OR
WS[tEast].value=new)
AND tile.pos.y<tEast.pos.y
AND (tEast.pos.x<plane.state.r.x2 OR tEast.pos.y>=plane.state.ys)
THEN [] ← NSSplit[plane, tile, tEast.pos.y];
tEast ← WS[tEast]
ENDLOOP;
--I SEdge[tEast]<=s
-- run South to North along the East edge
-- splitting outside (east)North-South,
-- merging East-West; eventually merging North-South..
tNorth ← tEast.sW; --not yet north..
WHILE NEdge[tNorth]<=s DO tNorth ← tNorth.eN ENDLOOP;
DO
tile ← tNorth; --current tile in area
tNorth ← tile.eN; --now north of current tile only
IF
NE[tile].value=new
AND (EEdge[tile]<plane.state.r.x2
OR SEdge[tile]>=plane.state.ys)
THEN {
IF tile.nE=tNorth.nE THEN [] ← NSSplit[plane, NE[tile], NEdge[tile]];
tile ← EWMerge[plane, tile, NE[tile]]
};
IF CompToN[
WS[tile]]
AND (SEdge[tile]>plane.state.r.y1)
THEN
tile ← NSMerge[plane, WS[tile], tile];
IF NEdge[tNorth]>n THEN EXIT
ENDLOOP;
IF CompToN[tile] THEN [] ← NSMerge[plane, tile, tNorth];
RETURN [tile]
END;
IsEmpty:
PUBLIC
PROC [plane: Tesselation, rect: Rect, skip:
REF
ANY ←
NIL]
RETURNS [
BOOL] =
-- relies on the Maximal East-West strip property.
BEGIN
rect.x1 ← MAX[maxSpace.x1, rect.x1];
rect.x2 ← MIN[maxSpace.x2, rect.x2];
rect.y1 ← MAX[maxSpace.y1, rect.y1];
rect.y2 ← MIN[maxSpace.y2, rect.y2];
IF rect.x1>rect.x2 OR rect.y1>rect.y2 THEN RETURN [TRUE];
plane.current ← Find[plane.current, rect.x1, rect.y1];
FOR tile: Tile ← plane.current, AboveOf[tile, rect.x1]
WHILE SEdge[tile]<rect.y2
DO
IF tile.value#skip OR EEdge[tile]<rect.x2 THEN RETURN [FALSE]
ENDLOOP;
RETURN [TRUE]
END;
BBox:
PUBLIC
PROC [plane: Tesselation, rect: Rect, skip:
REF
ANY ←
NIL]
RETURNS [bBox: Rect ← empty] =
-- relies on the Maximal East-West strip property.
BEGIN
tile: Tile;
rect.x1 ← MAX[maxSpace.x1, rect.x1];
rect.x2 ← MIN[maxSpace.x2, rect.x2];
rect.y1 ← MAX[maxSpace.y1, rect.y1];
rect.y2 ← MIN[maxSpace.y2, rect.y2];
IF rect.x1>rect.x2 OR rect.y1>rect.y2 THEN RETURN [empty];
--find south west corner
plane.current ← Find[plane.current, rect.x1, rect.y1];
FOR tile ← plane.current, AboveOf[tile, rect.x1]
DO
IF SEdge[tile]>=rect.y2 THEN RETURN; -- plane is empty
IF tile.value#skip OR EEdge[tile]<rect.x2 THEN EXIT
ENDLOOP;
bBox.y1 ← MAX[SEdge[tile], rect.y1];
WHILE SEdge[tile]<rect.y2
DO
IF tile.value#skip THEN {bBox.x1 ← rect.x1; EXIT};
-- else the east edge of tile must abut a non-skip tile (Maximal East-West)
IF EEdge[tile]<rect.x2 AND EEdge[tile]<bBox.x1 THEN bBox.x1 ← EEdge[tile];
tile ← AboveOf[tile, rect.x1];
ENDLOOP;
--find north east corner by going up starting at the south east corner
tile ← Find[plane.current, rect.x2-1, rect.y1];
WHILE SEdge[tile]<rect.y2
DO
IF tile.value#skip
THEN {
bBox.x2 ← rect.x2;
bBox.y2 ← NEdge[tile]
}
ELSE
IF WEdge[tile]>rect.x1
THEN {
bBox.x2 ← MAX[bBox.x2, WEdge[tile]];
bBox.y2 ← NEdge[tile]
};
tile ← AboveOf[tile, rect.x2-1];
ENDLOOP;
END;
ChangeEnumerateArea:
PUBLIC
PROC [plane: Tesselation, rect: Rect, eachRect: RectProc, data:
REF ←
NIL, skip:
REF] =
-- Uses the tiles own links to maintain an implicit stack of tiles.
-- Enumeration proceeds in a manner which ensures that a tiles nE and eN
-- pointers will never be needed once that tile has appeared in the enumeration;
-- this fact is exploited in ResetTesselation.
-- Defn: b is a's child iff b.sW = a.
BEGIN
IsChild:
PROC [me: Tile, you: Tile]
RETURNS [
BOOL] =
INLINE {
--actually only childs not on the border
RETURN [you.sW=me AND you.pos.y>rect.y1];
};
HasOldBrother:
PROC [me: Tile]
RETURNS [
BOOL] =
INLINE {
--actually only brother not on the border
RETURN [me.sW=WS[me].sW AND WS[me].pos.y>rect.y1];
};
CleanupUpperBorderOfTile:
PROC [plane: Tesselation, tile: Tile] =
INLINE {
plane.current ← tile;
[] ← RestrictedMyChangeTile[plane, tile, tile.value]
};
val: REF;
tile, visit: Tile;
outer: State ← plane.state;
ys, nextys: Number;
doneSouthEastTile: BOOL ← FALSE;
IF plane.immute THEN ERROR callErr;
rect.x1 ← MAX[maxSpace.x1, rect.x1];
rect.x2 ← MIN[maxSpace.x2, rect.x2];
rect.y1 ← MAX[maxSpace.y1, rect.y1];
rect.y2 ← MIN[maxSpace.y2, rect.y2];
IF rect.x1>=rect.x2 OR rect.y1>=rect.y2 THEN RETURN;
IF rect.x1<plane.state.r.x1 OR rect.y1<plane.state.r.y1 OR rect.x2>plane.state.r.x2 OR rect.y2>plane.state.r.y2 THEN ERROR callErr;
PreSplit[plane, rect];
tile ← Find[plane.current, rect.x1, rect.y2];
-- correct for one off error when rect.y2 lies at tile boundary (YUK)
IF SEdge[tile]=rect.y2 AND SEdge[tile]>rect.y1 THEN tile ← BelowOf[tile, rect.x1];
plane.current ← tile;
--a tile is visited only after all its children
WHILE ~doneSouthEastTile
DO
seeking: {youth, experience, nothing} ← youth; --of tile
DO
IF seeking=youth
THEN {
child: Tile ← RightOf[tile, rect.y2-1];
--if child is a child of tile then it is southernmost one
IF IsChild[tile, child]
THEN {
IF WEdge[child]<rect.x2 THEN {tile ← child; LOOP};
nextys ← NEdge[NE[tile]]; --protects completely
seeking ← experience --all childs of tile are handled
}
ELSE {
nextys ← NEdge[child];
seeking ← experience --all childs of tile are handled
}
};
--NOT seeking youth!
ys ← nextys;
visit ← tile;
-- Is tile a border tile?
IF tile.pos.x<=rect.x1
OR tile.pos.y<=rect.y1
THEN {
-- Find next border tile, i.e. next tree root
seeking ← nothing;
IF tile.pos.y>rect.y1 THEN tile ← BelowOf[tile, rect.x1]
ELSE {
IF EEdge[tile]>=rect.x2 THEN doneSouthEastTile ← TRUE
ELSE tile ← RightOf[tile, rect.y1];
}
}
ELSE {
IF HasOldBrother[tile]
THEN {
tile ← WS[tile]; --brother
seeking ← youth
}
ELSE {
nextys ← tile.pos.y;
tile ← tile.sW; --father!
}
};
val ← visit.value;
plane.state.r ← BArea[visit, rect];
plane.state.ys ← ys;
plane.state.yf ←
IF rect.y1=plane.state.r.y1
THEN plane.state.r.y1
ELSE IF plane.state.r.x1>rect.x1 THEN NEdge[SW[visit]]
ELSE rect.y2+1; --dont touch outside
--if tile touches south border it is visited after its father...
CleanupUpperBorderOfTile[plane, visit];
IF val#skip THEN eachRect[plane, plane.state.r, val, data]; --must not fool tile
IF seeking=nothing THEN EXIT
ENDLOOP
ENDLOOP;
plane.state ← outer;
PostMerge[plane, rect];
END;
BArea:
PROC [t: Tile, r: Rect]
RETURNS [Rect] =
TRUSTED
INLINE {
--clipped rect of tile; (upper and right border exclusive)
RETURN [[
x1: MAX[t.pos.x, r.x1],
y1: MAX[t.pos.y, r.y1],
x2: MIN[t.nE.pos.x, r.x2],
y2: MIN[t.eN.pos.y, r.y2]
]]
};
NSLineCleanup:
PROC [plane: Tesselation, n, s, x0: Number] =
--reeinstalls properties for all tiles touching the line
BEGIN
CanEWMerge:
PROC [plane: Tesselation, tileW: Tile]
RETURNS [
BOOL] =
INLINE
--check whether in mutable region
--doesnt check whether the height matches;
BEGIN
IF SEdge[tileW]<plane.state.r.y1 THEN ERROR; --can't occur in this context
IF WEdge[tileW]<plane.state.r.x1
THEN
IF SEdge[tileW]<plane.state.yf THEN RETURN [FALSE];
IF EEdge[
NE[tileW]]>plane.state.r.x2
THEN
IF SEdge[tileW]<plane.state.ys THEN RETURN [FALSE];
RETURN [TRUE]
END;
tWest: Tile ← NIL;
--E—W merges
--tile looping, north to south
tile: Tile ← Find[plane.current, x0, n];
WHILE NEdge[tile]>s
DO
next: Tile ← BelowOf[tile, x0];
IF WEdge[tile]=x0
THEN {
--check for E-W merges
tWest ← tile.sW;
--tWest looping south to north in range of tile; eventually split southernmost tWest
WHILE SEdge[tWest]<NEdge[tile]
DO
IF tWest.value#tile.value THEN tWest ← tWest.eN
ELSE {
IF SEdge[tile]<SEdge[tWest] THEN tile ← PNSSplit[plane, tile, SEdge[tWest]]
ELSE IF SEdge[tWest]<SEdge[tile] THEN tWest ← PNSSplit[plane, tWest, SEdge[tile]].north;
IF NEdge[tWest]>NEdge[tile] THEN [] ← PNSSplit[plane, tWest, NEdge[tile]]
ELSE IF NEdge[tile]>NEdge[tWest] THEN [] ← PNSSplit[plane, tile, NEdge[tWest]]; --north
IF CanEWMerge[plane, tWest]
THEN {
next ← AboveOf[tile, x0];
--but we wont do this AboveOf a second time; so tile is visited at max twice
[] ← EWMerge[plane, tWest, tile];
}
ELSE next ← BelowOf[tile, x0]; --forwards to exit the outer loop
EXIT;
}
ENDLOOP;
};
tile ← next;
ENDLOOP;
plane.current ← tile; --speeds up later usage of east west line...
--N-S merges
--tile looping south to north
DO
IF SEdge[tile]>n THEN EXIT;
IF WEdge[tile]=x0
THEN {
tWest ← tile.sW;
DO
WHILE CompToN[tWest]
DO
tWest ← PNSMerge[plane, tWest, EN[tWest]]
ENDLOOP;
IF NEdge[tWest]<=NEdge[tile] THEN tWest ← EN[tWest]
ELSE EXIT;
ENDLOOP;
};
IF CompToN[tile] THEN tile ← PNSMerge[plane, tile, EN[tile]] --north
ELSE tile ← AboveOf[tile, x0];
ENDLOOP;
END;
EWLineCleanup:
PROC [plane: Tesselation, w, e, y0: Number] =
BEGIN
tile: Tile ← Find[plane.current, e, y0-1];
DO
IF CompToN[tile]
THEN
IF SEdge[tile]>=plane.state.r.y1 THEN [] ← NSMerge[plane, tile, EN[tile]];
tile ← LeftOf[tile, y0-1];
IF WEdge[tile]<=w THEN EXIT;
ENDLOOP;
END;
PostMerge:
PROC [plane: Tesselation, rect: Rect] =
BEGIN
NSLineCleanup[plane, rect.y2, rect.y1, rect.x1];
NSLineCleanup[plane, rect.y2, rect.y1, rect.x2];
EWLineCleanup[plane, rect.x1, rect.x2, rect.y1];
END;
SplitNSLine:
PROC [plane: Tesselation, splitX, y1, y2: Number]
RETURNS [rider: Tile] =
--splits top and bottom tiles horizontally
--runs north to south splits other tiles vertically; without merges
--splits including y1
--at end: SEdge[rider]<y1; rider below or right of (splitX, y1)
BEGIN
plane.current ← rider ← Find[plane.current, splitX, y2-1];
IF NEdge[rider]>y2
AND WEdge[rider]<splitX
THEN
[] ← NSSplit[plane, rider, y2];--prevents EW cut too far north
WHILE rider.pos.y>=y1
DO
IF rider.pos.x<splitX THEN rider ← EWSplit[plane, rider, splitX].east;
rider ← WS[rider];
ENDLOOP;
IF NEdge[rider]>y1
THEN
IF rider.pos.x<splitX
THEN {
t: Tile ← NSSplit[plane, rider, y1].north; --rider stays south
[] ← EWSplit[plane, t, splitX];
};
RETURN [rider];
END;
PreSplit:
PROC [plane: Tesselation, rect: Rect] =
--don't wory about maximum stripes
--is that a good idea?
BEGIN
SplitEWLine:
PROC [rider: Tile, endX, splitY: Number]
RETURNS [Tile] =
--runs west to east; without merges
--assumes at begin: SEdge[rider]<splitY and rider contains endX
--splits including endX
BEGIN
WHILE rider.pos.x<endX
DO
IF NEdge[rider]>splitY THEN [] ← NSSplit[plane, rider, splitY];
rider ← NE[rider];
ENDLOOP;
RETURN [rider];
END;
--cut east border, nort to south
tile: Tile ← SplitNSLine[plane, rect.x1, rect.y1, rect.y2]; --north to south at the west border
tile ← SplitEWLine[tile, rect.x2, rect.y1]; --west to east at the south border
[] ← SplitNSLine[plane, rect.x2, rect.y1, rect.y2]; --north to south at the east border
END;
EnumerateArea:
PUBLIC
PROC [plane: Tesselation, rect: Rect, eachTile: TileProc, data:
REF ←
NIL, skip:
REF] =
-- Uses the tiles own links to maintain an implicit stack of tiles.
-- Enumeration proceeds in a manner which ensures that a tiles nE and eN
-- pointers will never be needed once that tile has appeared in the enumeration;
-- this fact is exploited in ResetTesselation.
-- Defn: b is a's child iff b.sW = a.
BEGIN
IsChild:
PROC [me: Tile, you: Tile]
RETURNS [
BOOL] =
INLINE {
--actually only childs not on the border
RETURN [you.sW=me AND you.pos.y>rect.y1];
};
HasOldBrother:
PROC [me: Tile]
RETURNS [
BOOL] =
INLINE {
--actually only brother not on the border
RETURN [me.sW=WS[me].sW AND WS[me].pos.y>rect.y1];
};
immute: BOOL;
tile: Tile;
visit: Tile;
doneSouthEastTile: BOOL ← FALSE;
rect.x1 ← MAX[maxSpace.x1, rect.x1];
rect.x2 ← MIN[maxSpace.x2, rect.x2];
rect.y1 ← MAX[maxSpace.y1, rect.y1];
rect.y2 ← MIN[maxSpace.y2, rect.y2];
IF rect.x1>=rect.x2 OR rect.y1>=rect.y2 THEN RETURN;
immute ← plane.immute;
plane.immute ← TRUE;
tile ← Find[plane.current, rect.x1, rect.y2];
-- correct for one off error when rect.y2 lies at tile boundary (YUK)
IF SEdge[tile]=rect.y2 AND SEdge[tile]>rect.y1 THEN tile ← BelowOf[tile, rect.x1];
plane.current ← tile;
--a tile is visited only after all its children
WHILE ~doneSouthEastTile
DO
seeking: {youth, experience, nothing} ← youth; --of tile
DO
IF seeking=youth
THEN {
child: Tile ← RightOf[tile, rect.y2-1];
--if child is a child of tile then it is southernmost one
IF IsChild[tile, child]
AND WEdge[child]<rect.x2
THEN {
tile ← child;
LOOP
}
ELSE seeking ← experience --all childs of tile are handled
};
--NOT seeking youth!
visit ← tile;
-- Is tile a border tile?
IF WEdge[tile]<=rect.x1
OR SEdge[tile]<=rect.y1
THEN {
-- Find next border tile, i.e. next tree root
seeking ← nothing;
IF SEdge[tile]>rect.y1 THEN tile ← BelowOf[tile, rect.x1]
ELSE {
IF EEdge[tile]>=rect.x2 THEN doneSouthEastTile ← TRUE
ELSE tile ← RightOf[tile, rect.y1];
}
}
ELSE {
IF HasOldBrother[tile]
THEN {
tile ← WS[tile]; --brother
seeking ← youth
}
ELSE tile ← tile.sW; --father!
};
IF visit.value#skip THEN eachTile[visit, data]; --must not fool tile
IF seeking=nothing THEN EXIT
ENDLOOP
ENDLOOP;
plane.immute ← immute;
END;
ListArea:
PUBLIC
PROC [plane: Tesselation, rect: Rect, skip:
REF ←
NIL]
RETURNS [rl:
LIST
OF
REF Region←
NIL] =
BEGIN
PerTile: TileProc = {
rl ←
CONS[
NEW[ Region ← [
[
MAX[WEdge[tile], rect.x1],
MAX[SEdge[tile], rect.y1],
MIN[EEdge[tile], rect.x2],
MIN[NEdge[tile], rect.y2]
],
tile.value
]], rl];
};
EnumerateArea[plane, rect, PerTile, NIL, skip];
END;
LeftOf:
PROC [tile: Tile, y: Number]
RETURNS [t: Tile] =
INLINE
-- assumes y is within tile (i.e. SEdge[tile] <= x & NEdge[tile] > x)
BEGIN
t ← SW[tile];
WHILE NEdge[t]<=y DO t ← EN[t] ENDLOOP;
END;
RightOf:
PROC [tile: Tile, y: Number]
RETURNS [t: Tile] =
INLINE
-- assumes y is within tile (i.e. SEdge[tile] <= x & NEdge[tile] > x)
BEGIN
t ← NE[tile];
WHILE t.pos.y>y DO t ← WS[t] ENDLOOP;
END;
BelowOf:
PROC [tile: Tile, x: Number]
RETURNS [t: Tile] =
INLINE
-- assumes x is within tile (i.e. WEdge[tile] <= x & EEdge[tile] > x)
BEGIN
t ← WS[tile];
WHILE NE[t].pos.x<=x DO t ← NE[t] ENDLOOP;
END;
AboveOf:
PROC [tile: Tile, x: Number]
RETURNS [t: Tile] =
INLINE
-- assumes x is within tile (i.e. WEdge[tile] <= x & EEdge[tile] > x)
BEGIN
t ← tile.eN;
WHILE t.pos.x>x DO t ← t.sW ENDLOOP;
END;
FindTile:
PUBLIC
PROC [plane: Tesselation, pos: Pos]
RETURNS [tile: Tile] =
BEGIN
pos.x ← MIN[maxSpace.x2, pos.x];
pos.y ← MIN[maxSpace.y2, pos.y];
tile ← Find[current: plane.current, x: pos.x, y: pos.y];
IF tile.value=guard THEN ERROR;
plane.current ← tile;
END;
Find:
PROC [current: Tile, x, y: Number]
RETURNS [Tile] =
--a-symmetric
TRUSTED BEGIN
IF current.value=deleted THEN ERROR UseOfAlreadyDeletedTile;
DO
--south-- WHILE y<current.pos.y DO current ← LOOPHOLE[current.wS] ENDLOOP;
--north-- WHILE y>=current.eN.pos.y DO current ← current.eN ENDLOOP;
IF x<current.pos.x
THEN
--west--
WHILE x<current.pos.x DO current ← current.sW ENDLOOP
ELSE
IF x>=current.nE.pos.x
THEN
--east--
WHILE x>=current.nE.pos.x DO current ← LOOPHOLE[current.nE] ENDLOOP
ELSE EXIT
ENDLOOP;
RETURN [current]
END;
EWSplit:
PROC [plane: Tesselation, tile: Tile, x: Number]
RETURNS [east: Tile] =
-- the East tile will be the new one.
-- x is west point in new east tile.
TRUSTED BEGIN
t: Tile;
--checks
IF SEdge[tile]<plane.state.r.y1 THEN ERROR;
IF WEdge[tile]<plane.state.r.x1
THEN
IF SEdge[tile]<plane.state.yf THEN ERROR;
IF EEdge[tile]>plane.state.r.x2
THEN
IF SEdge[tile]<plane.state.ys THEN ERROR;
IF WEdge[tile]>=x OR EEdge[tile]<= x THEN ERROR;
east ← NewTile[];
-- east starts out a replica of tile
east^ ← tile^;
plane.tileCount ← plane.tileCount + 1;
-- Fix the tiles relative to each other.
tile.nE ← LOOPHOLE[east];
east.sW ← tile;
east.pos.x ← x;
-- Fix North boundary
t ← east.eN;
WHILE t.pos.x>=x DO t.wS ← LOOPHOLE[east]; t ← t.sW ENDLOOP;
tile.eN ← t;
-- Fix East boundary
t ← NE[east];
WHILE t.sW=tile DO t.sW ← east; t ← WS[t] ENDLOOP;
-- Fix South boundary
t ← BelowOf[tile, x];
east.wS ← LOOPHOLE[t];
WHILE t.eN=tile DO t.eN ← east; t ← NE[t] ENDLOOP;
RETURN [east]
END;
PNSSplit:
PROC [plane: Tesselation, tile: Tile, y: Number]
RETURNS [north: Tile] =
--protected NS split
BEGIN
IF SEdge[tile]<plane.state.r.y1 THEN RETURN [tile];
IF WEdge[tile]<plane.state.r.x1
THEN
IF SEdge[tile]<plane.state.yf THEN RETURN [tile];
IF EEdge[tile]>plane.state.r.x2
THEN
IF SEdge[tile]<plane.state.ys THEN RETURN [tile];
RETURN [NSSplit[plane, tile, y]];
END;
NSSplit:
PROC [plane: Tesselation, tile: Tile, y: Number]
RETURNS [north: Tile] =
-- the North tile will be the new one.
-- y is south point of new north tile
TRUSTED BEGIN
t: Tile;
--check
IF SEdge[tile]<plane.state.r.y1 THEN ERROR;
IF WEdge[tile]<plane.state.r.x1
THEN
IF SEdge[tile]<plane.state.yf THEN ERROR;
IF EEdge[tile]>plane.state.r.x2
THEN
IF y<plane.state.ys THEN ERROR;
IF SEdge[tile]>=y OR NEdge[tile]<=y THEN ERROR;
north ← NewTile[];
-- north starts out a replica of tile
north^ ← tile^;
plane.tileCount ← plane.tileCount + 1;
-- Fix the tiles relative to each other.
tile.eN ← north;
north.wS ← LOOPHOLE[tile];
north.pos.y ← y;
-- Fix East boundary
t ← NE[north];
WHILE t.pos.y>=y DO t.sW ← north; t ← WS[t] ENDLOOP;
tile.nE ← LOOPHOLE[t];
-- Fix North boundary
t ← north.eN;
WHILE t.wS=P[tile] DO t.wS ← LOOPHOLE[north]; t ← t.sW ENDLOOP;
-- Fix West boundary
t ← tile.sW;
WHILE t.eN.pos.y<=y DO t ← t.eN ENDLOOP;
north.sW ← t;
WHILE NE[t]=tile DO t.nE ← LOOPHOLE[north]; t ← t.eN ENDLOOP;
RETURN [north]
END;
PEWMerge:
PROC [plane: Tesselation, tileW, tileE: Tile]
RETURNS [Tile] =
--assmes a tile does not intersect both, left and right
--returns east if not merged
BEGIN
IF SEdge[tileW]<plane.state.r.y1 THEN RETURN [tileE];
IF WEdge[tileW]<plane.state.r.x1
THEN
IF SEdge[tileW]<plane.state.yf THEN RETURN [tileE];
IF EEdge[tileE]>plane.state.r.x2
THEN
IF SEdge[tileE]<plane.state.ys THEN RETURN [tileE];
RETURN [EWMerge[plane, tileW, tileE]]
END;
EWMerge:
PROC [plane: Tesselation, tileW, tileE: Tile]
RETURNS [Tile] =
-- The East tile will be deallocated.
-- The caller must assure that tileW.value=tileE.value and that the tiles really
-- do have a common border of equal length.
TRUSTED BEGIN
--checks
IF tileE.sW#tileW OR NE[tileW]#tileE OR tileW.pos.y#tileE.pos.y OR tileW.eN.pos.y#tileE.eN.pos.y OR tileW.value#tileE.value THEN ERROR;
IF SEdge[tileW]<plane.state.r.y1 THEN ERROR;
IF WEdge[tileW]<plane.state.r.x1
THEN
IF SEdge[tileW]<plane.state.yf THEN ERROR;
IF EEdge[
NE[tileW]]>plane.state.r.x2
THEN
IF SEdge[tileW]<plane.state.ys THEN ERROR;
-- Fix the tiles relative to each other.
tileW.eN ← tileE.eN;
tileW.nE ← tileE.nE;
-- Fix North boundary
FOR t: Tile ← tileW.eN, t.sW WHILE WS[t]=tileE DO t.wS ← LOOPHOLE[tileW] ENDLOOP;
-- Fix East boundary
FOR t: Tile ← NE[tileW], WS[t] WHILE t.sW=tileE DO t.sW ← tileW ENDLOOP;
-- Fix South boundary
FOR t: Tile ← WS[tileE], NE[t] WHILE t.eN=tileE DO t.eN ← tileW ENDLOOP;
IF plane.current=tileE THEN plane.current ← tileW;
DisposeTile[tileE];
plane.tileCount ← plane.tileCount - 1;
RETURN [tileW];
END;
PNSMerge:
PROC [plane: Tesselation, tileS, tileN: Tile]
RETURNS [Tile] =
--returns north tile if not merged
BEGIN
IF SEdge[tileS]<plane.state.r.y1 THEN RETURN [tileN];
IF WEdge[tileS]<plane.state.r.x1
THEN
IF SEdge[tileS]<plane.state.yf THEN RETURN [tileN];
IF EEdge[tileS]>plane.state.r.x2
THEN
IF SEdge[tileS]<plane.state.ys THEN RETURN [tileN];
RETURN [NSMerge[plane, tileS, tileN]]
END;
NSMerge:
PROC [plane: Tesselation, tileS, tileN: Tile]
RETURNS [Tile] =
-- the north tile will be deallocated.
-- the caller must assure that tileS.value=tileN.value and that the tiles really
-- do have a common border of equal length.
TRUSTED BEGIN
--checks
IF WS[tileN]#tileS OR tileS.eN#tileN OR tileS.pos.x#tileN.pos.x OR NE[tileS].pos.x#NE[tileN].pos.x OR tileS.value#tileN.value THEN ERROR;
IF SEdge[tileS]<plane.state.r.y1 THEN ERROR;
IF WEdge[tileS]<plane.state.r.x1
THEN
IF SEdge[tileS]<plane.state.yf THEN ERROR;
IF EEdge[tileS]>plane.state.r.x2
THEN
IF SEdge[tileS]<plane.state.ys THEN ERROR;
-- Fix the tiles relative to each other.
tileS.nE ← tileN.nE;
tileS.eN ← tileN.eN;
-- Fix East boundary
FOR t: Tile ← NE[tileS], WS[t] WHILE t.sW=tileN DO t.sW ← tileS ENDLOOP;
-- Fix North boundary
FOR t: Tile ← tileS.eN, t.sW WHILE WS[t]=tileN DO t.wS ← LOOPHOLE[tileS] ENDLOOP;
-- Fix West boundary
FOR t: Tile ← tileN.sW, t.eN WHILE NE[t]=tileN DO t.nE ← LOOPHOLE[tileS] ENDLOOP;
IF plane.current=tileN THEN plane.current ← tileS;
DisposeTile[tileN];
plane.tileCount ← plane.tileCount - 1;
RETURN [tileS];
END;
P:
PROC [r: Tile]
RETURNS [
LONG
POINTER
TO TileRec] ~
TRUSTED INLINE {
RETURN [LOOPHOLE[r]]
};
Load: Commander.CommandProc = {
cmd.out.PutRope["CornerStiching loaded\n"];
};
FinalizeTesselation:
ENTRY
PROC [plane:
REF
CS.TesselationRec] =
BEGIN
ENABLE UNWIND => NULL;
InternalDispose: TileProc =
-- Depends on fact that Enumeration proceeds NE to SW,
-- so eN ref may be clobbered by caching process.
BEGIN
tile.value ← deleted;
tile.eN ← header;
header ← tile;
END;
header: Tile ← NIL;
IF plane.current#NIL THEN EnumerateArea[plane, maxSpace, InternalDispose, NIL, deleted];
plane.current ← plane.southEast ← NIL;
plane.data ← NIL;
WHILE header#
NIL
DO
t: Tile ← header;
header ← header.eN;
t.sW ← t.eN ← NIL; t.nE ← t.wS ← NIL;
ENDLOOP;
END;
FinalizerProcess: PROC[fooFQ: SafeStorage.FinalizationQueue] =
BEGIN
DO
foo: REF CS.TesselationRec = NARROW[SafeStorage.FQNext[fooFQ]];
FinalizeTesselation[foo];
ENDLOOP
END;
fooFQ: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[CODE[CS.TesselationRec], 0, fooFQ];
TRUSTED {Process.Detach[FORK FinalizerProcess[fooFQ]]};
InitTesselationBorderTiles[];
Commander.Register[key: "CornerStiching", proc: Load, doc: "loads the CornerStiching"];
END.
Edited on February 8, 1985 4:40:46 pm PST, by Jacobi
Formatting
Find completely rewritten
Edited on September 30, 1985 1:43:22 pm PDT, by Jacobi
Correct comments when which procedure is valid included in definition
EnumerateArea and ListArea made two procedures whith simpler parameters
Edited on December 12, 1985 6:28:29 pm PST, by Jacobi
maxSpace introduced,
Edited on January 14, 1986 7:07:36 pm PST, by Jacobi
finalization introduced
Edited on January 17, 1986 4:06:17 pm PST, by Jacobi
comments, invariants, shorter,
bug correction in ChangeTile,
BelowOf, WS, NE introduced
Edited on January 27, 1986 3:17:37 pm PST
New algorithm for ChangeEnumerateArea