magic/grouter/grouteChan.c

1099 lines
29 KiB
C

/*
* grouteChan.c --
*
* Code to maintain a map of usable channel areas, using a
* tile plane. One tile is used for each "unobstructed"
* channel area, namely one that contains no embedded regions
* where the channel's density has been exceeded.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/styles.h"
#include "utils/hash.h"
#include "utils/heap.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "debug/debug.h"
#include "gcr/gcr.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "dbwind/dbwind.h"
#include "utils/signals.h"
#include "router/router.h"
#include "grouter/grouter.h"
#include "utils/netlist.h"
#include "utils/styles.h"
#include "textio/textio.h"
#include "utils/malloc.h"
/* Plane to hold channel information */
Plane *glChanPlane;
/* Dummy celldef whose PL_DRC_CHECK plane is identical to glChanPlane above */
CellDef *glChanDef;
CellUse *glChanUse;
TileTypeBitMask glMaskRiver; /* Mask of river channel types */
TileTypeBitMask glMaskNormal; /* Mask of normal channel type */
TileTypeBitMask glMaskChannel; /* Mask of all channel types */
int glChanSplitRiver();
int glChanRiverBlock();
int glChanFloodVFunc(), glChanFloodHFunc();
bool glChanClip();
int glChanClipFunc();
int glChanMergeFunc();
int glChanFeedFunc();
int glChanPaintFunc();
int glChanSetClient();
void glChanFreeMap();
void glChanCheckCover();
void glChanBlockDens();
void glChanFlood();
void glChanShowTiles();
int glChanShowFunc();
typedef struct pa
{
Rect pa_area;
TileType pa_type;
struct pa *pa_next;
} PaintArea;
PaintArea *glChanPaintList;
/*
* ----------------------------------------------------------------------------
*
* glChanBuildMap --
*
* Build a tile plane that represents all the channels in the routing
* problem. This tile plane will contain tiles of four types: CT_BLOCKED,
* CT_NORMAL, CT_HRIVER, and CT_VRIVER, corresponding to unusable routing
* area and the three types of channels respectively.
*
* Each channel tile's client field points back to the channel it covers.
* A single channel can be covered by several tiles, however. Initially,
* each normal channel is covered by a single tile, but each river tile
* is covered by several tiles. For example, the tiles covering a HRIVER
* channel are chosen such that there is never a vertical tile boundary
* on either their RHS or LHS except at the top or bottom of the CT_HRIVER
* tile. Situations such as the following:
*
* +-------+
* | |
* | | normal
* | |
* |HRIVER +------
* | |
* | | normal
* | |
* +-------+
*
* aren't allowed to exist; the HRIVER tile gets split:
*
* +-------+
* | |
* |HRIVER | normal
* | |
* +-------+------
* | |
* |HRIVER | normal
* | |
* +-------+
*
* The reason for splitting CT_HRIVER tiles in this way (and CT_VRIVER tiles
* vertically using similar criteria) is to ensure that there is a different
* river tile for each channel on its opposite side. This fact is crucial
* to the working of the inner loop of the global router; see the code in
* groutePair.c for details of the algorithm used.
*
* Results:
* None.
*
* Side effects:
* Builds the tile plane glChanPlane as described above.
*
* ----------------------------------------------------------------------------
*/
void
glChanBuildMap(chanList)
GCRChannel *chanList; /* List of all channels in routing problem */
{
GCRChannel *ch;
bool workDone;
if (glChanPlane == NULL)
{
DBNewYank("__CHANMAP__", &glChanUse, &glChanDef);
glChanPlane = glChanDef->cd_planes[PL_DRC_CHECK];
glChanFreeMap();
TTMaskSetOnlyType(&glMaskRiver, CHAN_HRIVER);
TTMaskSetType(&glMaskRiver, CHAN_VRIVER);
TTMaskSetOnlyType(&glMaskNormal, CHAN_NORMAL);
TTMaskSetMask3(&glMaskChannel, &glMaskNormal, &glMaskRiver);
}
/*
* First pass: use painting to cover the areas of all channels
* with the appropriate type of tile.
*/
for (ch = chanList; ch; ch = ch->gcr_next)
DBPaintPlane(glChanPlane, &ch->gcr_area, DBStdWriteTbl(ch->gcr_type),
(PaintUndoInfo *) NULL);
if (DebugIsSet(glDebugID, glDebTiles))
glChanShowTiles("After painting all channels");
/*
* Second pass: use splits and merges to ensure that each channel
* covers exactly one tile. Leave this tile's ti_client field
* pointing to 'ch'.
*/
do
{
workDone = FALSE;
for (ch = chanList; ch; ch = ch->gcr_next)
if (glChanClip(ch))
workDone = TRUE;
}
while (workDone);
if (DebugIsSet(glDebugID, glDebTiles))
glChanShowTiles("After splits and merges");
if (DebugIsSet(glDebugID, glDebChan))
glChanCheckCover(chanList, &glMaskChannel);
/*
* Third pass: find regions of maximum density and "paint" them into
* the tile map. Regions where the density is exceeded in only one
* of the two possible directions through a normal channel result in
* painting a CHAN_HRIVER or CHAN_VRIVER tile as appropriate, while
* regions that are totally blocked result in CHAN_BLOCKED tiles.
* The client fields of the remaining nonblocked tiles are left
* pointing to the channels those tiles overlap.
*/
for (ch = chanList; ch; ch = ch->gcr_next)
glChanBlockDens(ch);
if (DebugIsSet(glDebugID, glDebTiles))
glChanShowTiles("After density blockages");
/*
* Fourth pass: split river tiles as necessary to ensure
* that no river tile has a perpendicular tile boundary
* on either of the sides from which signals can enter.
*/
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect, &glMaskRiver,
glChanSplitRiver, (ClientData) NULL))
/* Nothing */;
if (DebugIsSet(glDebugID, glDebTiles))
glChanShowTiles("After splitting river tiles");
/*
* Final pass: turn any river tiles whose pins are completely
* blocked on one side into blocked tiles. This just involves
* changing the type of the tile.
*/
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect,
&glMaskRiver, glChanRiverBlock, (ClientData) NULL);
if (DebugIsSet(glDebugID, glDebTiles))
glChanShowTiles("After blocking river tiles");
if (DebugIsSet(glDebugID, glDebChan))
{
glChanCheckCover(chanList, &glMaskNormal);
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect,
&glMaskChannel, glChanFeedFunc, (ClientData) NULL);
}
}
/*
* glChanFeedFunc --
*
* Used for leaving feedback for each channel tile above.
*
* Results:
* Always returns 0.
*
* Side effects:
* Leaves feedback for each tile found.
*/
int
glChanFeedFunc(tile)
Tile *tile;
{
char *mesg;
Rect r;
switch (TiGetType(tile))
{
case CHAN_NORMAL: mesg = "normal channel"; break;
case CHAN_HRIVER: mesg = "horizontal river channel"; break;
case CHAN_VRIVER: mesg = "vertical river channel"; break;
}
TITORECT(tile, &r);
DBWFeedbackAdd(&r, mesg, EditCellUse->cu_def, 1, STYLE_OUTLINEHIGHLIGHTS);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* glChanFreeMap --
*
* Free the tile plane built by glChanBuildMap().
*
* Results:
* None.
*
* Side effects:
* Frees memory.
*
* ----------------------------------------------------------------------------
*/
void
glChanFreeMap()
{
Tile *newCenterTile;
/* Eliminate all the tiles from this plane */
DBFreePaintPlane(glChanPlane);
/* Allocate a new central space tile */
newCenterTile = TiAlloc();
glChanPlane->pl_hint = newCenterTile;
TiSetBody(newCenterTile, CHAN_BLOCKED);
dbSetPlaneTile(glChanPlane, newCenterTile);
}
/*
* ----------------------------------------------------------------------------
*
* glChanCheckCover --
*
* Debugging procedure used to check that each channel whose type is
* contained in 'mask' is covered by exactly one tile. Also check
* all channels to make sure that the tiles they cover are the same
* as the channel type (except for blockages).
*
* Results:
* None.
*
* Side effects:
* Leaves feedback where errors occur.
*
* ----------------------------------------------------------------------------
*/
int glChanCheckCount;
void
glChanCheckCover(chanList, mask)
GCRChannel *chanList;
TileTypeBitMask *mask;
{
int glChanCheckFunc();
GCRChannel *ch;
char mesg[1024];
for (ch = chanList; ch; ch = ch->gcr_next)
{
glChanCheckCount = 0;
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
&DBAllTypeBits, glChanCheckFunc,
(ClientData) ch);
if (TTMaskHasType(mask, ch->gcr_type) && glChanCheckCount != 1)
{
(void) sprintf(mesg, "%d tiles over channel", glChanCheckCount);
DBWFeedbackAdd(&ch->gcr_area, mesg, EditCellUse->cu_def, 1,
STYLE_PALEHIGHLIGHTS);
}
}
}
/*
* glChanCheckFunc --
*
* Called by above for each tile overlapping a channel.
*
* Results:
* Always returns 0.
*
* Side effects:
* May leave feedback if a channel is overlapped by the
* wrong type of tile.
*/
int
glChanCheckFunc(tile, ch)
Tile *tile;
GCRChannel *ch;
{
char mesg[1024];
Rect r;
glChanCheckCount++;
if (NOTBLOCKED(tile))
{
if (TiGetType(tile) != ch->gcr_type)
{
TITORECT(tile, &r);
(void) sprintf(mesg, "Different tile type %d for chan %d",
TiGetType(tile), ch->gcr_type);
DBWFeedbackAdd(&r, mesg, EditCellUse->cu_def,
1, STYLE_MEDIUMHIGHLIGHTS);
}
if (tile->ti_client != (ClientData) ch)
{
TITORECT(tile, &r);
(void) sprintf(mesg, "Tile client 0x%"DLONG_PREFIX"x doesn't match chan %p",
(dlong) tile->ti_client, (void *) ch);
DBWFeedbackAdd(&r, mesg, EditCellUse->cu_def,
1, STYLE_MEDIUMHIGHLIGHTS);
}
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* glChanClip --
*
* Leave a single tile overlapping the area of channel 'ch'.
* Leave this tile pointing to 'ch'.
*
* Results:
* Returns TRUE if we had to do any splits or merges,
* FALSE if everything was as it should be.
*
* Side effects:
* May split or merge tiles.
*
* ----------------------------------------------------------------------------
*/
bool
glChanClip(ch)
GCRChannel *ch;
{
bool ret;
ret = FALSE;
/* First part: clip every tile overlapping this channel to its boundary */
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
&DBAllTypeBits, glChanClipFunc, (ClientData) &ch->gcr_area))
ret = TRUE;
/* Second part: set the client field of every tile to 'ch' */
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
&DBAllTypeBits, glChanSetClient, (ClientData) ch);
/*
* Third part: merge every tile overlapping this channel into a
* single one. The procedure called for each tile extracts the
* channel boundaries from the client field of the tile, and
* then generates merges within the channel boundary.
*/
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &ch->gcr_area,
&DBAllTypeBits, glChanMergeFunc, (ClientData) NULL))
ret = TRUE;
if (DebugIsSet(glDebugID, glDebTiles))
{
char mesg[256];
(void) sprintf(mesg, "After clipping chan %p", (void *) ch);
glChanShowTiles(mesg);
}
return ret;
}
int
glChanSetClient(tile, cdata)
Tile *tile;
ClientData cdata;
{
tile->ti_client = cdata;
return 0;
}
void
glChanShowTiles(mesg)
char *mesg;
{
char answer[100], m[1024];
DBWAreaChanged(glChanDef, &TiPlaneRect, DBW_ALLWINDOWS, 0);
WindUpdate();
(void) sprintf(m, "%s: --more-- (t for tiles): ", mesg);
if (TxGetLinePrompt(answer, sizeof answer, m) == NULL || answer[0] != 't')
return;
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &TiPlaneRect,
&DBAllTypeBits, glChanShowFunc, (ClientData) NULL);
}
int
glChanShowFunc(tile)
Tile *tile;
{
GCRChannel *ch;
char mesg[1024];
Rect r;
TITORECT(tile, &r);
ShowRect(EditCellUse->cu_def, &r, STYLE_PALEHIGHLIGHTS);
(void) sprintf(mesg, "tile ch=%"DLONG_PREFIX"x type=%d",
(dlong) tile->ti_client, TiGetType(tile));
TxMore(mesg);
ShowRect(EditCellUse->cu_def, &r, STYLE_ERASEHIGHLIGHTS);
if (tile->ti_client == (ClientData) CLIENTDEFAULT)
return 0;
ch = (GCRChannel *) tile->ti_client;
ShowRect(EditCellUse->cu_def, &ch->gcr_area, STYLE_MEDIUMHIGHLIGHTS);
(void) sprintf(mesg, "chan %p type=%d", (void *) ch, ch->gcr_type);
TxMore(mesg);
ShowRect(EditCellUse->cu_def, &ch->gcr_area, STYLE_ERASEHIGHLIGHTS);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* glChanClipFunc --
*
* Called by DBSrPaintArea() on behalf of glChanClip() above for each
* tile overlapping 'area': splits 'tile' as necessary to ensure that
* it is completely contained within area.
*
* Results:
* Returns 1 if we did any splitting, 0 otherwise.
*
* Side effects:
* May split tiles.
* When a tile is split, the new tile's body and ti_client
* field are set to copies of the original tile's.
*
* ----------------------------------------------------------------------------
*/
int
glChanClipFunc(tile, area)
Tile *tile;
Rect *area;
{
ClientData tileClient = tile->ti_client;
TileType type = TiGetType(tile);
Tile *newTile;
int ret;
ret = 0;
if (LEFT(tile) < area->r_xbot)
{
tile = TiSplitX(tile, area->r_xbot);
TiSetBody(tile, type);
tile->ti_client = tileClient;
ret = 1;
}
if (BOTTOM(tile) < area->r_ybot)
{
tile = TiSplitY(tile, area->r_ybot);
TiSetBody(tile, type);
tile->ti_client = tileClient;
ret = 1;
}
if (RIGHT(tile) > area->r_xtop)
{
newTile = TiSplitX(tile, area->r_xtop);
TiSetBody(newTile, type);
newTile->ti_client = tileClient;
ret = 1;
}
if (TOP(tile) > area->r_ytop)
{
newTile = TiSplitY(tile, area->r_ytop);
TiSetBody(newTile, type);
newTile->ti_client = tileClient;
ret = 1;
}
return ret;
}
/*
* ----------------------------------------------------------------------------
*
* glChanMergeFunc --
*
* Called by DBSrPaintArea() on behalf of glChanClip() above for each
* tile contained within some area. Merges this tile with any of
* its neighbors within its channel's area, where the channel is
* pointed to by the ti_client field. We leave the resulting tile's
* ti_client field pointing to this channel ('ch').
*
* IMPORTANT ASSUMPTION: no tile actually overlaps the boundary of
* ch->gcr_area. This is ensured by the caller's having called
* glChanClipFunc() enough times to have split all the tiles
* that originally might have crossed the channel's boundary.
*
* Results:
* Returns 1 if we did any merging, 0 otherwise.
*
* Side effects:
* May merge tiles.
* Leaves tile->ti_client set to 'ch'.
*
* ----------------------------------------------------------------------------
*/
int
glChanMergeFunc(tile)
Tile *tile;
{
GCRChannel *ch = (GCRChannel *) tile->ti_client;
Tile *tp;
int ret;
ret = 0;
if (TOP(tile) < ch->gcr_area.r_ytop)
{
tp = RT(tile);
if (TiGetType(tp) == TiGetType(tile)
&& LEFT(tp) == LEFT(tile)
&& RIGHT(tp) == RIGHT(tile))
{
TiJoinY(tile, tp, glChanPlane);
ret = 1;
}
}
if (LEFT(tile) > ch->gcr_area.r_xbot)
{
tp = BL(tile);
if (TiGetType(tp) == TiGetType(tile)
&& TOP(tp) == TOP(tile)
&& BOTTOM(tp) == BOTTOM(tile))
{
TiJoinX(tile, tp, glChanPlane);
ret = 1;
}
}
if (BOTTOM(tile) > ch->gcr_area.r_ybot)
{
tp = LB(tile);
if (TiGetType(tp) == TiGetType(tile)
&& LEFT(tp) == LEFT(tile)
&& RIGHT(tp) == RIGHT(tile))
{
TiJoinY(tile, tp, glChanPlane);
ret = 1;
}
}
if (RIGHT(tile) < ch->gcr_area.r_xtop)
{
tp = TR(tile);
if (TiGetType(tp) == TiGetType(tile)
&& TOP(tp) == TOP(tile)
&& BOTTOM(tp) == BOTTOM(tile))
{
TiJoinX(tile, tp, glChanPlane);
ret = 1;
}
}
return ret;
}
/*
* ----------------------------------------------------------------------------
*
* glChanBlockDens --
*
* Find regions of maximum density in 'ch' (if 'ch' is a normal channel) and
* "paint" them into the tile map. Blockages of a single direction result
* in CHAN_HRIVER or CHAN_VRIVER tiles being painted as appropriate; if
* blockages exist in both directions, then we produce a CHAN_BLOCKED tile.
*
* We rely on the fact that regions of maximum density extend completely
* across a channel, and hence completely across any tile (since tiles are
* never bigger than channels). The client fields of the any tiles in the
* area of the channel that aren't totally blocked are left pointing to
* the overlapping channel.
*
* If columns i through j inclusive have maximum density, then the blockage
* we paint extends from midway between columns i-1 and i to midway between
* column j and j+1, guaranteeing that tile boundaries will always fall
* on half-grid lines.
*
* Results:
* None.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
void
glChanBlockDens(ch)
GCRChannel *ch;
{
GlobChan *gc = (GlobChan *) ch->gcr_client;
int halfGrid, shiftedOrigin;
DensMap *dRow, *dCol;
PaintArea *pa;
short *dens;
int lo, hi;
Rect area;
if (ch->gcr_type != CHAN_NORMAL)
return;
dCol = &gc->gc_postDens[CZ_COL];
dRow = &gc->gc_postDens[CZ_ROW];
halfGrid = RtrGridSpacing / 2;
glChanPaintList = (PaintArea *) NULL;
if (dCol->dm_max >= dCol->dm_cap)
{
dens = dCol->dm_value;
area.r_ybot = ch->gcr_area.r_ybot;
area.r_ytop = ch->gcr_area.r_ytop;
shiftedOrigin = ch->gcr_origin.p_x - halfGrid;
for (lo = 1; lo < dCol->dm_size; lo++)
{
if (dens[lo] >= dCol->dm_cap)
{
hi = lo+1;
while (dens[hi] >= dCol->dm_cap && hi < dCol->dm_size)
hi++;
/* Remember area of blocked tile to paint */
area.r_xbot = shiftedOrigin + lo * RtrGridSpacing;
area.r_xtop = shiftedOrigin + hi * RtrGridSpacing;
pa = (PaintArea *) mallocMagic((unsigned)(sizeof (PaintArea)));
pa->pa_area = area;
pa->pa_next = glChanPaintList;
pa->pa_type = CHAN_VRIVER;
glChanPaintList = pa;
/* Will get incremented in loop header */
lo = hi-1;
}
}
}
if (dRow->dm_max >= dRow->dm_cap)
{
dens = dRow->dm_value;
area.r_xbot = ch->gcr_area.r_xbot;
area.r_xtop = ch->gcr_area.r_xtop;
shiftedOrigin = ch->gcr_origin.p_y - halfGrid;
for (lo = 1; lo < dRow->dm_size; lo++)
{
if (dens[lo] >= dRow->dm_cap)
{
hi = lo+1;
while (dens[hi] >= dRow->dm_cap && hi < dRow->dm_size)
hi++;
/* Remember area of blocked tile to paint */
area.r_ybot = shiftedOrigin + lo * RtrGridSpacing;
area.r_ytop = shiftedOrigin + hi * RtrGridSpacing;
pa = (PaintArea *) mallocMagic((unsigned)(sizeof (PaintArea)));
pa->pa_area = area;
pa->pa_next = glChanPaintList;
pa->pa_type = CHAN_HRIVER;
glChanPaintList = pa;
/* Will get incremented in loop header */
lo = hi-1;
}
}
}
/*
* Now do all the work.
* First paint all the blocked areas on glChanPaintList.
* Then make a second pass through this list, searching the
* area around each blocked area tile we painted for river
* tiles of the appropriate type, and propagating this blockage
* to those river tiles as well. Do this until the wavefront
* stops, which will happen as soon as we reach a normal channel.
*/
do
{
for (pa = glChanPaintList; pa; pa = pa->pa_next)
{
/* Clip tiles overlapped by pa->pa_area */
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &pa->pa_area,
&DBAllTypeBits, glChanClipFunc, PTR2CD(&pa->pa_area)))
/* Nothing */;
/* Change the type of all tiles within the area */
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &pa->pa_area,
&DBAllTypeBits, glChanPaintFunc, INT2CD(pa->pa_type));
/*
* Allow merging, as long as no tiles get merged across
* channel boundaries.
*/
while (DBSrPaintArea((Tile *) NULL, glChanPlane, &pa->pa_area,
&DBAllTypeBits, glChanMergeFunc, (ClientData) NULL));
}
/* Second pass to propagate blockages to nearby areas */
for (pa = glChanPaintList, glChanPaintList = NULL; pa; pa = pa->pa_next)
{
glChanFlood(&pa->pa_area, pa->pa_type);
freeMagic((char *) pa);
}
} while (glChanPaintList != NULL);
}
int
glChanPaintFunc(tile, type)
Tile *tile;
TileType type;
{
static TileType changeTable[4][4] = {
/* Paint atop CHAN_NORMAL */
{ CHAN_NORMAL, CHAN_HRIVER, CHAN_VRIVER, CHAN_BLOCKED },
/* Paint atop CHAN_HRIVER */
{ CHAN_HRIVER, CHAN_HRIVER, CHAN_BLOCKED, CHAN_BLOCKED },
/* Paint atop CHAN_VRIVER */
{ CHAN_VRIVER, CHAN_BLOCKED, CHAN_VRIVER, CHAN_BLOCKED },
/* Paint atop CHAN_BLOCKED */
{ CHAN_BLOCKED, CHAN_BLOCKED, CHAN_BLOCKED, CHAN_BLOCKED },
};
TiSetBody(tile, changeTable[TiGetType(tile)][type]);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* glChanFlood --
*
* Search all four sides of 'area' (an area that has just been painted
* as type) for river tiles of the appropriate type (where appropriate
* means tiles that are completely blocked by a tile of type type to
* the side to which it lies).
*
* Each time an appropriate river tiles is found, record a CHAN_BLOCKED
* rectangle on the list glChanPaintList that extends from 'area' through to
* the other side of the river channel.
*
* The purpose of this procedure is so that the presence of the blockage
* on one side of a river tile will be visible from its other side.
*
* Results:
* None.
*
* Side effects:
* May prepend stuff to glChanPaintList.
*
* ----------------------------------------------------------------------------
*/
void
glChanFlood(area, type)
Rect *area;
TileType type;
{
TileTypeBitMask hMask, vMask;
Rect outside;
TTMaskSetOnlyType(&hMask, CHAN_HRIVER);
TTMaskSetOnlyType(&vMask, CHAN_VRIVER);
if (type != CHAN_VRIVER)
{
/* TOP */
outside = *area;
outside.r_ybot = area->r_ytop;
outside.r_ytop = area->r_ytop + 1;
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &vMask,
glChanFloodVFunc, (ClientData) area);
/* BOTTOM */
outside = *area;
outside.r_ybot = area->r_ybot - 1;
outside.r_ytop = area->r_ybot;
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &vMask,
glChanFloodVFunc, (ClientData) area);
}
if (type != CHAN_HRIVER)
{
/* LEFT */
outside = *area;
outside.r_xbot = area->r_xbot - 1;
outside.r_xtop = area->r_xbot;
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &hMask,
glChanFloodHFunc, (ClientData) area);
/* RIGHT */
outside = *area;
outside.r_xbot = area->r_xtop;
outside.r_xtop = area->r_xtop + 1;
(void) DBSrPaintArea((Tile *) NULL, glChanPlane, &outside, &hMask,
glChanFloodHFunc, (ClientData) area);
}
}
int
glChanFloodVFunc(tile, area)
Tile *tile;
Rect *area;
{
PaintArea *pa;
pa = (PaintArea *) mallocMagic((unsigned) (sizeof (PaintArea)));
pa->pa_area.r_xbot = MAX(area->r_xbot, LEFT(tile));
pa->pa_area.r_xtop = MIN(area->r_xtop, RIGHT(tile));
pa->pa_area.r_ybot = BOTTOM(tile);
pa->pa_area.r_ytop = TOP(tile);
pa->pa_next = glChanPaintList;
pa->pa_type = CHAN_BLOCKED;
glChanPaintList = pa;
return 0;
}
int
glChanFloodHFunc(tile, area)
Tile *tile;
Rect *area;
{
PaintArea *pa;
pa = (PaintArea *) mallocMagic((unsigned)(sizeof (PaintArea)));
pa->pa_area.r_ybot = MAX(area->r_ybot, BOTTOM(tile));
pa->pa_area.r_ytop = MIN(area->r_ytop, TOP(tile));
pa->pa_area.r_xbot = LEFT(tile);
pa->pa_area.r_xtop = RIGHT(tile);
pa->pa_next = glChanPaintList;
pa->pa_type = CHAN_BLOCKED;
glChanPaintList = pa;
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* glChanSplitRiver --
*
* Called for each river tile (CHAN_HRIVER or CHAN_VRIVER) in glChanPlane.
* Search along the two sides of this tile from which signals can enter
* (the LHS and RHS for CHAN_HRIVER, or the top and bottom for CHAN_VRIVER),
* and make sure that there are no tile boundaries involving some type of
* tile other than CHAN_BLOCKED along that side. If there is a boundary,
* split tile at the boundary.
*
* Results:
* Returns 1 if we did any splitting, 0 if not.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
int
glChanSplitRiver(tile)
Tile *tile;
{
ClientData tileClient = tile->ti_client;
Tile *tp, *newTile;
int ret;
ret = 0;
if (TiGetType(tile) == CHAN_HRIVER)
{
for (tp = BL(tile); TOP(tp) < TOP(tile); tp = RT(tp))
{
if (NOTBLOCKED(tp) || NOTBLOCKED(RT(tp)))
{
tile = TiSplitY(tile, TOP(tp));
TiSetBody(tile, CHAN_HRIVER);
tile->ti_client = tileClient;
ret = 1;
}
}
for (tp = TR(tile); BOTTOM(tp) > BOTTOM(tile); tp = LB(tp))
{
if (NOTBLOCKED(tp) || NOTBLOCKED(LB(tp)))
{
newTile = TiSplitY(tile, BOTTOM(tp));
TiSetBody(newTile, CHAN_HRIVER);
newTile->ti_client = tileClient;
ret = 1;
}
}
}
else
{
for (tp = RT(tile); LEFT(tp) > LEFT(tile); tp = BL(tp))
{
if (NOTBLOCKED(tp) || NOTBLOCKED(BL(tp)))
{
newTile = TiSplitX(tile, LEFT(tp));
TiSetBody(newTile, CHAN_VRIVER);
newTile->ti_client = tileClient;
ret = 1;
}
}
for (tp = LB(tile); RIGHT(tp) < RIGHT(tile); tp = TR(tp))
{
if (NOTBLOCKED(tp) || NOTBLOCKED(TR(tp)))
{
tile = TiSplitX(tile, RIGHT(tp));
TiSetBody(tile, CHAN_VRIVER);
tile->ti_client = tileClient;
ret = 1;
}
}
}
return ret;
}
/*
* ----------------------------------------------------------------------------
*
* glChanRiverBlock --
*
* Called for each river routing tile. Checks to make sure that at least
* some of the pins along the usable sides of this channel are still free;
* if they're not, we change this tile's type to CHAN_BLOCKED.
*
* Note: river routing tiles can overlay portions of a normal channel
* in which the density of the channel equals its capacity in either
* the horizontal or vertical direction.
*
* Results:
* Always returns 0.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
int
glChanRiverBlock(tile)
Tile *tile;
{
GCRPin *pin, *pinLast;
GCRChannel *ch;
int lo, hi;
ch = (GCRChannel *) tile->ti_client;
if (TiGetType(tile) == CHAN_HRIVER)
{
lo = (BOTTOM(tile) - ch->gcr_origin.p_y) / RtrGridSpacing;
hi = (TOP(tile) - ch->gcr_origin.p_y) / RtrGridSpacing;
if (lo < 1) lo = 1;
if (hi > ch->gcr_width) hi = ch->gcr_width;
pinLast = &ch->gcr_lPins[hi];
for (pin = &ch->gcr_lPins[lo]; pin <= pinLast; pin++)
if (pin->gcr_pId == NULL && pin->gcr_linked)
return 0;
pinLast = &ch->gcr_rPins[hi];
for (pin = &ch->gcr_rPins[lo]; pin <= pinLast; pin++)
if (pin->gcr_pId == NULL && pin->gcr_linked)
return 0;
}
else
{
/* CHAN_VRIVER */
lo = (LEFT(tile) - ch->gcr_origin.p_x) / RtrGridSpacing;
hi = (RIGHT(tile) - ch->gcr_origin.p_x) / RtrGridSpacing;
if (lo < 1) lo = 1;
if (hi > ch->gcr_length) hi = ch->gcr_length;
pinLast = &ch->gcr_bPins[hi];
for (pin = &ch->gcr_bPins[lo]; pin <= pinLast; pin++)
if (pin->gcr_pId == NULL && pin->gcr_linked)
return 0;
pinLast = &ch->gcr_tPins[hi];
for (pin = &ch->gcr_tPins[lo]; pin <= pinLast; pin++)
if (pin->gcr_pId == NULL && pin->gcr_linked)
return 0;
}
TiSetBody(tile, CHAN_BLOCKED);
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* glChanPinToTile --
*
* Find the tile corresponding to the pin 'pin'.
* The pin mustn't be in one of the corners of a channel.
*
* Results:
* Returns a pointer to the tile found.
* Returns NULL if the tile found was of type CHAN_BLOCKED.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
Tile *
glChanPinToTile(hintTile, pin)
Tile *hintTile; /* Hint for starting the tile search */
GCRPin *pin; /* Find tile containing this pin */
{
GCRChannel *ch;
Tile *tp;
Point p;
Rect r;
/* Figure out which channel tile the output point lies in */
p = pin->gcr_point;
switch (pin->gcr_side)
{
case GEO_NORTH: p.p_y--; break;
case GEO_EAST: p.p_x--; break;
}
tp = TiSrPoint(hintTile, glChanPlane, &p);
if (TiGetType(tp) == CHAN_BLOCKED)
return (Tile *) NULL;
ASSERT(tp->ti_client != (ClientData) CLIENTDEFAULT, "glChanPinToTile");
ch = (GCRChannel *) tp->ti_client;
TITORECT(tp, &r);
ASSERT(GEO_SURROUND(&ch->gcr_area, &r), "glChanPinToTile");
return tp;
}