<> <> <> <> <> <> <> <> DIRECTORY CornerStitching; CornerStitchingImpl: CEDAR MONITOR EXPORTS CornerStitching = BEGIN Tesselation: TYPE = CornerStitching.Tesselation; Tile: TYPE = CornerStitching.Tile; Number: TYPE = CornerStitching.Number; Coord: TYPE = CornerStitching.Coord; Rect: TYPE = CornerStitching.Rect; NEdge: PROCEDURE [t: REF Tile] RETURNS [Number] = INLINE {RETURN [t.en.pos.y]}; EEdge: PROCEDURE [t: REF Tile] RETURNS [Number] = INLINE {RETURN [PtoR[t.ne].pos.x]}; SEdge: PROCEDURE [t: REF Tile] RETURNS [Number] = INLINE {RETURN [t.pos.y]}; WEdge: PROCEDURE [t: REF Tile] RETURNS [Number] = INLINE {RETURN [t.pos.x]}; <<>> <<-- Error Codes>> TileValue: PUBLIC ERROR = CODE; TileDeleted: PUBLIC ERROR = CODE; DegenerateTile: ERROR = CODE; <<>> <<-- Co-ordinates at "infinity">> neLimitCoord: Coord = [x: Number.LAST, y: Number.LAST]; nwLimitCoord: Coord = [x: Number.FIRST, y: Number.LAST]; swLimitCoord: Coord = [x: Number.FIRST, y: Number.FIRST]; seLimitCoord: Coord = [x: Number.LAST, y: Number.FIRST]; emptyRect: Rect = [x1: Number.FIRST+1, y1: Number.FIRST, x2: Number.LAST-1, y2: Number.LAST-1]; <<>> <<-- House-keeping tile values to flag deleted tiles and tiles at "infinity">> guard: REF ANY = $CornerStitchingPrivateEdgeGuard; deleted: REF ANY = $CornerStitchingPrivateDeletedTile; <<>> <<-- Border Tiles (shared by all tesselations)>> northLimit: REF Tile = NEW[Tile _ [pos: nwLimitCoord, value: guard]]; northBuffer: REF Tile = NEW[Tile _ [pos: [emptyRect.x1, emptyRect.y2], value: guard]]; southLimit: REF Tile = NEW[Tile _ [pos: swLimitCoord, value: guard]]; eastLimit: REF Tile = NEW[Tile _ [pos: seLimitCoord, value: guard]]; westLimit: REF Tile = NEW[Tile _ [pos: swLimitCoord, value: guard]]; InitTesselationBorderTiles: PROCEDURE = 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 _ RtoP[eastLimit]; eastLimit.en _ northLimit; <<-- South-East>> southLimit.en _ eastLimit; southLimit.ne _ RtoP[eastLimit]; eastLimit.ws _ RtoP[southLimit]; eastLimit.sw _ southLimit; <<-- North-West>> westLimit.ne _ RtoP[northLimit]; westLimit.en _ northLimit; northLimit.sw _ westLimit; northLimit.ws _ RtoP[westLimit]; <<-- South-West>> southLimit.sw _ westLimit; westLimit.ws _ RtoP[southLimit]; <<-- northBuffer>> northBuffer.en _ northLimit; northBuffer.ne _ RtoP[eastLimit]; northBuffer.sw _ westLimit; northBuffer.ws _ NIL; END; Disguise: TYPE = RECORD [ hiddenValue: REF ANY ]; disposedTiles: REF Tile _ NIL; disguiseCache: REF Disguise _ NIL; DumpCache: PUBLIC ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; IF disposedTiles#NIL THEN { lag: REF Tile _ disposedTiles; FOR tc: REF Tile _ disposedTiles.en, tc.en WHILE tc#NIL DO lag.en _ NIL; lag _ tc ENDLOOP; disposedTiles _ NIL }; IF disguiseCache#NIL THEN { lag: REF Disguise _ disguiseCache; FOR dc: REF Disguise _ NARROW[disguiseCache.hiddenValue], NARROW[dc.hiddenValue] WHILE dc # NIL DO lag.hiddenValue _ NIL; lag _ dc ENDLOOP; disguiseCache _ NIL }; END; NewTile: ENTRY PROCEDURE RETURNS [tile: REF Tile] = BEGIN ENABLE UNWIND => NULL; IF disposedTiles#NIL THEN { tile _ disposedTiles; disposedTiles _ disposedTiles.en; } ELSE tile _ NEW[Tile]; END; DisposeTile: ENTRY PROCEDURE [tile: REF Tile] = <<--and cache the tile, this is faster than going through the garbage collector>> BEGIN ENABLE UNWIND => NULL; tile.sw _ NIL; -- Prevent potential circular chains in the disposed cache. tile.ne _ tile.ws _ NIL; tile.value _ deleted; tile.en _ disposedTiles; disposedTiles _ tile; END; NewDisguise: ENTRY PROCEDURE [hiddenValue: REF_NIL] RETURNS [disguise: REF Disguise] = BEGIN ENABLE UNWIND => NULL; IF disguiseCache#NIL THEN { disguise _ disguiseCache; disguiseCache _ NARROW[disguiseCache.hiddenValue]; } ELSE disguise _ NEW[Disguise]; disguise.hiddenValue _ hiddenValue; END; DisposeDisguise: ENTRY PROCEDURE [disguise: REF Disguise] = BEGIN ENABLE UNWIND => NULL; disguise.hiddenValue _ disguiseCache; disguiseCache _ disguise; END; NewTesselation: PUBLIC PROCEDURE [data: REF_NIL, stopFlag: REF BOOL_NIL] RETURNS [REF Tesselation] = BEGIN eastSpace: REF Tile = NewTile[]; centreSpace: REF Tile = NewTile[]; IF stopFlag=NIL THEN stopFlag _ NEW[BOOL_FALSE]; eastSpace^ _ [ en: northBuffer, ne: RtoP[eastLimit], sw: centreSpace, ws: RtoP[southLimit], pos: [emptyRect.x2, emptyRect.y1], value: guard ]; centreSpace^ _ [ en: northBuffer, ne: RtoP[eastSpace], sw: westLimit, ws: RtoP[southLimit], pos: [emptyRect.x1, emptyRect.y1], value: NIL ]; RETURN [NEW[Tesselation _ [southEast: eastSpace, current: centreSpace, data: data, stopFlag: stopFlag]]] END; FreeTesselation: PUBLIC PROCEDURE [plane: REF Tesselation, freeCache: BOOLEAN _ TRUE] = BEGIN CacheIt: CornerStitching.PerTileProc = <<[tile: REF Tile, data: REF ANY] RETURNS [REF ANY] -- >> <<-- Depends on fact that Enumeration proceeds NE to SW, so en ref may be clobbered by caching process.>> BEGIN DisposeTile[tile]; END; <> [] _ EnumerateArea[plane, [x1~Number.FIRST/2, y1~Number.FIRST/2, x2~Number.LAST/2, y2~Number.LAST/2], CacheIt, NIL, deleted]; plane.current _ plane.southEast _ NIL; IF freeCache THEN DumpCache[]; plane.tilesInTesselationCount _ 0 END; ChangeRect: PUBLIC PROCEDURE [plane: REF Tesselation, rect: Rect, newValue: REF ANY _ NIL] = BEGIN SplitEWRunningBorder: PROCEDURE [StartTile: REF Tile, splitLine, lineEndpoint: Number] = INLINE BEGIN boundaryRider: REF Tile _ StartTile; WHILE WEdge[boundaryRider]splitLine DO boundaryRider _ PtoR[boundaryRider.ws] ENDLOOP; <<-- The second clause here is necessary to avoid splitting tiles which will never be repaired by changeTile, and will thus not be made maximal north-south. Its kind of subtle, and probably more than a little ugly, but I can probably prove its correct if pressed.>> IF SEdge[boundaryRider]> boundaryRider _ NSSplit[ plane, boundaryRider, splitLine]; boundaryRider _ PtoR[boundaryRider.ne] ENDLOOP; END; tile: REF Tile _ FindTileContainingPoint[plane.current, rect.x1, rect.y2]; <<-- tile contains nw corner (sort of!)>> plane.current _ tile; SplitEWRunningBorder[tile, rect.y2, rect.x2]; --north border SplitEWRunningBorder[FindTileContainingPoint[plane.current, rect.x1, rect.y1], rect.y1, rect.x2]; --south border <<-- Now we start gobbling up all the tiles into wide flat bands of newValue.>> tile _ FindTileContainingPoint[plane.current, rect.x1, rect.y2-1]; <<-- First get our western border tile into shape.>> DO IF tile.value # newValue AND WEdge[tile] < rect.x1 THEN { outsideTile: REF Tile; tile _ EWSplit[plane, tile, rect.x1]; outsideTile _ tile.sw; <<-- Better make sure we keep outside tile in order NS-wise.>> IF outsideTile.value=outsideTile.en.value AND WEdge[outsideTile]=WEdge[outsideTile.en] AND EEdge[outsideTile]=EEdge[outsideTile.en] THEN outsideTile _ NSMerge[plane, outsideTile, outsideTile.en]; IF outsideTile.value=PtoR[outsideTile.ws].value AND WEdge[outsideTile]=WEdge[PtoR[outsideTile.ws]] AND EEdge[outsideTile]=EEdge[PtoR[outsideTile.ws]] THEN outsideTile _ NSMerge[plane, PtoR[outsideTile.ws], outsideTile] }; DO IF tile.value#newValue THEN { IF EEdge[tile]>rect.x2 THEN { outsideTile: REF Tile; outsideTile _ EWSplit[plane, tile, rect.x2]; IF outsideTile.value=outsideTile.en.value AND WEdge[outsideTile]=WEdge[outsideTile.en] AND EEdge[outsideTile] = EEdge[outsideTile.en] THEN outsideTile _ NSMerge[plane, outsideTile, outsideTile.en]; IF outsideTile.value=PtoR[outsideTile.ws].value AND WEdge[outsideTile]=WEdge[PtoR[outsideTile.ws]] AND EEdge[outsideTile]=EEdge[PtoR[outsideTile.ws]] THEN outsideTile _ NSMerge[plane, PtoR[outsideTile.ws], outsideTile] }; tile _ ChangeTile[plane, tile, newValue] }; IF EEdge[tile]>=rect.x2 THEN EXIT; tile _ PtoR[tile.ne]; WHILE SEdge[tile]>=rect.y2 DO tile _ PtoR[tile.ws] ENDLOOP; -- EMM ENDLOOP; IF WEdge[tile]>rect.x1 THEN ERROR; IF SEdge[tile]<=rect.y1 THEN EXIT; tile _ PtoR[tile.ws]; WHILE EEdge[tile]<=rect.x1 DO tile _ PtoR[tile.ne] ENDLOOP ENDLOOP END; FuncChangeRect: PUBLIC PROCEDURE [plane: REF Tesselation, rect: Rect, perTile: CornerStitching.PerTileChangeProc, data: REF ANY _ NIL] = BEGIN DisguiseTile: CornerStitching.PerTileProc = -- [tile: REF Tile, data: REF ANY] RETURNS [REF ANY] BEGIN tile.value _ NewDisguise[tile.value]; END; ApplyFuncToTile: CornerStitching.PerTileProc = <<--[tile: REF Tile, data: REF ANY] RETURNS [REF ANY] >> BEGIN disguise: REF Disguise = NARROW[tile.value]; clippedBB: Rect = [ MAX[WEdge[tile], rect.x1], MAX[SEdge[tile], rect.y1], MIN[EEdge[tile], rect.x2], MIN[NEdge[tile], rect.y2] ]; -- Clip tile against Enumeration's bounding box <<-- Restore tile value>> [] _ ChangeTile[plane: plane, tile: tile, newValue: disguise.hiddenValue]; <<-- Call user perTile function>> perTile[plane: plane, rect: clippedBB, oldValue: disguise.hiddenValue, data: data]; DisposeDisguise[disguise]; END; <<>> <<--FuncChangeRect>> [] _ EnumerateArea[plane, rect, DisguiseTile, NIL, deleted]; [] _ EnumerateArea[plane, rect, ApplyFuncToTile, data, deleted]; END; ChangeTile: PROCEDURE [plane: REF Tesselation, tile: REF Tile, newValue: REF ANY _ NIL] RETURNS [REF Tile] = <<-- Returns the northernmost tile in the changed region. (There is only one such tile by the maximal-EW-property)>> BEGIN north: Number = NEdge[tile]; east: Number = EEdge[tile]; south: Number = SEdge[tile]; west: Number = WEdge[tile]; tWest, tEast, tNorth: REF Tile; tile.value _ newValue; -- Give tile newValue, then restore maximal East-West strip property <<-- First we tidy up any newValue tiles that abut any of the tile's four corners, >> <<-- but extend beyond the corner in a North-South direction>> tEast _ PtoR[tile.ne]; IF tEast.value=newValue AND NEdge[tEast]>north THEN [] _ NSSplit[plane, tEast, north]; tWest _ tile.en; WHILE WEdge[tWest]>=west DO tWest _ tWest.sw ENDLOOP; <<-- Now we are at the tile whose east EEdge is >= west but whose WEdge is < west. >> <<-- In fact SEdge[tWest] < north will only hold if EEdge = west>> IF tWest.value=newValue AND SEdge[tWest]> IF tEast.value=newValue AND NEdge[tEast]>south THEN [] _ NSSplit[plane, tEast, south]; <<-- Now we convert the West and East adjacent tiles to maximal East-West strips>> <<-- First run South to North along the West edge splitting North-South, and merging East-West the newValue tile created from the tile being changed.>> tWest _ tile.sw; WHILE NEdge[tWest]> IF tWest.value=newValue THEN tile _ EWMerge[plane, tWest, tile]; <<-- Now any maximal-EW-property violations of tile are confined to its Eastern border. >> <<-- There may however still be some pending merges at the northern and southern borders.>> <<-- Run North to South along the East edge splitting North-South any newValue tile to >> <<-- the East which abuts more than one newValue tile in the changed area.>> tEast _ PtoR[tile.ne]; WHILE SEdge[tEast]>south DO tile _ tEast.sw; IF (tEast.value=newValue OR PtoR[tEast.ws].value=newValue) AND SEdge[tile]> <<-- and merging East-West the newValue tiles created from the tile being changed.>> tNorth _ tEast.sw; WHILE NEdge[tNorth]<=south DO tNorth _ tNorth.en; ENDLOOP; DO tile _ tNorth; tNorth _ tNorth.en; IF PtoR[tile.ne].value=newValue THEN { IF tile.ne=tNorth.ne THEN [] _ NSSplit[plane, PtoR[tile.ne], NEdge[tile]]; tile _ EWMerge[plane, tile, PtoR[tile.ne]] }; IF tile.value=PtoR[tile.ws].value AND WEdge[tile]=WEdge[PtoR[tile.ws]] AND EEdge[tile]=EEdge[PtoR[tile.ws]] THEN tile _ NSMerge[plane, PtoR[tile.ws], tile]; IF NEdge[tNorth]>north THEN EXIT ENDLOOP; IF tile.value=tNorth.value AND WEdge[tile]=WEdge[tNorth] AND EEdge[tile]=EEdge[tNorth] THEN [] _ NSMerge[plane, tile, tNorth]; RETURN [tile] END; AreaEmpty: PUBLIC PROCEDURE [plane: REF Tesselation, rect: Rect, backgroundValue: REF ANY _ NIL] RETURNS [BOOLEAN] = <<-- Return TRUE if all tiles in area are of value backgroundValue, else FALSE. >> <<-- Relies on the Maximal East-West strip property.>> BEGIN plane.current _ FindTileContainingPoint[plane.current, rect.x1, rect.y1]; FOR tile: REF Tile _ plane.current, TileAbove[tile, rect.x1] WHILE SEdge[tile]> <<-- tiles in rect. >> <<-- Relies on the Maximal East-West strip property.>> BEGIN tile: REF Tile; bBox _ [x1~Number.LAST, y1~Number.LAST, x2~Number.FIRST, y2~Number.FIRST]; IF rect.x1=rect.x2 THEN ERROR DegenerateTile; plane.current _ FindTileContainingPoint[plane.current, rect.x1, rect.y1]; FOR tile _ plane.current, TileAbove[tile, rect.x1] DO IF SEdge[tile]>=rect.y2 THEN RETURN; -- Tile is empty IF tile.value#backgroundValue OR EEdge[tile]> IF EEdge[tile]> <<-- (This is the cause of the ugly .PREDs)>> IF tile.value#backgroundValue THEN { bBox.x2 _ rect.x2; bBox.y2 _ NEdge[tile] } ELSE IF WEdge[tile]>rect.x1 THEN { IF WEdge[tile]>bBox.x2 THEN bBox.x2 _ WEdge[tile]; bBox.y2 _ NEdge[tile] }; tile _ TileAbove[tile, rect.x2-1]; ENDLOOP; END; EnumerateArea: PUBLIC PROCEDURE [plane: REF Tesselation, rect: Rect, perTile: CornerStitching.PerTileProc _ NIL, data: REF ANY _ NIL, backgroundValue: REF ANY _ NIL] RETURNS [REF ANY] = <<-- 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 FreeTesselation.>> <<-- Defn: b is a's child iff b.sw = a.>> BEGIN IsChildOf: PROCEDURE [ me: REF Tile, you: REF Tile] RETURNS [BOOLEAN] = INLINE { RETURN [you.sw=me AND SEdge[you]>rect.y1]; }; IsBrotherOf: PROCEDURE [ me: REF Tile, you: REF Tile] RETURNS [BOOLEAN] = INLINE { RETURN [me.sw=you.sw AND SEdge[you]>rect.y1]; }; tileEnumeration: LIST OF REF CornerStitching.Region _ NIL; tile: REF Tile _ FindTileContainingPoint[plane.current, rect.x1, rect.y2]; doneSouthEastTile: BOOLEAN _ FALSE; <<-- 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 _ PtoR[tile.ws]; WHILE EEdge[tile]<=rect.x1 DO tile _ PtoR[tile.ne] ENDLOOP; }; plane.current _ tile; WHILE ~doneSouthEastTile DO seeking: {youth, experience, nothing} _ youth; DO prevT: REF Tile; IF seeking=youth THEN { child: REF Tile _ PtoR[tile.ne]; WHILE SEdge[child]>=rect.y2 DO child _ PtoR[child.ws] ENDLOOP; IF IsChildOf[tile, child] AND WEdge[child]> 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 _ PtoR[tile.ws]; WHILE EEdge[tile]<=rect.x1 DO tile _ PtoR[tile.ne] ENDLOOP } ELSE { IF EEdge[tile]>=rect.x2 THEN doneSouthEastTile _ TRUE ELSE { tile _ PtoR[tile.ne]; WHILE SEdge[tile]>rect.y1 DO tile _ PtoR[tile.ws] ENDLOOP; } } } ELSE { IF IsBrotherOf[tile, PtoR[tile.ws]] THEN { tile _ PtoR[tile.ws]; seeking _ youth } ELSE tile _ tile.sw; }; IF prevT.value#backgroundValue THEN { IF perTile#NIL THEN perTile[prevT, data] ELSE tileEnumeration _ CONS[NEW[ CornerStitching.Region _ [ [ MAX[WEdge[prevT], rect.x1], MAX[SEdge[prevT], rect.y1], MIN[EEdge[prevT], rect.x2], MIN[NEdge[prevT], rect.y2]], prevT.value] ], tileEnumeration]; }; IF seeking=nothing THEN EXIT ENDLOOP ENDLOOP; IF perTile=NIL THEN data _ tileEnumeration; RETURN [data] END; TileAbove: PROCEDURE [tile: REF Tile, x: Number] RETURNS [REF Tile] = <<-- Assumes x is within tile (i.e. WEdge[tile] <= x & EEdge[tile] > x)>> BEGIN IF WEdge[tile]>x OR EEdge[tile]<=x THEN ERROR; tile _ tile.en; WHILE WEdge[tile]>x DO tile _ tile.sw; ENDLOOP; RETURN [tile] END; lastm1: Number = Number.LAST-2; TileAt: PUBLIC PROCEDURE [plane: REF Tesselation, pos: Coord] RETURNS [tile: REF Tile] = BEGIN tile _ FindTileContainingPoint[plane.current, MIN[lastm1, pos.x], MIN[lastm1, pos.y]]; IF tile=northBuffer THEN ERROR; plane.current _ tile; END; FindTileContainingPoint: PROCEDURE [tile: REF Tile, x, y: Number] RETURNS [REF Tile] = <<--a-symmetric>> TRUSTED BEGIN IF tile.value=deleted THEN ERROR TileDeleted; DO --south-- WHILE y=tile.en.pos.y DO tile _ tile.en ENDLOOP; IF x=tile.ne.pos.x THEN --east-- WHILE x>=tile.ne.pos.x DO tile _ LOOPHOLE[tile.ne] ENDLOOP ELSE EXIT ENDLOOP; RETURN [tile] END; EWSplit: PROCEDURE [plane: REF Tesselation, tile: REF Tile, x: Number] RETURNS [REF Tile] = <<-- The East one will be the new one.>> BEGIN eastTile: REF Tile _ NewTile[]; t: REF Tile; IF WEdge[tile]>=x OR EEdge[tile]<= x THEN ERROR; <<-- eastTile starts out a replica of tile>> eastTile^ _ tile^; plane.tilesInTesselationCount _ plane.tilesInTesselationCount + 1; <<-- Fix the tiles relative to each other.>> tile.ne _ RtoP[eastTile]; eastTile.sw _ tile; eastTile.pos.x _ x; <<-- Fix North boundary>> t _ eastTile.en; WHILE t.pos.x>=x DO t.ws _ RtoP[eastTile]; t _ t.sw ENDLOOP; tile.en _ t; <<-- Fix East boundary>> t _ PtoR[eastTile.ne]; WHILE t.sw=tile DO t.sw _ eastTile; t _ PtoR[t.ws] ENDLOOP; <<-- Fix South boundary>> t _ PtoR[tile.ws]; WHILE PtoR[t.ne].pos.x<=x DO t _ PtoR[t.ne] ENDLOOP; eastTile.ws _ RtoP[t]; WHILE t.en=tile DO t.en _ eastTile; t _ PtoR[t.ne] ENDLOOP; RETURN [eastTile] END; NSSplit: PROCEDURE [plane: REF Tesselation, tile: REF Tile, y: Number] RETURNS [REF Tile] = <<-- The North one will be the new one.>> BEGIN northTile: REF Tile _ NewTile[]; t: REF Tile; IF SEdge[tile]>=y OR NEdge[tile]<=y THEN ERROR; <<-- northTile starts out a replica of tile>> northTile^ _ tile^; plane.tilesInTesselationCount _ plane.tilesInTesselationCount + 1; <<-- Fix the tiles relative to each other.>> tile.en _ northTile; northTile.ws _ RtoP[tile]; northTile.pos.y _ y; <<-- Fix East boundary>> t _ PtoR[northTile.ne]; WHILE t.pos.y>=y DO t.sw _ northTile; t _ PtoR[t.ws] ENDLOOP; tile.ne _ RtoP[t]; <<-- Fix North boundary>> t _ northTile.en; WHILE t.ws=RtoP[tile] DO t.ws _ RtoP[northTile]; t _ t.sw ENDLOOP; <<-- Fix West boundary>> t _ tile.sw; WHILE t.en.pos.y<=y DO t _ t.en ENDLOOP; northTile.sw _ t; WHILE PtoR[t.ne]=tile DO t.ne _ RtoP[northTile]; t _ t.en ENDLOOP; RETURN [northTile] END; EWMerge: PROCEDURE [plane: REF Tesselation, tileW, tileE: REF Tile] RETURNS [REF 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.>> BEGIN <<--checks>> IF tileE.sw#tileW OR PtoR[tileW.ne]#tileE OR tileW.pos.y#tileE.pos.y OR tileW.en.pos.y#tileE.en.pos.y OR tileW.value#tileE.value THEN ERROR; <<-- Fix the tiles relative to each other.>> tileW.en _ tileE.en; tileW.ne _ tileE.ne; <<-- Fix North boundary>> FOR t: REF Tile _ tileW.en, t.sw WHILE PtoR[t.ws]=tileE DO t.ws _ RtoP[tileW]; ENDLOOP; <<-- Fix East boundary>> FOR t: REF Tile _ PtoR[tileW.ne], PtoR[t.ws] WHILE t.sw=tileE DO t.sw _ tileW; ENDLOOP; <<-- Fix South boundary>> FOR t: REF Tile _ PtoR[tileE.ws], PtoR[t.ne] WHILE t.en=tileE DO t.en _ tileW; ENDLOOP; tileE.value _ deleted; tileE.en _ NIL; IF plane.current=tileE THEN plane.current _ tileW; DisposeTile[tileE]; plane.tilesInTesselationCount _ plane.tilesInTesselationCount - 1; RETURN [tileW]; END; NSMerge: PROCEDURE [plane: REF Tesselation, tileS, tileN: REF Tile] RETURNS [REF 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.>> BEGIN <<--checks>> IF PtoR[tileN.ws]#tileS OR tileS.en#tileN OR tileS.pos.x#tileN.pos.x OR PtoR[tileS.ne].pos.x#PtoR[tileN.ne].pos.x OR tileS.value#tileN.value THEN ERROR; <<-- Fix the tiles relative to each other.>> tileS.ne _ tileN.ne; tileS.en _ tileN.en; <<-- Fix East boundary>> FOR t: REF Tile _ PtoR[tileS.ne], PtoR[t.ws] WHILE t.sw=tileN DO t.sw _ tileS; ENDLOOP; <<-- Fix North boundary>> FOR t: REF Tile _ tileS.en, t.sw WHILE PtoR[t.ws]=tileN DO t.ws _ RtoP[tileS]; ENDLOOP; <<-- Fix West boundary>> FOR t: REF Tile _ tileN.sw, t.en WHILE PtoR[t.ne]=tileN DO t.ne _ RtoP[tileS]; ENDLOOP; tileN.value _ deleted; tileN.en _ NIL; IF plane.current=tileN THEN plane.current _ tileS; DisposeTile[tileN]; plane.tilesInTesselationCount _ plane.tilesInTesselationCount - 1; RETURN [tileS]; END; PtoR: PROCEDURE [p: LONG POINTER TO Tile] RETURNS [REF Tile] ~ INLINE { TRUSTED { RETURN [LOOPHOLE[p]] } }; RtoP: PROCEDURE [r: REF Tile] RETURNS [LONG POINTER TO Tile] ~ INLINE { TRUSTED { RETURN [LOOPHOLE[r]] } }; InitTesselationBorderTiles[] END. <> <> <<>> <> <> <<>>