Files
magic/router/rtrDcmpose.c
T
R. Timothy Edwards 516c9d7635 First cut of pulling the TT_SIDE bit out of the tile database
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.
2026-01-09 12:05:03 -05:00

1031 lines
28 KiB
C

/*
* rtrDcmpose.c --
*
* Channel decomposition module.
*
* Create a cell tile plane where each space tile in the error
* plane represents a channel to be separately routed by the
* channel router.
*
* Enumerate cell tile corners, choosing the shortest horizontal or
* vertical extention from a corner to another cell or a previously
* defined channel boundary. Split or merge tiles accordingly.
*
* The ti_client field of space tiles is used is a boolean flag
* in order to distinguish between horizontal edges generated by
* the original plane and horizontal edges defining channels. This
* is done in the new, generated plane--not in the original plane.
*
* *********************************************************************
* * 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. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/router/rtrDcmpose.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/times.h>
#include "utils/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "dbwind/dbwind.h"
#include "utils/heap.h"
#include "utils/undo.h"
#include "router/router.h"
#include "router/rtrDcmpose.h"
#include "gcr/gcr.h"
#include "grouter/grouter.h"
#include "utils/netlist.h"
#include "utils/styles.h"
#include "utils/malloc.h"
#include "netmenu/netmenu.h"
#include "debug/debug.h"
/* C99 compat */
#include "router/routerInt.h"
/* The following tile types are used during channel decomposition */
#define CELLTILE 1 /* Cell tile -- no channels here */
#define USERCHAN 2 /* User-defined channel */
bool rtrDidInit = FALSE; /* TRUE when rtrTileToChannel initialized */
/* Area being routed; set in RtrDecompose */
Rect RouteArea;
/* Forward declarations */
extern int rtrSrCells();
extern void rtrRoundRect();
extern void rtrHashKill();
extern void rtrSplitToArea();
extern void rtrMarkChannel();
extern void rtrMerge(Tile **delay1, Tile *tup, Tile *tdn, Plane *plane);
bool rtrUseCorner();
/*
* ----------------------------------------------------------------------------
*
* RtrDecomposeName --
*
* Interface to commands module; perform channel decomposition
* over the area 'area', as though we would be routing the netlist
* with the name 'name'. If 'name' is NULL, don't assume any
* netlist; if it is the string "-", use the current netlist.
*
* Results:
* Pointer to the def holding the decomposed channel tiles. If
* the area is too small to be useful, returns NULL.
*
* Side effects:
* See RtrDecompose().
*
* ----------------------------------------------------------------------------
*/
CellDef *
RtrDecomposeName(routeUse, area, name)
CellUse *routeUse; /* Cell to be decomposed */
Rect *area; /* Confine channels to this area */
char *name; /* Name of netlist if non-NULL; otherwise, use the
* name of the current netlist or that of routeUse
* as described above.
*/
{
NLNetList netList, *netListPtr = (NLNetList *) NULL;
CellDef *def;
if (name)
{
if (strcmp(name, "-") == 0)
name = routeUse->cu_def->cd_name;
NMNewNetlist(name);
if (NLBuild(routeUse, &netList) <= 0)
TxError("No nets in netlist.\n");
else
netListPtr = &netList;
}
def = RtrDecompose(routeUse, area, netListPtr);
/* Clean up global routing information */
if (netListPtr)
NLFree(netListPtr);
return (def);
}
/*
* ----------------------------------------------------------------------------
*
* RtrDecompose --
*
* Top level function of the channel decomposition code. Initialize
* and then enumerate subcells of the edit cell for processing.
* Channels can currently appear only in empty space where there
* are no subcells.
*
* The list of all nets to route is pointed to by 'netList'; this
* will eventually be used when support for over-cell channels is
* put back in.
*
* Results:
* Pointer to the def holding the decomposed channel tiles. If
* the area is too small to be useful, returns NULL.
*
* Side effects:
* The DRC error plane of the returned cell def is marked with space
* tiles [ NO LONGER MAXIMAL HORIZONTAL ] representing channels.
* Modifies area to round it down to even grid points. Modifies
* RouteArea to hold final routing area.
*
* ----------------------------------------------------------------------------
*/
CellDef *
RtrDecompose(routeUse, area, netList)
CellUse *routeUse;
Rect *area;
NLNetList *netList;
{
SearchContext scx;
CellDef *cdTo;
int tmp;
/*
* Redoing the channel structure invalidates the RtrTileToChannel table.
* Reinitialize the hash table before proceeding.
*/
if (rtrDidInit) rtrHashKill(&RtrTileToChannel);
HashInit(&RtrTileToChannel, 128, 1);
rtrDidInit = TRUE;
/*
* Round area up so that its edges are at the canonical places
* halfway between grid points.
*/
tmp = RTR_GRIDUP(area->r_xtop, RtrOrigin.p_x) - RtrGridSpacing/2;
if (tmp < area->r_xtop) area->r_xtop = tmp + RtrGridSpacing;
else area->r_xtop = tmp;
tmp = RTR_GRIDUP(area->r_xbot, RtrOrigin.p_x) - RtrGridSpacing/2;
if (tmp > area->r_xbot) area->r_xbot = tmp - RtrGridSpacing;
else area->r_xbot = tmp;
tmp = RTR_GRIDUP(area->r_ytop, RtrOrigin.p_y) - RtrGridSpacing/2;
if (tmp < area->r_ytop) area->r_ytop = tmp + RtrGridSpacing;
else area->r_ytop = tmp;
tmp = RTR_GRIDUP(area->r_ybot, RtrOrigin.p_y) - RtrGridSpacing/2;
if (tmp > area->r_ybot) area->r_ybot = tmp - RtrGridSpacing;
else area->r_ybot = tmp;
RouteArea = *area;
if (GEO_RECTNULL(area)) return NULL;
cdTo = RtrFindChannelDef();
/*
* Paint non-space tiles in both the DRC check and error planes where
* cells are in the source def. Pass the search area to rtrSrCells()
* vi the global RouteArea. The code in rtrSrCells() takes care of
* leaving empty space wherever there are __CHANNEL__ labels.
*
* We make two copies of the channel information because it isn't
* safe to be both searching and updating the same plane. Thus,
* one plane (DRC check) is used for searching, but updates are
* made in the other plane.
*/
UndoDisable();
DBClearPaintPlane(cdTo->cd_planes[PL_DRC_ERROR]);
DBClearPaintPlane(cdTo->cd_planes[PL_DRC_CHECK]);
scx.scx_use = routeUse;
scx.scx_area = RouteArea;
scx.scx_trans = GeoIdentityTransform;
(void) DBCellSrArea(&scx, rtrSrCells, (ClientData) cdTo);
/* Split space tiles to the edges of the routing area */
rtrSplitToArea(&RouteArea, cdTo);
/*
* Clear the valid flags for horizontal edges for all space tiles in
* the error plane of the result cell.
*/
(void) DBSrPaintArea((Tile *) NULL, cdTo->cd_planes[PL_DRC_ERROR],
&RouteArea, &DBAllTypeBits, rtrSrClear,
(ClientData) &RouteArea);
/*
* Enumerate all tiles in the given area.
* If a tile is not a space tile, then perform the corner
* extension algorithm.
*/
(void) DBSrPaintArea((Tile *) NULL, cdTo->cd_planes[PL_DRC_CHECK],
&RouteArea, &DBAllTypeBits, rtrSrFunc,
(ClientData) (cdTo->cd_planes[PL_DRC_ERROR]));
/* Allow the modified area to be redisplayed if the cell is visible */
DBReComputeBbox(cdTo);
DBWAreaChanged(cdTo, &RouteArea, DBW_ALLWINDOWS, &DBAllButSpaceBits);
UndoEnable();
return (cdTo);
}
/*
* ----------------------------------------------------------------------------
*
* RtrFindChannelDef --
*
* Return a pointer to the __CHANNEL__ cell def that holds the
* channel structure. Creates this cell if it doesn't exist
*
* Results:
* Pointer to the __CHANNEL__ def.
*
* Side effects:
* May create the __CHANNEL__ def if it doesn't already exist.
* If it creates the def, marks it as CDINTERNAL.
*
* ----------------------------------------------------------------------------
*/
CellDef *
RtrFindChannelDef()
{
CellDef *def;
/* Create our target cell */
if ((def = DBCellLookDef("__CHANNEL__")) == (CellDef *) NULL)
{
def = DBCellNewDef("__CHANNEL__");
DBCellSetAvail(def);
def->cd_flags |= CDINTERNAL;
}
return (def);
}
/*
* ----------------------------------------------------------------------------
*
* rtrSrCells --
*
* Paints a silhouette of the cell tile plane. For each cell, paint
* error paint into the error plane of the CellDef 'def'. Clip any
* paints to the global RouteArea. If the cell is an array, enumerate
* each of its instances separately; this allows connections on interior
* edges of the array.
*
* Results:
* Returns 0 to keep DBCellSrArea from aborting the search.
*
* Side effects:
* Paints into both the DRC check and DRC error planes of the celldef
* given by 'def'. The area of each cell is expanded before painting,
* out to points midway between grid lines. The points are chosen
* so that any routing on grid lines outside the painted area will
* be far enough from the cell not to cause design-rule violations
* (this distance is determined by RtrSubcellSep). In addition, one
* extra grid line is left along side cells to jog terminals over
* to grid points.
*
* ----------------------------------------------------------------------------
*/
int
rtrSrCells(scx, targetDef)
SearchContext *scx; /* The cell to be painted */
CellDef *targetDef; /* The def into which the silhouette is painted */
{
CellDef *def = scx->scx_use->cu_def;
Rect rootBbox, gridBbox;
/*
* Transform the enumerated cell use outlines to get the outline
* of the cell within its parent.
*/
RtrMilestonePrint();
GeoTransRect(&scx->scx_trans, &def->cd_bbox, &rootBbox);
/*
* First, move down the bottom and left boundaries of the cell
* to a safe point midway between grid lines.
*/
gridBbox = rootBbox;
rtrRoundRect(&gridBbox, RtrSubcellSepUp, RtrSubcellSepDown, TRUE);
/* Clip to the routing area and paint into the channel planes */
GeoClip(&gridBbox, &RouteArea);
(void) DBPaintPlane(targetDef->cd_planes[PL_DRC_CHECK], &gridBbox,
DBStdWriteTbl(CELLTILE), (PaintUndoInfo *) NULL);
(void) DBPaintPlane(targetDef->cd_planes[PL_DRC_ERROR], &gridBbox,
DBStdWriteTbl(CELLTILE), (PaintUndoInfo *) NULL);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* rtrRoundRect --
*
* Round a rectangle out to the nearest grid line, and
* extend to a point halfway to the next grid point (if
* doRoundUp is TRUE) or back half a grid from the nearest
* grid line (if doRoundUp is FALSE).
*
* The halfway points are chosen to be RtrGridSpacing/2
* down or to the left from grid lines. Before rounding,
* we add sepUp to the top and right, and sepDown to the
* bottom and left.
*
* Results:
* None.
*
* Side effects:
* Modifies 'r' as indicated above.
*
* ----------------------------------------------------------------------------
*/
void
rtrRoundRect(r, sepUp, sepDown, doRoundUp)
Rect *r;
int sepUp, sepDown;
bool doRoundUp;
{
int halfGrid = RtrGridSpacing / 2;
r->r_xbot = RTR_GRIDDOWN(r->r_xbot - sepDown, RtrOrigin.p_x);
r->r_ybot = RTR_GRIDDOWN(r->r_ybot - sepDown, RtrOrigin.p_y);
if (doRoundUp)
{
r->r_xbot -= halfGrid;
r->r_ybot -= halfGrid;
}
else
{
r->r_xbot += RtrGridSpacing - halfGrid;
r->r_ybot += RtrGridSpacing - halfGrid;
}
/*
* Move up the top and right boundaries. Note: it's important
* that we always SUBTRACT halfgrid from a grid point rather
* than adding sometimes: if RtrGridSpacing is odd, then adding
* and subtracting give different results.
*/
r->r_xtop = RTR_GRIDUP(r->r_xtop + sepUp, RtrOrigin.p_x);
r->r_ytop = RTR_GRIDUP(r->r_ytop + sepUp, RtrOrigin.p_y);
if (doRoundUp)
{
r->r_xtop += RtrGridSpacing - halfGrid;
r->r_ytop += RtrGridSpacing - halfGrid;
}
else
{
r->r_xtop -= halfGrid;
r->r_ytop -= halfGrid;
}
}
/*
* ----------------------------------------------------------------------------
*
* rtrHashKill --
*
* Free the remaining storage in channels in the hash table.
* Kill the table.
*
* Results:
* None.
*
* Side effects:
* Memory gets freed. The global RtrTileToChannel gets cleared.
*
* ----------------------------------------------------------------------------
*/
void
rtrHashKill(ht)
HashTable *ht;
{
HashEntry *he;
HashSearch hs;
HashStartSearch(&hs);
while ((he = HashNext(ht, &hs)))
GCRFreeChannel((GCRChannel *) HashGetValue(he));
HashKill(ht);
}
/*
* ----------------------------------------------------------------------------
*
* rtrSplitToArea --
*
* Clip space tiles to the edges of the (given) routing area.
*
* Results:
* None.
*
* Side effects:
* Changes tiles in the data base.
*
* ----------------------------------------------------------------------------
*/
void
rtrSplitToArea(area, def)
Rect *area; /* Routing area */
CellDef *def; /* Def holding routing results */
{
Tile *tile;
Point p;
/*
* First split top and bottom space tiles, if any.
* Note, there is at most one space tile spanning the top
* of the routing area, due to the horizontal strip property
* plus the earlier clipping of cell tiles to the routing area.
*/
p = area->r_ur;
tile = TiSrPoint((Tile *) NULL, def->cd_planes[PL_DRC_ERROR], &p);
if ((TOP(tile) > area->r_ytop) && (BOTTOM(tile) < area->r_ytop))
(void) TiSplitY(tile, area->r_ytop);
p.p_y = area->r_ll.p_y - 1;
tile = TiSrPoint((Tile *) NULL, def->cd_planes[PL_DRC_ERROR], &p);
if ((BOTTOM(tile) < area->r_ybot) && (TOP(tile) > area->r_ybot))
tile = TiSplitY(tile, area->r_ybot);
/*
* Search up the left edge of the routing area,
* looking for space tiles spanning the edge.
* If found, split them.
*/
p = area->r_ll;
while (p.p_y < area->r_ytop)
{
tile = TiSrPoint(tile, def->cd_planes[PL_DRC_ERROR], &p);
if ((LEFT(tile) < p.p_x) && (RIGHT(tile) > p.p_x))
tile = TiSplitX(tile, p.p_x);
p.p_y = TOP(tile);
}
/* Do the right edge of the routing area in the same manner */
p.p_x = area->r_xtop;
p.p_y = area->r_ybot;
while (p.p_y < area->r_ytop)
{
tile = TiSrPoint(tile, def->cd_planes[PL_DRC_ERROR], &p);
if ((LEFT(tile) < p.p_x) && (RIGHT(tile) > p.p_x))
tile = TiSplitX(tile, p.p_x);
p.p_y = TOP(tile);
}
}
/*
* ----------------------------------------------------------------------------
*
* rtrSrClear --
*
* DBSrPaintArea function for each tile in the error plane of the __CHANNEL__
* def. Sets the flags to 0 in internal space tiles, marking horizontal
* invalid. Mark edges at the boundary of the routing region as valid.
*
* Results:
* Always returns 0.
*
* Side effects:
* Sets flags in tiles.
*
* ----------------------------------------------------------------------------
*/
int
rtrSrClear(tile, dinfo, area)
Tile *tile;
TileType dinfo;
Rect *area;
{
/* Clear all */
rtrCLEAR(tile, -1);
if (TiGetBody(tile) == (ClientData) NULL)
{
/* Mark horizontal edges touching box */
if (TOP(tile) == area->r_ytop)
{
/* Mark top */
rtrMARK(tile, rtrNW);
rtrMARK(tile, rtrNE);
}
if (BOTTOM(tile) == area->r_ytop)
{
/* Mark bottom */
rtrMARK(tile, rtrSW);
rtrMARK(tile, rtrSE);
}
}
else
{
/* Mark all flags in a non-space tile */
/* Mark top */
rtrMARK(tile, rtrNW);
rtrMARK(tile, rtrNE);
/* Mark bottom */
rtrMARK(tile, rtrSW);
rtrMARK(tile, rtrSE);
}
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* rtrSrFunc --
*
* Search function called from DBSrPaintArea for each tile in the
* plane. Do this search in the OLD TILE PLANE. Process corners
* bordering space tiles.
*
* Results:
* Returns a 0 to DBSrPaintArea so it won't abort the search.
*
* Side effects:
* Modifies the result plane to reflect the channel structure.
*
* ----------------------------------------------------------------------------
*/
int
rtrSrFunc(tile, dinfo, plane)
Tile *tile; /* Candidate cell tile */
TileType dinfo; /* Split tile information (unused) */
Plane *plane; /* Plane in which searches take place */
{
Tile *tiles[3];
Point p;
/* Ignore space tiles */
if (TiGetBody(tile) == (ClientData) NULL)
return (0);
/*
* Check each corner of this cell tile to see if it is convex,
* and no marked boundary is incident upon it.
*/
p = tile->ti_ll;
if (rtrUseCorner(&p, rtrSW, plane, tiles))
rtrMarkChannel(plane, tiles, &p, rtrSW);
p.p_y = TOP(tile);
if (rtrUseCorner(&p, rtrNW, plane, tiles))
rtrMarkChannel(plane, tiles, &p, rtrNW);
p.p_x = RIGHT(tile);
if (rtrUseCorner(&p, rtrNE, plane, tiles))
rtrMarkChannel(plane, tiles, &p, rtrNE);
p.p_y = BOTTOM(tile);
if (rtrUseCorner(&p, rtrSE, plane, tiles))
rtrMarkChannel(plane, tiles, &p, rtrSE);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* rtrUseCorner --
*
* Search for legal corners upon which to apply the channel definition
* algorithm. Check both horizontal tiles for markings, since only
* one (the shorter) might be marked.
*
* Results:
* Return FALSE if the corner is not convex or a legal boundary already
* extends from the corner. Otherwise return TRUE.
*
* Side effects:
* Return pointers to space tiles adjacent to the corner.
* tiles[0] is not modified by this routine.
* tiles[1] is the spanning tile above or below the corner.
* tiles[2] is the side tile left or right of the corner.
*
* ----------------------------------------------------------------------------
*/
bool
rtrUseCorner(point, corner, plane, tiles)
Point *point; /* Point at which a cell corner is found */
int corner; /* Selects NE, NW, SE, or SW cell corner */
Plane *plane; /* Plane to be searched for tiles */
Tile *tiles[]; /* Return pointers to found space tiles */
{
Point p0, p1;
Tile * tile;
/* Reject a corner if it lies on the boundary of the routing area */
if (point->p_x <= RouteArea.r_xbot
|| point->p_x >= RouteArea.r_xtop
|| point->p_y <= RouteArea.r_ybot
|| point->p_y >= RouteArea.r_ytop)
{
return (FALSE);
}
/*
* Search the area above (below) the corner. If two space tiles, then a
* vertical boundary marks a channel edge. If one top (bottom) tile and
* one side tile, and the horizontal edge is not marked, then the corner
* is okay.
*/
p1 = p0 = *point;
switch (corner)
{
case rtrNE:
p1.p_y--;
break;
case rtrNW:
p1.p_x--;
p1.p_y--;
break;
case rtrSE:
p0.p_y--;
break;
case rtrSW:
p0.p_y--;
p1.p_x--;
break;
default:
ASSERT(FALSE, "rtrUseCorner corner botch");
break;
}
tile = tiles[1] = TiSrPoint((Tile *) NULL, plane, &p0);
if( (TiGetBody(tile) != (ClientData) NULL) || (LEFT(tile) == point->p_x)
|| (RIGHT(tile) == point->p_x) )
return(FALSE); /* Vertical boundary at corner */
tile = tiles[2] = TiSrPoint((Tile *) NULL, plane, &p1);
if(TiGetBody(tile) != (ClientData) NULL)
return(FALSE); /* Not a corner */
switch(corner)
{
case rtrNE: return(!rtrMARKED(tile, rtrNW)); break;
case rtrNW: return(!rtrMARKED(tile, rtrNE)); break;
case rtrSE: return(!rtrMARKED(tile, rtrSW)); break;
case rtrSW: return(!rtrMARKED(tile, rtrSE)); break;
}
return(FALSE);
}
/*
* ----------------------------------------------------------------------------
*
* rtrMarkChannel --
*
* Find the shortest segment from the corner to another boundary.
* Split and merge space tiles to reflect channel structure. Update
* edge status in the tile plane.
*
* Results:
* None.
*
* Side effects:
* Modifies the result plane to reflect channel definition.
*
* ----------------------------------------------------------------------------
*/
void
rtrMarkChannel(plane, tiles, point, corner)
Plane *plane; /* Plane for searching */
Tile *tiles[]; /* Bordering space tiles */
Point *point; /* Coordinates of corner */
int corner; /* Corner of tile to process */
{
int xDist, yDist, d1, d2, lastY;
Tile *tile, *new;
Point curPt;
bool pos;
pos = ((corner == rtrNE) || (corner == rtrSE));
xDist = rtrXDist(tiles, point->p_x, pos);
yDist = rtrYDist(tiles, point, ((corner==rtrNE) || (corner==rtrNW)), plane);
if (xDist < yDist) /* Choose and mark the horizontal boundary */
{
if(pos)
{
d1 = RIGHT(tiles[1]);
d2 = RIGHT(tiles[2]);
if(corner == rtrNE)
{
rtrMARK(tiles[2], rtrNW);
if(d1 >= d2) rtrMARK(tiles[2], rtrNE);
if(d1 <= d2) rtrMARK(tiles[1], rtrSE);
}
else
{
rtrMARK(tiles[2], rtrSW);
if(d1 >= d2) rtrMARK(tiles[2], rtrSE);
if(d1 <= d2) rtrMARK(tiles[1], rtrNE);
}
}
else
{
d1 = LEFT(tiles[1]);
d2 = LEFT(tiles[2]);
if(corner == rtrNW)
{
rtrMARK(tiles[2], rtrNE);
if(d1 >= d2) rtrMARK(tiles[2], rtrNW);
if(d1 <= d2) rtrMARK(tiles[1], rtrSW);
}
else
{
rtrMARK(tiles[2], rtrSE);
if(d1 >= d2) rtrMARK(tiles[2], rtrSW);
if(d1 <= d2) rtrMARK(tiles[1], rtrNW);
}
}
}
else /* Choose the vertical boundary */
{
Tile *delayed = NULL; /* delayed free to extend lifetime */
/*
* Split a sequence of space tiles starting with tiles[0]
* (the bottom tile), for yDist at the point->p_y.
* Merge tiles where possible.
*/
tile=tiles[0];
curPt.p_x=point->p_x;
curPt.p_y=BOTTOM(tile);
lastY = point->p_y;
if((corner == rtrNW) || (corner == rtrNE)) lastY += yDist;
while(TRUE)
{
ASSERT(TiGetBody(tile) == (ClientData)NULL,
"rtrMerge: merge cell tile");
new = TiSplitX(tile, curPt.p_x);
ASSERT(TiGetBody(new) == (ClientData)NULL, "rtrMerge: merge cell new");
/* Fix horizontal flags in 'new' tile and (old) 'tile' tile */
if (rtrMARKED(tile,rtrNE)) rtrMARK(new,rtrNE);
else rtrCLEAR(new,rtrNE);
if (rtrMARKED(tile,rtrSE)) rtrMARK(new,rtrSE);
else rtrCLEAR(new,rtrNE);
/*
* Clear these flags:
* couldn't cross the boundary unless it was clear.
*/
rtrCLEAR(new, rtrNW);
rtrCLEAR(new, rtrSW);
rtrCLEAR(tile, rtrNE);
rtrCLEAR(tile, rtrSE);
/* Merge tile and new with lower neighbors if possible */
rtrMerge(&delayed, new, LB(new), plane);
rtrMerge(&delayed, tile, LB(tile), plane);
/* Find next (higher) tile to split */
if (TOP(tile) >= lastY) break;
curPt.p_y = TOP(tile);
tile=TiSrPoint(tile, plane, &curPt);
}
/* Merge new and tile with upper neighbors if possible */
rtrMerge(&delayed, RT(new), new, plane);
rtrMerge(&delayed, RT(tile), tile, plane);
TiFreeIf(delayed);
}
}
/*
* ----------------------------------------------------------------------------
*
* rtrYDist --
*
* Finds the distance from a point to an upper or lower channel boundary.
*
* Results:
* The integer distance from the point to the boundary.
*
* Side effects:
* Return a pointer to the bottom tile in the split sequence.
*
* ----------------------------------------------------------------------------
*/
int
rtrYDist(tiles, point, up, plane)
Tile *tiles[]; /* Start tile in [1]. Put bottom tile in [0] */
Point *point; /* Point from which distance is measure */
bool up; /* TRUE if search up, FALSE if down */
Plane *plane; /* Cell plane for search */
{
Tile *current = tiles[1], *next;
int x, yStart, flag;
Point p;
p = *point;
x = p.p_x;
yStart = p.p_y;
for (;;)
{
if (up)
{
p.p_y = TOP(current);
if (p.p_y >= RouteArea.r_ytop) break;
}
else
{
p.p_y = BOTTOM(current);
if (p.p_y <= RouteArea.r_ybot) break;
p.p_y--;
}
/*
* See if we ran into a cell tile. Since the cell tile defines
* the boundary of a channel, terminate the search. If going
* down, reset the y coordinate to the bottom of the last good
* channel.
*/
next = TiSrPoint(current, plane, &p);
if (TiGetBody(next) != (ClientData) NULL)
{
if (!up) p.p_y++;
break;
}
/* Done if a vertical boundary */
if (LEFT(next) == x || RIGHT(next) == x)
break;
/*
* Classify as one of the following cases:
*
* __|_n_|__ |___c___| __|_n__| |__ c|__ |__n|__ __|_c__|
* | c | | n | | c| | n | |c | | n|
* (A) (B) (C) (D) (E) (F)
*/
if (LEFT(current) < LEFT(next))
{
if (RIGHT(current) > RIGHT(next))
{
if (up) flag = rtrMARKED(next, rtrSW); /*(A)*/
else flag = rtrMARKED(next, rtrNW); /*(B)*/
}
else
{
if (up) flag = rtrMARKED(current, rtrNE); /*(C)*/
else flag = rtrMARKED(current, rtrSE); /*(D)*/
}
}
else
{
if (up) flag = rtrMARKED(current, rtrNW); /*(E)*/
else flag = rtrMARKED(current, rtrSW); /*(F)*/
}
if (flag)
{
if (!up) p.p_y = BOTTOM(current);
break;
}
current = next;
}
if (up)
{
tiles[0] = tiles[1];
return (p.p_y - yStart);
}
else
{
tiles[0] = current;
return (yStart - p.p_y);
}
}
/*
* ----------------------------------------------------------------------------
*
* rtrXDist --
*
* Finds the distance from a point to a left or right channel boundary.
*
* Results:
* The integer distance from the point to the boundary.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
rtrXDist(tiles, x, isRight)
Tile *tiles[]; /* Space tiles bordering the corner */
int x; /* Starting x for distance calculation */
bool isRight; /* TRUE if right, FALSE if left */
{
int l0, l1;
if (isRight)
l0 = RIGHT(tiles[1]) - x, l1 = RIGHT(tiles[2]) - x;
else
l0 = x - LEFT(tiles[1]), l1 = x - LEFT(tiles[2]);
return (MIN(l0, l1));
}
/*
* ----------------------------------------------------------------------------
*
* rtrMerge --
*
* Merge two space tiles provided they share a common horizontal edge.
* The upper is the first argument tile.
*
* Results:
* None.
*
* Side effects:
* Updates the horizontal flags in the resulting tile.
*
* ----------------------------------------------------------------------------
*/
void
rtrMerge(Tile **delay1, Tile *tup, Tile *tdn, Plane *plane)
{
Tile *side;
/* Skip if either is a cell tile */
if (TiGetBody(tup) != (ClientData) NULL
|| TiGetBody(tdn) != (ClientData) NULL)
return;
if (LEFT(tdn) != LEFT(tup) || RIGHT(tdn) != RIGHT(tup))
return;
/*
* Set flags for the result.
* Relies on TiJoinY to preserve the first arg as the composite tile.
*/
ASSERT( (BOTTOM(tdn)>=RouteArea.r_ybot) && (TOP(tup)<=RouteArea.r_ytop),
"rtrMerge: merging with a tile outside the routing area");
if (rtrMARKED(tdn, rtrSW)) rtrMARK(tup, rtrSW); else rtrCLEAR(tup, rtrSW);
if (rtrMARKED(tdn, rtrSE)) rtrMARK(tup, rtrSE); else rtrCLEAR(tup, rtrSE);
TiJoinY1(delay1, tup, tdn, plane);
/*
* Merge sideways if the result of the join matches a tile on either side,
* provided the neighbor is a space tile and is inside the routing area.
*/
side = BL(tup);
if (TiGetBody(side) == (ClientData) NULL
&& LEFT(side) >= RouteArea.r_xbot
&& TOP(side) == TOP(tup)
&& BOTTOM(side) == BOTTOM(tup))
TiJoinX1(delay1, tup, side, plane);
side = TR(tup);
if (TiGetBody(side) == (ClientData) NULL
&& RIGHT(side) <= RouteArea.r_xtop
&& TOP(side) == TOP(tup)
&& BOTTOM(side) == BOTTOM(tup))
TiJoinX1(delay1, tup, side, plane);
}