mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-09-08 11:43:43 +02:00
and forcing it to be passed as an argument to all the callback functions for the search routines that require it. Magic now compiles and runs with the new code, but there are a number of known issues that need to be fixed up. Committing now so that I can rebase on the last update to the master branch.
1118 lines
30 KiB
C
1118 lines
30 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, dinfo, clientdata)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
ClientData clientdata; /* (unused) */
|
|
{
|
|
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();
|
|
PlaneSetHint(glChanPlane, 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, dinfo, ch)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
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, dinfo, clientdata)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
ClientData clientdata; /* (unused) */
|
|
{
|
|
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, dinfo, area)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
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, dinfo, clientdata)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
ClientData clientdata; /* (unused) */
|
|
{
|
|
GCRChannel *ch = (GCRChannel *) tile->ti_client;
|
|
Tile *delayed = NULL; /* delayed free to extend lifetime */
|
|
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))
|
|
{
|
|
TiJoinY1(&delayed, 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))
|
|
{
|
|
TiJoinX1(&delayed, 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))
|
|
{
|
|
TiJoinY1(&delayed, 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))
|
|
{
|
|
TiJoinX1(&delayed, tile, tp, glChanPlane);
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
TiFreeIf(delayed);
|
|
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 */
|
|
free_magic1_t mm1 = freeMagic1_init();
|
|
for (pa = glChanPaintList, glChanPaintList = NULL; pa; pa = pa->pa_next)
|
|
{
|
|
glChanFlood(&pa->pa_area, pa->pa_type);
|
|
freeMagic1(&mm1, (char *) pa);
|
|
}
|
|
freeMagic1_end(&mm1);
|
|
} while (glChanPaintList != NULL);
|
|
}
|
|
|
|
int
|
|
glChanPaintFunc(tile, dinfo, type)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
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, dinfo, area)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
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, dinfo, area)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
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, dinfo, clientdata)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
ClientData clientdata; /* (unused) */
|
|
{
|
|
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, dinfo, clientdata)
|
|
Tile *tile;
|
|
TileType dinfo; /* (unused) */
|
|
ClientData clientdata; /* (unused) */
|
|
{
|
|
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;
|
|
}
|