2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* SimDBstuff.c -
|
|
|
|
|
*
|
|
|
|
|
* This file contains routines that extract electrically connected
|
2020-05-23 23:13:14 +02:00
|
|
|
* regions of a layout for Magic. This extractor operates
|
2017-04-25 14:41:48 +02:00
|
|
|
* hierarchically, across cell boundaries (SimTreeCopyConnect), as
|
|
|
|
|
* well as within a single cell (SimSrConnect).
|
|
|
|
|
*
|
|
|
|
|
* This also contains routines corresponding to those in the DBWind
|
|
|
|
|
* module.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * 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. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* University of California
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "utils/geofast.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
2021-03-22 14:49:56 +01:00
|
|
|
#include "utils/stack.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "database/databaseInt.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "utils/signals.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "extract/extractInt.h"
|
|
|
|
|
#include "sim/sim.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "commands/commands.h"
|
|
|
|
|
#include "textio/txcommands.h"
|
|
|
|
|
#include "utils/styles.h"
|
|
|
|
|
#include "graphics/graphics.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char bestName[256];
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SimConnectFunc
|
|
|
|
|
*
|
|
|
|
|
* This procedure is based upon the function dbcConnectFunc in the
|
|
|
|
|
* database module.
|
|
|
|
|
*
|
|
|
|
|
* This procedure is invoked by SimTreeSrTiles from SimTreeCopyConnect,
|
|
|
|
|
* whenever a tile is found that is connected to the current area
|
|
|
|
|
* being processed. If the tile overlaps the search area in a non-
|
|
|
|
|
* trivial way (i.e. more than a 1x1 square of overlap at a corner)
|
|
|
|
|
* then the area of the tile is added onto the list of things to check.
|
|
|
|
|
* The "non-trivial" overlap check is needed to prevent caddy-corner
|
|
|
|
|
* tiles from being considered as connected.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 normally, 1 if an abort condition has been encountered.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Paints into the destination definition.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 20:41:36 +01:00
|
|
|
SimConnectFunc(
|
|
|
|
|
Tile *tile, /* Tile found. */
|
|
|
|
|
TreeContext *cx) /* Describes context of search. The client
|
2017-04-25 14:41:48 +02:00
|
|
|
* data is a pointer to the list head of
|
|
|
|
|
* the conSrArg2's describing the areas
|
|
|
|
|
* left to check.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
struct conSrArg2 *csa2;
|
|
|
|
|
Rect tileArea, *srArea, newarea;
|
|
|
|
|
SearchContext *scx = cx->tc_scx;
|
2025-01-31 20:43:08 +01:00
|
|
|
TileTypeBitMask notConnectMask;
|
|
|
|
|
const TileTypeBitMask *connectMask;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType loctype, ctype;
|
|
|
|
|
TileType dinfo = 0;
|
|
|
|
|
int i, pNum;
|
|
|
|
|
static char nodeName[256];
|
|
|
|
|
CellDef *def;
|
|
|
|
|
TerminalPath *tpath = cx->tc_filter->tf_tpath;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &tileArea);
|
|
|
|
|
srArea = &scx->scx_area;
|
|
|
|
|
|
|
|
|
|
if (((tileArea.r_xbot >= srArea->r_xtop-1) ||
|
|
|
|
|
(tileArea.r_xtop <= srArea->r_xbot+1)) &&
|
|
|
|
|
((tileArea.r_ybot >= srArea->r_ytop-1) ||
|
|
|
|
|
(tileArea.r_ytop <= srArea->r_ybot+1)))
|
|
|
|
|
{
|
|
|
|
|
/* If the search area is only one unit wide or tall, then it's
|
|
|
|
|
* OK to have only a small overlap. This happens only when
|
|
|
|
|
* looking for an initial search tile.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (((srArea->r_xtop-1) != srArea->r_xbot)
|
|
|
|
|
&& ((srArea->r_ytop-1) != srArea->r_ybot)) return 0;
|
|
|
|
|
}
|
|
|
|
|
GeoTransRect(&scx->scx_trans, &tileArea, &newarea);
|
|
|
|
|
|
|
|
|
|
/* Clip the current area down to something that overlaps the
|
|
|
|
|
* area of interest.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
csa2 = (struct conSrArg2 *)cx->tc_filter->tf_arg;
|
|
|
|
|
GeoClip(&newarea, csa2->csa2_bounds);
|
|
|
|
|
if (GEO_RECTNULL(&newarea)) return 0;
|
|
|
|
|
|
|
|
|
|
/* Stuff unique to the nodename search follows. */
|
|
|
|
|
|
|
|
|
|
if (tpath != (TerminalPath *)NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Extract the node name */
|
|
|
|
|
|
|
|
|
|
char *n = tpath->tp_next;
|
|
|
|
|
char c = *n;
|
|
|
|
|
|
|
|
|
|
SigDisableInterrupts();
|
2020-05-23 23:13:14 +02:00
|
|
|
strcpy(nodeName, SimGetNodeName(cx->tc_scx, tile, tpath->tp_first));
|
2017-04-25 14:41:48 +02:00
|
|
|
SigEnableInterrupts();
|
|
|
|
|
|
|
|
|
|
*n = c;
|
|
|
|
|
|
|
|
|
|
/* save the "best" name for this node */
|
|
|
|
|
|
|
|
|
|
if (bestName[0] == '\0' || efPreferredName(nodeName, bestName))
|
|
|
|
|
strcpy(bestName, nodeName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loctype = TiGetTypeExact(tile);
|
|
|
|
|
|
|
|
|
|
/* Resolve geometric transformations on diagonally-split tiles */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
dinfo = DBTransformDiagonal(loctype, &scx->scx_trans);
|
|
|
|
|
loctype = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* See if the destination cell contains stuff over the whole
|
|
|
|
|
* current area (on its home plane) that is connected to it.
|
|
|
|
|
* If so, then there's no need to process the current area,
|
|
|
|
|
* since any processing that is needed was already done before.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
pNum = DBPlane(loctype);
|
|
|
|
|
connectMask = &csa2->csa2_connect[loctype];
|
|
|
|
|
|
|
|
|
|
if (DBIsContact(loctype))
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask *rMask = DBResidueMask(loctype);
|
|
|
|
|
TileTypeBitMask *cMask;
|
|
|
|
|
|
|
|
|
|
TTMaskSetOnlyType(¬ConnectMask, loctype);
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Differenct contact types may share residues (6/18/04) */
|
2017-04-25 14:41:48 +02:00
|
|
|
for (ctype = TT_TECHDEPBASE; ctype < DBNumUserLayers; ctype++)
|
|
|
|
|
{
|
|
|
|
|
if (DBIsContact(ctype))
|
|
|
|
|
{
|
|
|
|
|
cMask = DBResidueMask(ctype);
|
|
|
|
|
if (TTMaskIntersect(rMask, cMask))
|
|
|
|
|
TTMaskSetType(¬ConnectMask, ctype);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The mask of contact types must include all stacked contacts */
|
2020-05-23 23:13:14 +02:00
|
|
|
for (ctype = DBNumUserLayers; ctype < DBNumTypes; ctype++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
cMask = DBResidueMask(ctype);
|
|
|
|
|
if TTMaskHasType(cMask, loctype)
|
|
|
|
|
TTMaskSetType(¬ConnectMask, ctype);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TTMaskCom(¬ConnectMask);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TTMaskCom2(¬ConnectMask, connectMask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def = csa2->csa2_use->cu_def;
|
|
|
|
|
if (DBSrPaintNMArea((Tile *) NULL, def->cd_planes[pNum],
|
|
|
|
|
dinfo, &newarea, ¬ConnectMask, dbcUnconnectFunc,
|
|
|
|
|
(ClientData) connectMask) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Paint this tile into the destination cell. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
DBNMPaintPlane(def->cd_planes[pNum], dinfo, &newarea,
|
|
|
|
|
DBStdPaintTbl(loctype, pNum), (PaintUndoInfo *) NULL);
|
|
|
|
|
|
|
|
|
|
/* Since the whole area of this tile hasn't been recorded,
|
|
|
|
|
* we must process its area to find any other tiles that
|
|
|
|
|
* connect to it. Add each of them to the list of things
|
|
|
|
|
* to process. We have to expand the search area by 1 unit
|
|
|
|
|
* on all sides because SimTreeSrTiles only returns things
|
|
|
|
|
* that overlap the search area, and we want things that
|
|
|
|
|
* even just touch.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Only extend those sides bordering the diagonal tile */
|
|
|
|
|
|
|
|
|
|
if (dinfo & TT_DIAGONAL)
|
|
|
|
|
{
|
|
|
|
|
if (dinfo & TT_SIDE) /* right */
|
|
|
|
|
newarea.r_xtop += 1;
|
|
|
|
|
else /* left */
|
|
|
|
|
newarea.r_xbot -= 1;
|
|
|
|
|
if (((dinfo & TT_SIDE) >> 1)
|
|
|
|
|
== (dinfo & TT_DIRECTION)) /* top */
|
|
|
|
|
newarea.r_ytop += 1;
|
|
|
|
|
else /* bottom */
|
|
|
|
|
newarea.r_ybot -= 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newarea.r_xbot -= 1;
|
|
|
|
|
newarea.r_ybot -= 1;
|
|
|
|
|
newarea.r_xtop += 1;
|
|
|
|
|
newarea.r_ytop += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/* Abort the name search if the name is in the abort name search table
|
2017-04-25 14:41:48 +02:00
|
|
|
* or if the name is global and the SimIgnoreGlobals flag is not set.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (SimSawAbortString || SigInterruptPending)
|
|
|
|
|
return 1;
|
|
|
|
|
else if (SimIsGetnode && !SimIgnoreGlobals)
|
|
|
|
|
{
|
|
|
|
|
i = strlen(nodeName);
|
|
|
|
|
if (nodeName[i - 1] == '!')
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-22 14:49:56 +01:00
|
|
|
/* Check if any of the last 5 entries has the same type and */
|
|
|
|
|
/* area. If so, don't duplicate the existing entry. */
|
|
|
|
|
/* (NOTE: Connect masks are all from the same table, so */
|
|
|
|
|
/* they can be compared by address, no need for TTMaskEqual)*/
|
|
|
|
|
|
|
|
|
|
for (i = csa2->csa2_lasttop; (i >= 0) && (i > csa2->csa2_lasttop - 5); i--)
|
|
|
|
|
if (connectMask == csa2->csa2_list[i].connectMask)
|
|
|
|
|
if (GEO_SURROUND(&csa2->csa2_list[i].area, &newarea))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Register the area and connection mask as needing to be processed */
|
|
|
|
|
|
2021-03-22 14:49:56 +01:00
|
|
|
if (++csa2->csa2_top == CSA2_LIST_SIZE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* Reached list size limit---need to enlarge the list */
|
|
|
|
|
/* Double the size of the list every time we hit the limit */
|
|
|
|
|
|
|
|
|
|
conSrArea *newlist;
|
|
|
|
|
|
2021-03-22 14:49:56 +01:00
|
|
|
newlist = (conSrArea *)mallocMagic(CSA2_LIST_SIZE * sizeof(conSrArea));
|
|
|
|
|
StackPush((ClientData)csa2->csa2_list, csa2->csa2_stack);
|
2017-04-25 14:41:48 +02:00
|
|
|
csa2->csa2_list = newlist;
|
2021-03-22 14:49:56 +01:00
|
|
|
csa2->csa2_top = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
csa2->csa2_list[csa2->csa2_top].area = newarea;
|
|
|
|
|
csa2->csa2_list[csa2->csa2_top].connectMask = connectMask;
|
|
|
|
|
csa2->csa2_list[csa2->csa2_top].dinfo = dinfo;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SimTreeCopyConnect
|
|
|
|
|
*
|
|
|
|
|
* This procedure is very similar to DBTreeCopyConnect.
|
|
|
|
|
*
|
|
|
|
|
* This procedure copies connected information from a given cell
|
|
|
|
|
* hierarchy to a given (flat) cell. Starting from the tile underneath
|
|
|
|
|
* the given area, this procedure finds all paint in all cells
|
|
|
|
|
* that is connected to that information. All such paint is
|
|
|
|
|
* copied into the result cell. If there are several electrically
|
|
|
|
|
* distinct nets underneath the given area, one of them is picked
|
|
|
|
|
* at more-or-less random.
|
|
|
|
|
*
|
|
|
|
|
* Modified so the result cell is NOT first cleared of all paint. This
|
|
|
|
|
* allows multiple calls, to highlight incomplete routing nets.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The contents of the result cell are modified.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define MAXPATHNAME 256
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 20:41:36 +01:00
|
|
|
SimTreeCopyConnect(
|
|
|
|
|
SearchContext *scx, /* Describes starting area. The
|
2017-04-25 14:41:48 +02:00
|
|
|
* scx_use field gives the root of
|
|
|
|
|
* the hierarchy to search, and the
|
|
|
|
|
* scx_area field gives the starting
|
|
|
|
|
* area. An initial tile must overlap
|
|
|
|
|
* this area. The transform is from
|
|
|
|
|
* coords of scx_use to destUse.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *mask, /* Tile types to start from in area. */
|
2025-01-31 20:41:36 +01:00
|
|
|
int xMask, /* Information must be expanded in all
|
2017-04-25 14:41:48 +02:00
|
|
|
* of the windows indicated by this
|
|
|
|
|
* mask. Use 0 to consider all info
|
|
|
|
|
* regardless of expansion.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *connect, /* Points to table that defines what
|
2017-04-25 14:41:48 +02:00
|
|
|
* each tile type is considered to
|
|
|
|
|
* connect to. Use DBConnectTbl as
|
|
|
|
|
* a default.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const Rect *area, /* The resulting information is
|
2017-04-25 14:41:48 +02:00
|
|
|
* clipped to this area. Pass
|
|
|
|
|
* TiPlaneRect to get everything.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
CellUse *destUse, /* Result use in which to place
|
2017-04-25 14:41:48 +02:00
|
|
|
* anything connected to material of
|
|
|
|
|
* type mask in area of rootUse.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
char *Node_Name) /* Name of node returned.
|
2017-04-25 14:41:48 +02:00
|
|
|
* NOTE: Don't call this "NodeName",
|
|
|
|
|
* because that conflicts with reserved
|
|
|
|
|
* words in some compilers.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
TerminalPath tpath;
|
|
|
|
|
char pathName[MAXPATHNAME];
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *newmask;
|
2017-04-25 14:41:48 +02:00
|
|
|
struct conSrArg2 csa2;
|
|
|
|
|
TileType newtype;
|
|
|
|
|
|
|
|
|
|
csa2.csa2_use = destUse;
|
|
|
|
|
csa2.csa2_bounds = area;
|
|
|
|
|
csa2.csa2_connect = connect;
|
|
|
|
|
|
2021-03-22 14:49:56 +01:00
|
|
|
csa2.csa2_list = (conSrArea *)mallocMagic(CSA2_LIST_SIZE * sizeof(conSrArea));
|
2017-04-25 14:41:48 +02:00
|
|
|
csa2.csa2_top = -1;
|
2021-03-22 14:49:56 +01:00
|
|
|
csa2.csa2_lasttop = -1;
|
|
|
|
|
|
|
|
|
|
csa2.csa2_stack = StackNew(100);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
tpath.tp_first = tpath.tp_next = pathName;
|
2020-05-23 23:13:14 +02:00
|
|
|
tpath.tp_last = pathName + MAXPATHNAME;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
pathName[0] = '\0';
|
|
|
|
|
bestName[0] = '\0';
|
|
|
|
|
|
|
|
|
|
(void) SimTreeSrTiles(scx, mask, xMask, &tpath, SimConnectFunc,
|
|
|
|
|
(ClientData) &csa2);
|
|
|
|
|
while (csa2.csa2_top >= 0)
|
|
|
|
|
{
|
|
|
|
|
newmask = csa2.csa2_list[csa2.csa2_top].connectMask;
|
|
|
|
|
scx->scx_area = csa2.csa2_list[csa2.csa2_top].area;
|
|
|
|
|
newtype = csa2.csa2_list[csa2.csa2_top].dinfo;
|
2021-03-22 14:49:56 +01:00
|
|
|
if (csa2.csa2_top == 0)
|
|
|
|
|
{
|
2021-03-23 16:14:44 +01:00
|
|
|
if (StackLook(csa2.csa2_stack) != (ClientData)NULL)
|
2021-03-22 14:49:56 +01:00
|
|
|
{
|
|
|
|
|
freeMagic(csa2.csa2_list);
|
|
|
|
|
csa2.csa2_list = (conSrArea *)StackPop(csa2.csa2_stack);
|
|
|
|
|
csa2.csa2_top = CSA2_LIST_SIZE - 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
csa2.csa2_top--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
csa2.csa2_top--;
|
|
|
|
|
|
|
|
|
|
csa2.csa2_lasttop = csa2.csa2_top;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (newtype & TT_DIAGONAL)
|
|
|
|
|
SimTreeSrNMTiles(scx, newtype, newmask, xMask, &tpath,
|
|
|
|
|
SimConnectFunc, (ClientData) &csa2);
|
|
|
|
|
else
|
|
|
|
|
SimTreeSrTiles(scx, newmask, xMask, &tpath, SimConnectFunc,
|
|
|
|
|
(ClientData) &csa2);
|
|
|
|
|
}
|
|
|
|
|
freeMagic((char *)csa2.csa2_list);
|
2021-03-22 14:49:56 +01:00
|
|
|
StackFree(csa2.csa2_stack);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Recompute the bounding box of the destination and record
|
|
|
|
|
* its area for redisplay.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
strcpy(Node_Name, bestName);
|
|
|
|
|
DBReComputeBbox(destUse->cu_def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efPreferredName --
|
|
|
|
|
*
|
|
|
|
|
* This is the same function used in the ext2sim module. We need this
|
|
|
|
|
* function for the rsim interface to Magic.
|
|
|
|
|
*
|
|
|
|
|
* Determine which of two names is more preferred. The most preferred
|
|
|
|
|
* name is a global name. Given two non-global names, the one with the
|
|
|
|
|
* fewest pathname components is the most preferred. If the two names
|
|
|
|
|
* have equally many pathname components, we choose the shortest.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* TRUE if 'name1' is preferable to 'name2', FALSE if not.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
2025-01-31 20:41:36 +01:00
|
|
|
efPreferredName(
|
2025-01-31 20:43:08 +01:00
|
|
|
const char *name1,
|
|
|
|
|
const char *name2)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int nslashes1, nslashes2;
|
2025-01-31 20:43:08 +01:00
|
|
|
const char *np1, *np2;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if( name1[0] == '@' && name1[1] == '=' )
|
|
|
|
|
return( TRUE );
|
|
|
|
|
else if( name2[0] == '@' && name2[1] == '=' )
|
|
|
|
|
return( FALSE );
|
|
|
|
|
|
|
|
|
|
for (nslashes1 = 0, np1 = name1; *np1; ) {
|
|
|
|
|
if (*np1++ == '/')
|
|
|
|
|
nslashes1++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (nslashes2 = 0, np2 = name2; *np2; ) {
|
|
|
|
|
if (*np2++ == '/')
|
|
|
|
|
nslashes2++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
--np1;
|
|
|
|
|
--np2;
|
|
|
|
|
|
|
|
|
|
if (!SimIgnoreGlobals)
|
|
|
|
|
{
|
|
|
|
|
/* both are global names */
|
|
|
|
|
if ((*np1 == '!') && (*np2 == '!')) {
|
|
|
|
|
/* check # of pathname components */
|
|
|
|
|
if (nslashes1 < nslashes2) return (TRUE);
|
|
|
|
|
if (nslashes1 > nslashes2) return (FALSE);
|
|
|
|
|
|
|
|
|
|
/* same # of pathname components; check length */
|
|
|
|
|
if (np1 - name1 < np2 - name2) return (TRUE);
|
|
|
|
|
if (np1 - name1 > np2 - name2) return (FALSE);
|
|
|
|
|
|
|
|
|
|
/* same # of pathname components; same length; use lex order */
|
2020-05-23 23:13:14 +02:00
|
|
|
if (strcmp(name1, name2) > 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
return(TRUE);
|
|
|
|
|
else
|
|
|
|
|
return(FALSE);
|
|
|
|
|
}
|
|
|
|
|
if (*np1 == '!') return(TRUE);
|
|
|
|
|
if (*np2 == '!') return(FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* neither name is global */
|
|
|
|
|
/* chose label over generated name */
|
|
|
|
|
if (*np1 != '#' && *np2 == '#') return (TRUE);
|
|
|
|
|
if (*np1 == '#' && *np2 != '#') return (FALSE);
|
|
|
|
|
|
|
|
|
|
/* either both are labels or generated names */
|
|
|
|
|
/* check pathname components */
|
|
|
|
|
if (nslashes1 < nslashes2) return (TRUE);
|
|
|
|
|
if (nslashes1 > nslashes2) return (FALSE);
|
|
|
|
|
|
|
|
|
|
/* same # of pathname components; check length */
|
|
|
|
|
if (np1 - name1 < np2 - name2) return (TRUE);
|
|
|
|
|
if (np1 - name1 > np2 - name2) return (FALSE);
|
|
|
|
|
|
|
|
|
|
/* same # of pathname components; same length; use lex ordering */
|
2020-05-23 23:13:14 +02:00
|
|
|
if (strcmp(name1, name2) > 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
return(TRUE);
|
|
|
|
|
else
|
|
|
|
|
return(FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SimSrConnect
|
|
|
|
|
*
|
|
|
|
|
* This is similar to the procedure DBSrConnect, except that the
|
|
|
|
|
* marks on each tile in the cell are not erased.
|
|
|
|
|
*
|
|
|
|
|
* Search through a cell to find all paint that is electrically
|
|
|
|
|
* connected to things in a given starting area.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* 0 is returned if the search finished normally. 1 is returned
|
|
|
|
|
* if the search was aborted.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The search starts from one (random) non-space tile in "startArea"
|
|
|
|
|
* that matches the types in the mask parameter. For every paint
|
|
|
|
|
* tile that is electrically connected to the initial tile and that
|
|
|
|
|
* intersects the rectangle "bounds", func is called. Func should
|
|
|
|
|
* have the following form:
|
|
|
|
|
*
|
|
|
|
|
* int
|
|
|
|
|
* func(tile, clientData)
|
|
|
|
|
* Tile *tile;
|
|
|
|
|
* ClientData clientData;
|
|
|
|
|
* {
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* The clientData passed to func is the same one that was passed
|
|
|
|
|
* to us. Func returns 0 under normal conditions; if it returns
|
|
|
|
|
* 1 then the search is aborted.
|
|
|
|
|
*
|
|
|
|
|
* *** WARNING ***
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Func should not modify any paint during the search, since this
|
|
|
|
|
* will mess up pointers kept by these procedures and likely cause
|
2020-05-23 23:13:14 +02:00
|
|
|
* a core-dump.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 20:41:36 +01:00
|
|
|
SimSrConnect(
|
|
|
|
|
CellDef *def, /* Cell definition in which to carry out
|
2017-04-25 14:41:48 +02:00
|
|
|
* the connectivity search. Only paint
|
|
|
|
|
* in this definition is considered.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const Rect *startArea, /* Area to search for an initial tile. Only
|
2017-04-25 14:41:48 +02:00
|
|
|
* tiles OVERLAPPING the area are considered.
|
|
|
|
|
* This area should have positive x and y
|
|
|
|
|
* dimensions.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *mask,/* Only tiles of one of these types are used
|
2017-04-25 14:41:48 +02:00
|
|
|
* as initial tiles.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *connect,
|
|
|
|
|
/* Pointer to a table indicating what tile
|
2017-04-25 14:41:48 +02:00
|
|
|
* types connect to what other tile types.
|
|
|
|
|
* Each entry gives a mask of types that
|
|
|
|
|
* connect to tiles of a given type.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const Rect *bounds, /* Area, in coords of scx->scx_use->cu_def,
|
2017-04-25 14:41:48 +02:00
|
|
|
* that limits the search: only tiles
|
|
|
|
|
* overalapping this area will be returned.
|
|
|
|
|
* Use TiPlaneRect to search everywhere.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
int (*func)(), /* Function to apply at each connected tile. */
|
|
|
|
|
ClientData clientData) /* Client data for above function. */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
struct conSrArg csa;
|
|
|
|
|
int startPlane, result;
|
|
|
|
|
Tile *startTile; /* Starting tile for search. */
|
|
|
|
|
|
|
|
|
|
result = 0;
|
|
|
|
|
csa.csa_def = def;
|
|
|
|
|
csa.csa_bounds = *bounds;
|
|
|
|
|
|
|
|
|
|
/* Find a starting tile (if there are many tiles underneath the
|
|
|
|
|
* starting area, pick any one). The search function just saves
|
|
|
|
|
* the tile address and returns.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
startTile = NULL;
|
|
|
|
|
for (startPlane = PL_TECHDEPBASE; startPlane < DBNumPlanes; startPlane++)
|
|
|
|
|
{
|
|
|
|
|
if (DBSrPaintArea((Tile *) NULL,
|
|
|
|
|
def->cd_planes[startPlane], startArea, mask,
|
2025-01-31 21:07:55 +01:00
|
|
|
dbSrConnectStartFunc, PTR2CD(&startTile)) != 0) break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
if (startTile == NULL) return 0;
|
|
|
|
|
|
|
|
|
|
/* Pass 1. During this pass the client function gets called. */
|
|
|
|
|
|
|
|
|
|
csa.csa_clientFunc = func;
|
|
|
|
|
csa.csa_clientData = clientData;
|
|
|
|
|
csa.csa_clear = FALSE;
|
|
|
|
|
csa.csa_connect = connect;
|
2021-04-21 19:03:26 +02:00
|
|
|
csa.csa_pNum = startPlane;
|
2025-01-31 21:07:55 +01:00
|
|
|
if (dbSrConnectFunc(startTile, PTR2CD(&csa)) != 0) result = 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SimTreeSrTiles
|
|
|
|
|
*
|
|
|
|
|
* Similar to the procedure DBTreeSrTiles, although having a terminal
|
|
|
|
|
* path similar to procedure DBTreeSrLabels.
|
|
|
|
|
*
|
|
|
|
|
* Recursively search downward from the supplied CellUse for
|
|
|
|
|
* all visible paint tiles matching the supplied type mask.
|
|
|
|
|
*
|
|
|
|
|
* The procedure should be of the following form:
|
|
|
|
|
* int
|
|
|
|
|
* func(tile, cxp)
|
|
|
|
|
* Tile *tile;
|
|
|
|
|
* TreeContext *cxp;
|
|
|
|
|
* {
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* The SearchContext is stored in cxp->tc_scx, the user's arg is stored
|
|
|
|
|
* in cxp->tc_filter->tf_arg, and the terminal path is stored in
|
|
|
|
|
* cxp->tc_filter->tf_tpath.
|
|
|
|
|
*
|
|
|
|
|
* In the above, the scx transform is the net transform from the coordinates
|
|
|
|
|
* of tile to "world" coordinates (or whatever coordinates the initial
|
|
|
|
|
* transform supplied to SimTreeSrTiles was a transform to). Func returns
|
|
|
|
|
* 0 under normal conditions. If 1 is returned, it is a request to
|
|
|
|
|
* abort the search.
|
|
|
|
|
*
|
|
|
|
|
* *** WARNING ***
|
|
|
|
|
*
|
|
|
|
|
* The client procedure should not modify any of the paint planes in
|
|
|
|
|
* the cells visited by SimTreeSrTiles, because we use DBSrPaintArea
|
2019-03-23 00:58:47 +01:00
|
|
|
* as our paint-tile enumeration function.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* 0 is returned if the search finished normally. 1 is returned
|
|
|
|
|
* if the search was aborted.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Whatever side effects are brought about by applying the
|
|
|
|
|
* procedure supplied.
|
|
|
|
|
*
|
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 20:41:36 +01:00
|
|
|
SimTreeSrTiles(
|
|
|
|
|
SearchContext *scx, /* Pointer to search context specifying
|
2017-04-25 14:41:48 +02:00
|
|
|
* a cell use to search, an area in the
|
|
|
|
|
* coordinates of the cell's def, and a
|
|
|
|
|
* transform back to "root" coordinates.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *mask,/* Only tiles with a type for which
|
2017-04-25 14:41:48 +02:00
|
|
|
* a bit in this mask is on are processed.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
int xMask, /* All subcells are visited recursively
|
2017-04-25 14:41:48 +02:00
|
|
|
* until we encounter uses whose flags,
|
|
|
|
|
* when anded with xMask, are not
|
|
|
|
|
* equal to xMask.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
TerminalPath *tpath, /* Pointer to a structure describing a
|
2017-04-25 14:41:48 +02:00
|
|
|
* partially filled-in terminal pathname.
|
|
|
|
|
* Add new components as encountered.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
int (*func)(), /* Function to apply at each qualifying tile */
|
|
|
|
|
ClientData cdarg) /* Client data for above function */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-01-31 21:10:51 +01:00
|
|
|
int SimCellTileSrFunc(SearchContext *scx, ClientData cdata); /* cb_database_cellsrarea_t (TreeFilter *fp) */
|
2017-04-25 14:41:48 +02:00
|
|
|
TreeFilter filter;
|
|
|
|
|
|
|
|
|
|
filter.tf_func = func;
|
|
|
|
|
filter.tf_arg = cdarg;
|
|
|
|
|
filter.tf_mask = mask;
|
|
|
|
|
filter.tf_xmask = xMask;
|
|
|
|
|
filter.tf_planes = DBTechTypesToPlanes(mask);
|
|
|
|
|
filter.tf_tpath = tpath;
|
|
|
|
|
filter.tf_dinfo = 0;
|
|
|
|
|
|
2025-01-31 21:10:51 +01:00
|
|
|
return SimCellTileSrFunc(scx, PTR2CD(&filter));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* SimTreeSrNMTiles ---
|
|
|
|
|
* This is a variant of the above in which the search is over
|
|
|
|
|
* a non-Manhattan triangular area.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 20:41:36 +01:00
|
|
|
SimTreeSrNMTiles(
|
|
|
|
|
SearchContext *scx, /* Pointer to search context specifying
|
2017-04-25 14:41:48 +02:00
|
|
|
* a cell use to search, an area in the
|
|
|
|
|
* coordinates of the cell's def, and a
|
|
|
|
|
* transform back to "root" coordinates.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
TileType dinfo, /* Type containing information about the
|
2017-04-25 14:41:48 +02:00
|
|
|
* triangular area to search.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const TileTypeBitMask *mask,/* Only tiles with a type for which
|
2017-04-25 14:41:48 +02:00
|
|
|
* a bit in this mask is on are processed.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
int xMask, /* All subcells are visited recursively
|
2017-04-25 14:41:48 +02:00
|
|
|
* until we encounter uses whose flags,
|
|
|
|
|
* when anded with xMask, are not
|
|
|
|
|
* equal to xMask.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
TerminalPath *tpath, /* Pointer to a structure describing a
|
2017-04-25 14:41:48 +02:00
|
|
|
* partially filled-in terminal pathname.
|
|
|
|
|
* Add new components as encountered.
|
|
|
|
|
*/
|
2025-01-31 20:41:36 +01:00
|
|
|
int (*func)(), /* Function to apply at each qualifying tile */
|
|
|
|
|
ClientData cdarg) /* Client data for above function */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-01-31 21:10:51 +01:00
|
|
|
int SimCellTileSrFunc(SearchContext *scx, ClientData cdata); /* cb_database_cellsrarea_t (TreeFilter *fp) */
|
2017-04-25 14:41:48 +02:00
|
|
|
TreeFilter filter;
|
|
|
|
|
|
|
|
|
|
filter.tf_func = func;
|
|
|
|
|
filter.tf_arg = cdarg;
|
|
|
|
|
filter.tf_mask = mask;
|
|
|
|
|
filter.tf_xmask = xMask;
|
|
|
|
|
filter.tf_dinfo = dinfo;
|
|
|
|
|
filter.tf_planes = DBTechTypesToPlanes(mask);
|
|
|
|
|
filter.tf_tpath = tpath;
|
|
|
|
|
|
2025-01-31 21:10:51 +01:00
|
|
|
return SimCellTileSrFunc(scx, PTR2CD(&filter));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Filter procedure applied to subcells by SimTreeSrTiles().
|
|
|
|
|
*/
|
|
|
|
|
|
2025-01-31 21:10:51 +01:00
|
|
|
/** @typedef cb_database_cellsrarea_t */
|
2017-04-25 14:41:48 +02:00
|
|
|
int
|
2025-01-31 20:41:36 +01:00
|
|
|
SimCellTileSrFunc(
|
|
|
|
|
SearchContext *scx,
|
2025-01-31 21:10:51 +01:00
|
|
|
ClientData cdata) /* (TreeFilter *fp) */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-01-31 21:10:51 +01:00
|
|
|
TreeFilter *fp = (TreeFilter *)CD2PTR(cdata);
|
2017-04-25 14:41:48 +02:00
|
|
|
TreeContext context;
|
|
|
|
|
TerminalPath *tp;
|
|
|
|
|
CellDef *def = scx->scx_use->cu_def;
|
|
|
|
|
int pNum, result;
|
|
|
|
|
char *tnext;
|
|
|
|
|
|
|
|
|
|
ASSERT(def != (CellDef *) NULL, "SimCellTileSrFunc");
|
|
|
|
|
if (!DBDescendSubcell(scx->scx_use, fp->tf_xmask))
|
|
|
|
|
return 0;
|
|
|
|
|
if ((def->cd_flags & CDAVAILABLE) == 0)
|
2023-04-18 17:01:58 +02:00
|
|
|
if (!DBCellRead(def, TRUE, TRUE, NULL))
|
|
|
|
|
return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
context.tc_scx = scx;
|
|
|
|
|
context.tc_filter = fp;
|
|
|
|
|
|
|
|
|
|
/* Create the path prefix */
|
|
|
|
|
/* Don't prepend the "Topmost cell" ID of the top-level cell. */
|
|
|
|
|
|
|
|
|
|
if ((fp->tf_tpath != (TerminalPath *)NULL)
|
|
|
|
|
&& (scx->scx_use->cu_parent != NULL))
|
|
|
|
|
{
|
|
|
|
|
tp = fp->tf_tpath;
|
|
|
|
|
tnext = tp->tp_next;
|
|
|
|
|
tp->tp_next = DBPrintUseId(scx, tp->tp_next, tp->tp_last -
|
|
|
|
|
tp->tp_next, FALSE);
|
|
|
|
|
if (tp->tp_next < tp->tp_last)
|
|
|
|
|
{
|
|
|
|
|
*(tp->tp_next++) = '/';
|
|
|
|
|
*(tp->tp_next) = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Apply the function first to any of the tiles in the planes
|
|
|
|
|
* for this CellUse's CellDef that match the mask.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
result = 0;
|
|
|
|
|
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
if (PlaneMaskHasPlane(fp->tf_planes, pNum))
|
|
|
|
|
{
|
|
|
|
|
if (fp->tf_dinfo & TT_DIAGONAL)
|
|
|
|
|
{
|
|
|
|
|
TileType dinfo = DBTransformDiagonal(fp->tf_dinfo, &scx->scx_trans);
|
|
|
|
|
if (DBSrPaintNMArea((Tile *) NULL, def->cd_planes[pNum],
|
|
|
|
|
dinfo, &scx->scx_area, fp->tf_mask,
|
|
|
|
|
fp->tf_func, (ClientData) &context))
|
|
|
|
|
{
|
|
|
|
|
result = 1;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (DBSrPaintArea((Tile *) NULL, def->cd_planes[pNum],
|
|
|
|
|
&scx->scx_area, fp->tf_mask,
|
|
|
|
|
fp->tf_func, (ClientData) &context))
|
|
|
|
|
{
|
|
|
|
|
result = 1;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now apply ourselves recursively to each of the CellUses
|
|
|
|
|
* in our tile plane.
|
|
|
|
|
*/
|
|
|
|
|
|
2025-01-31 21:10:51 +01:00
|
|
|
if (DBCellSrArea(scx, SimCellTileSrFunc, PTR2CD(fp)))
|
2017-04-25 14:41:48 +02:00
|
|
|
result = 1;
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
/* Remove the trailing pathname component from the TerminalPath */
|
|
|
|
|
if ((fp->tf_tpath != (TerminalPath *)NULL)
|
|
|
|
|
&& (scx->scx_use->cu_parent != NULL))
|
|
|
|
|
{
|
|
|
|
|
fp->tf_tpath->tp_next = tnext;
|
|
|
|
|
*tnext = '\0';
|
|
|
|
|
}
|
|
|
|
|
return (result);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SimPutLabel --
|
|
|
|
|
*
|
|
|
|
|
* Same as DBPutLabel, except this does not set the cell modified flag.
|
|
|
|
|
*
|
|
|
|
|
* Place a rectangular label in the database, in a particular cell.
|
|
|
|
|
*
|
|
|
|
|
* It is the responsibility of higher-level routines to insure that
|
|
|
|
|
* the material to which the label is being attached really exists at
|
|
|
|
|
* this point in the cell, and that TT_SPACE is used if there is
|
|
|
|
|
* no single material covering the label's entire area. The routine
|
|
|
|
|
* DBAdjustLabels is useful for this.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* The return value is the actual alignment position used for
|
|
|
|
|
* the label. This may be different from align, if align is
|
|
|
|
|
* defaulted.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Updates the label list in the CellDef to contain the label.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2025-01-31 20:41:36 +01:00
|
|
|
SimPutLabel(
|
|
|
|
|
CellDef *cellDef, /* Cell in which label is placed */
|
2025-01-31 20:43:08 +01:00
|
|
|
const Rect *rect, /* Location of label, see above for description */
|
2025-01-31 20:41:36 +01:00
|
|
|
int align, /* Orientation/alignment of text. If this is < 0,
|
2017-04-25 14:41:48 +02:00
|
|
|
* an orientation will be picked to keep the text
|
|
|
|
|
* inside the cell boundary.
|
|
|
|
|
*/
|
2025-01-31 20:43:08 +01:00
|
|
|
const char *text, /* Pointer to actual text of label */
|
2025-01-31 20:41:36 +01:00
|
|
|
TileType type) /* Type of tile to be labeled */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
Label *lab;
|
|
|
|
|
int len, x1, x2, y1, y2, tmp, labx, laby;
|
|
|
|
|
|
|
|
|
|
len = strlen(text) + sizeof (Label) - sizeof lab->lab_text + 1;
|
|
|
|
|
lab = (Label *) mallocMagic((unsigned) len);
|
|
|
|
|
strcpy(lab->lab_text, text);
|
|
|
|
|
|
|
|
|
|
/* Pick a nice alignment if the caller didn't give one. If the
|
|
|
|
|
* label is more than BORDER units from an edge of the cell,
|
|
|
|
|
* use GEO_NORTH. Otherwise, put the label on the opposite side
|
|
|
|
|
* from the boundary, so it won't stick out past the edge of
|
|
|
|
|
* the cell boundary.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#define BORDER 5
|
|
|
|
|
if (align < 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = (cellDef->cd_bbox.r_xtop - cellDef->cd_bbox.r_xbot)/3;
|
|
|
|
|
if (tmp > BORDER) tmp = BORDER;
|
|
|
|
|
x1 = cellDef->cd_bbox.r_xbot + tmp;
|
|
|
|
|
x2 = cellDef->cd_bbox.r_xtop - tmp;
|
|
|
|
|
tmp = (cellDef->cd_bbox.r_ytop - cellDef->cd_bbox.r_ybot)/3;
|
|
|
|
|
if (tmp > BORDER) tmp = BORDER;
|
|
|
|
|
y1 = cellDef->cd_bbox.r_ybot + tmp;
|
|
|
|
|
y2 = cellDef->cd_bbox.r_ytop - tmp;
|
|
|
|
|
labx = (rect->r_xtop + rect->r_xbot)/2;
|
|
|
|
|
laby = (rect->r_ytop + rect->r_ybot)/2;
|
|
|
|
|
|
|
|
|
|
if (labx <= x1)
|
|
|
|
|
{
|
|
|
|
|
if (laby <= y1) align = GEO_NORTHEAST;
|
|
|
|
|
else if (laby >= y2) align = GEO_SOUTHEAST;
|
|
|
|
|
else align = GEO_EAST;
|
|
|
|
|
}
|
|
|
|
|
else if (labx >= x2)
|
|
|
|
|
{
|
|
|
|
|
if (laby <= y1) align = GEO_NORTHWEST;
|
|
|
|
|
else if (laby >= y2) align = GEO_SOUTHWEST;
|
|
|
|
|
else align = GEO_WEST;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (laby <= y1) align = GEO_NORTH;
|
|
|
|
|
else if (laby >= y2) align = GEO_SOUTH;
|
|
|
|
|
else align = GEO_NORTH;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lab->lab_just = align;
|
|
|
|
|
lab->lab_type = type;
|
|
|
|
|
lab->lab_rect = *rect;
|
|
|
|
|
lab->lab_next = NULL;
|
|
|
|
|
lab->lab_flags = 0;
|
|
|
|
|
if (cellDef->cd_labels == NULL)
|
|
|
|
|
cellDef->cd_labels = lab;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ASSERT(cellDef->cd_lastLabel->lab_next == NULL, "SimPutLabel");
|
|
|
|
|
cellDef->cd_lastLabel->lab_next = lab;
|
|
|
|
|
}
|
|
|
|
|
cellDef->cd_lastLabel = lab;
|
|
|
|
|
|
|
|
|
|
DBUndoPutLabel(cellDef, lab);
|
|
|
|
|
return align;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef RSIM_MODULE
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SimRsimHandler
|
|
|
|
|
*
|
|
|
|
|
* This procedure is the button handler for the rsim tool.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Left button: used to move the whole box by the lower-left corner.
|
|
|
|
|
* Right button: used to re-size the box by its upper-right corner.
|
|
|
|
|
* If one of the left or right buttons is pushed, then the
|
|
|
|
|
* other is pushed, the corner is switched to the nearest
|
|
|
|
|
* one to the cursor. This corner is remembered for use
|
|
|
|
|
* in box positioning/sizing when both buttons have gone up.
|
|
|
|
|
* Middle button: used to display the rsim node values of whatever
|
|
|
|
|
* paint is selected.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2025-01-31 20:41:36 +01:00
|
|
|
SimRsimHandler(
|
|
|
|
|
MagWindow *w, /* Window containing cursor. */
|
|
|
|
|
TxCommand *cmd) /* Describes what happened. */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
static int buttonCorner = TOOL_ILG;
|
|
|
|
|
int button = cmd->tx_button;
|
|
|
|
|
|
|
|
|
|
if (button == TX_MIDDLE_BUTTON)
|
|
|
|
|
{
|
|
|
|
|
if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
|
|
|
|
|
SimRsimMouse(w);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
|
|
|
|
|
{
|
|
|
|
|
if ((WindNewButtons & (TX_LEFT_BUTTON|TX_RIGHT_BUTTON))
|
|
|
|
|
== (TX_LEFT_BUTTON|TX_RIGHT_BUTTON))
|
|
|
|
|
{
|
|
|
|
|
/* Both buttons are now down. In this case, the FIRST
|
|
|
|
|
* button pressed determines whether we move or size,
|
|
|
|
|
* and the second button is just used as a signal to pick
|
|
|
|
|
* the closest corner.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
buttonCorner = ToolGetCorner(&cmd->tx_p);
|
|
|
|
|
if (button == TX_LEFT_BUTTON) button = TX_RIGHT_BUTTON;
|
|
|
|
|
else button = TX_LEFT_BUTTON;
|
|
|
|
|
}
|
|
|
|
|
else if (button == TX_LEFT_BUTTON) buttonCorner = TOOL_BL;
|
|
|
|
|
else buttonCorner = TOOL_TR;
|
|
|
|
|
dbwButtonSetCursor(button, buttonCorner);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* A button has just come up. If both buttons are down and one
|
|
|
|
|
* is released, we just change the cursor to reflect the current
|
|
|
|
|
* corner and the remaining button (i.e. move or size box).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (WindNewButtons != 0)
|
|
|
|
|
{
|
|
|
|
|
if (button == TX_LEFT_BUTTON)
|
|
|
|
|
dbwButtonSetCursor(TX_RIGHT_BUTTON, buttonCorner);
|
|
|
|
|
else dbwButtonSetCursor(TX_LEFT_BUTTON, buttonCorner);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The last button has been released. Reset the cursor to normal
|
|
|
|
|
* form and then move or size the box.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
GrSetCursor(STYLE_CURS_RSIM);
|
|
|
|
|
switch (button)
|
|
|
|
|
{
|
|
|
|
|
case TX_LEFT_BUTTON:
|
|
|
|
|
ToolMoveBox(buttonCorner, &cmd->tx_p, TRUE, (CellDef *) NULL);
|
|
|
|
|
break;
|
|
|
|
|
case TX_RIGHT_BUTTON:
|
|
|
|
|
ToolMoveCorner(buttonCorner, &cmd->tx_p, TRUE,
|
|
|
|
|
(CellDef *) NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|