magic/database/DBtpaint.c

835 lines
22 KiB
C
Raw Normal View History

/*
* DBtechpaint.c --
*
* Management of composition rules and the paint/erase tables.
*
* *********************************************************************
* * 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/database/DBtpaint.c,v 1.2 2010/06/08 19:16:42 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <ctype.h>
#include <string.h> /* for memset(), memcpy() */
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/tech.h"
#include "textio/textio.h"
/* Painting and erasing tables */
PaintResultType DBPaintResultTbl[NP][NT][NT];
PaintResultType DBEraseResultTbl[NP][NT][NT];
PaintResultType DBWriteResultTbl[NT][NT];
PaintResultType DBSpecialResultTbl[NT];
PlaneMask DBTypePaintPlanesTbl[NT];
PlaneMask DBTypeErasePlanesTbl[NT];
/* ----------------- Data local to tech file processing --------------- */
/*
* Tables telling which rules are default, and which have come
* from user-specified rules. The bit is CLEAR if the type is
* a default type.
*/
TileTypeBitMask dbNotDefaultEraseTbl[NT];
TileTypeBitMask dbNotDefaultPaintTbl[NT];
/* --------------------- Data local to this file ---------------------- */
int dbNumSavedRules = 0;
Rule dbSavedRules[NT];
/* Forward declarations */
extern void dbTechBitTypeInit();
bool dbTechAddPaintErase();
bool dbTechSaveCompose();
/*
* ----------------------------------------------------------------------------
*
* DBTechInitCompose --
*
* Initialize the painting and erasing rules prior to processing
* the "compose" section. The rules for builtin types are computed
* here, as well as the default rules for all other types. This
* procedure must be called after the "types" and "contacts" sections
* have been read, since we need to know about all existing tile types.
*
* Results:
* None.
*
* Side effects:
* Modifies the paint and erase tables.
*
* ----------------------------------------------------------------------------
*/
void
DBTechInitCompose()
{
TileType s, t, r;
int ps;
PaintResultType *stype, *dtype;
TileTypeBitMask *ttype;
/* Default painting rules for error types */
static TileType errorBitToType[] =
{
TT_SPACE, /* 0 */ TT_ERROR_P, /* 1 */
TT_ERROR_S, /* 2 */ TT_ERROR_PS, /* 3 */
};
/* Painting and erasing are no-ops for undefined tile types */
/* The following code is the FAST version using memcpy(). */
/* See below for the actual slow loops. */
stype = dtype = &(DBEraseResultTbl[0][0][0]);
for (ps = 0; ps < TT_MAXTYPES; ps++)
*dtype++ = (PaintResultType)ps;
for (ps = 1; ps < PL_MAXTYPES * TT_MAXTYPES; ps++)
{
memcpy((void *)dtype, (void *)stype, (size_t)TT_MAXTYPES
* sizeof(PaintResultType));
dtype += TT_MAXTYPES;
}
/* Fast copy the entire erase table to the paint table memory */
dtype = &(DBPaintResultTbl[0][0][0]);
memcpy((void *)dtype, (void *)stype, (size_t)(TT_MAXTYPES
* TT_MAXTYPES * PL_MAXTYPES * sizeof(PaintResultType)));
/* The following code is dreadfully slow, but I'm leaving it */
/* in as a comment because it's easier to read. The code */
/* above uses memory copying tricks to speed up the process. */
/*
for (pNum = 0; pNum < PL_MAXTYPES; pNum++)
{
for (s = 0; s < TT_MAXTYPES; s++)
{
for (t = 0; t < TT_MAXTYPES; t++)
{
/- Paint and erase are no-ops -/
dbSetEraseEntry(s, t, pNum, s);
dbSetPaintEntry(s, t, pNum, s);
}
}
}
for (s = 0; s < TT_MAXTYPES; s++)
{
for (t = 0; t < TT_MAXTYPES; t++)
{
/- Write overwrites existing contents -/
dbSetWriteEntry(s, t, t);
}
}
*/
#if TT_MAXTYPES <= 256
/* For single-byte values, memset() is fastest.
dtype = &(DBWriteResultTbl[0][0]);
for (q = 0; q < TT_MAXTYPES; q++)
{
memset((void *)dtype, (int)q, (size_t)TT_MAXTYPES);
dtype += TT_MAXTYPES;
}
#else
/* This is the slow loop, but it still faster than using */
/* macro dbSetWriteEntry(). */
dtype = &(DBWriteResultTbl[0][0]);
for (t = 0; t < TT_MAXTYPES; t++)
for (s = 0; s < TT_MAXTYPES; s++)
*dtype++ = t;
#endif
/* All painting and erasing rules are default initially */
/* This is also faster than the loop below. */
ttype = &(dbNotDefaultEraseTbl[0]);
for (s = 0; s < DBNumTypes; s++) *ttype++ = DBZeroTypeBits;
ttype = &(dbNotDefaultPaintTbl[0]);
for (s = 0; s < DBNumTypes; s++) *ttype++ = DBZeroTypeBits;
/*
for (s = 0; s < DBNumTypes; s++)
{
dbNotDefaultEraseTbl[s] = DBZeroTypeBits;
dbNotDefaultPaintTbl[s] = DBZeroTypeBits;
}
*/
/*
* For each type t:
* erase(t, t, plane(t)) -> SPACE
*
* For each type s, t:
* paint(s, t, plane(t)) -> t
* paint(s, t, ~plane(t)) -> s
*/
for (s = 0; s < DBNumTypes; s++)
{
if ((ps = DBPlane(s)) > 0)
{
for (t = 0; t < DBNumTypes; t++)
{
if (DBPlane(t) > 0)
{
r = (ps == DBPlane(t)) ? t : s;
dbSetEraseEntry(s, t, ps, s);
dbSetPaintEntry(s, t, ps, r);
}
}
/* Everything can be erased to space on its home plane */
dbSetEraseEntry(s, s, ps, TT_SPACE);
/* Everything paints over space on its home plane */
dbSetPaintEntry(TT_SPACE, s, ps, s);
}
}
/*
* Special handling for check tile and error tile combinations.
*/
#define PCHK PL_DRC_CHECK
#define PERR PL_DRC_ERROR
#define tblsize(t) ( (sizeof (t)) / (sizeof (t[0])) )
dbTechBitTypeInit(errorBitToType, tblsize(errorBitToType), PERR, FALSE);
#undef tblsize
/*
* Paint results are funny for check plane because
* CHECKPAINT+CHECKSUBCELL = CHECKPAINT
*/
dbSetPaintEntry(TT_SPACE, TT_CHECKPAINT, PCHK, TT_CHECKPAINT);
dbSetPaintEntry(TT_SPACE, TT_CHECKSUBCELL, PCHK, TT_CHECKSUBCELL);
dbSetPaintEntry(TT_CHECKPAINT, TT_CHECKSUBCELL, PCHK, TT_CHECKPAINT);
dbSetPaintEntry(TT_CHECKSUBCELL, TT_CHECKPAINT, PCHK, TT_CHECKPAINT);
#undef PCHK
#undef PERR
/* Added 5/27/10: Special table used for painting non-Manhattan */
/* tiles. Uses TT_CHECKSUBCELL because that type does not exist on */
/* any paintable plane, and the checkpaint plane does not use non- */
/* manhattan tiles. */
for (s = 0; s < DBNumTypes; s++) DBSpecialResultTbl[s] = TT_CHECKSUBCELL;
}
/*
* ----------------------------------------------------------------------------
*
* dbTechBitTypeInit --
*
* Handle initialization of the paint and erase result tables for a
* set of ln2(n) primary types with n distinct mutual overlap types.
* The table bitToType points to a table containing n TileTypes
* (the overlap types) with the property that
*
* bitToType[i] and bitToType[j] combine to yield bitToType[i | j]
*
* Also (unless composeFlag is set) erasing bitToType[j] from bitToType[i]
* gives bitToType[i & (~j)],
* i.e., it clears all of the j-type material out of the i-type material.
* The bitToType[k] for which k's binary representation has only a single
* bit set in it are the "primary" types.
*
* If composeFlag is set, the above is modified slightly to be analagous
* to compose rules, specifically, erase rules for nonprimary types are
* the default rules, i.e. they only erase precisely themselves. This
* makes ":erase *-primary" work in the expected way.
*
* Results:
* None.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
void
dbTechBitTypeInit(bitToType, n, pNum, composeFlag)
TileType *bitToType;
int n, pNum;
bool composeFlag;
{
int i, j;
TileType have, type;
for (i = 0; i < n; i++)
{
have = bitToType[i];
for (j = 0; j < n; j++)
{
type = bitToType[j];
dbSetPaintEntry(have, type, pNum, bitToType[i | j]);
if(!composeFlag || dbIsPrimary(j))
{
dbSetEraseEntry(have, type, pNum, bitToType[i & (~j)]);
}
}
}
}
/* Returns nonzero if exactly one bit set */
int
dbIsPrimary(n)
int n;
{
int bitCount;
for(bitCount=0; n>0; n=n>>1)
{
if(n&1)
{
bitCount++;
}
}
return (bitCount==1);
}
/*
* ----------------------------------------------------------------------------
*
* DBTechAddCompose --
*
* Process a single compose/erase rule. If the type being described is
* a contact, save the rule and defer processing it until the end of this
* section, because we need to know the behavior of all non-contact types
* that might be residues before processing a contact composition rule.
* Rules for non-contact types are processed here.
*
* Results:
* TRUE if successful, FALSE on error.
*
* Side effects:
* Modifies the paint/erase tables if the type being described
* is not a contact; otherwise, appends a rule to the list of
* contact erase/compose rules for later processing. Marks the
* paint/erase table entries affected to show that they contain
* user-specified rules instead of the default ones, so we don't
* override them later.
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
bool
DBTechAddCompose(sectionName, argc, argv)
char *sectionName;
int argc;
char *argv[];
{
TileType type, r, s;
int pNum, ruleType, i;
static char *ruleNames[] =
{ "compose", "decompose", "paint", "erase", 0 };
static int ruleTypes[] =
{ RULE_COMPOSE, RULE_DECOMPOSE, RULE_PAINT, RULE_ERASE };
if (argc < 4)
{
TechError("Line must contain at least ruletype, result + pair\n");
return FALSE;
}
/* Look up and skip over type of rule */
i = Lookup(*argv, ruleNames);
if (i < 0)
{
TechError("%s rule type %s. Must be one of:\n\t",
i == -1 ? "Ambiguous" : "Unknown", *argv);
for (i = 0; ruleNames[i]; i++)
TxError("\"%s\" ", ruleNames[i]);
TxError("\n");
return FALSE;
}
ruleType = ruleTypes[i];
argv++, argc--;
/* Paint or erase rules are processed specially */
switch (ruleType)
{
case RULE_PAINT:
case RULE_ERASE:
return (dbTechAddPaintErase(ruleType, sectionName, argc, argv));
}
/* Compose or decompose rule: find result type and then skip over it */
if ((type = DBTechNoisyNameType(*argv)) < 0)
return FALSE;
argv++, argc--;
if (argc & 01)
{
TechError("Types on RHS of rule must be in pairs\n");
return FALSE;
}
/* Compose/decompose rules for contacts are saved away */
if (IsContact(type))
return dbTechSaveCompose(ruleType, type, argc, argv);
/* Rules for non-contacts are processed here */
for ( ; argc > 0; argc -= 2, argv += 2)
{
if ((r = DBTechNoisyNameType(argv[0])) < 0
|| (s = DBTechNoisyNameType(argv[1])) < 0)
return FALSE;
if (IsContact(r) || IsContact(s))
{
TechError("Can't have contact layers on RHS of non-contact rule\n");
return FALSE;
}
pNum = DBPlane(r);
switch (ruleType)
{
case RULE_COMPOSE:
dbSetPaintEntry(r, s, pNum, type);
dbSetPaintEntry(s, r, pNum, type);
TTMaskSetType(&dbNotDefaultPaintTbl[r], s);
TTMaskSetType(&dbNotDefaultPaintTbl[s], r);
/* Fall through to */
case RULE_DECOMPOSE:
dbSetPaintEntry(type, r, pNum, type);
dbSetPaintEntry(type, s, pNum, type);
dbSetEraseEntry(type, r, pNum, s);
dbSetEraseEntry(type, s, pNum, r);
TTMaskSetType(&dbNotDefaultPaintTbl[type], r);
TTMaskSetType(&dbNotDefaultPaintTbl[type], s);
TTMaskSetType(&dbNotDefaultEraseTbl[type], r);
TTMaskSetType(&dbNotDefaultEraseTbl[type], s);
break;
}
}
return TRUE;
}
/*
* ----------------------------------------------------------------------------
*
* dbTechSaveCompose --
*
* Save a compose rule for a contact 't' in the table dbSavedRules.
* Check to make sure the rule is legal.
*
* Results:
* Returns TRUE if successful, FALSE on error.
*
* Side effects:
* Updates dbSavedRules[] and increments dbNumSavedRules.
*
* ----------------------------------------------------------------------------
*/
bool
dbTechSaveCompose(ruleType, t, argc, argv)
int ruleType;
TileType t;
int argc;
char *argv[];
{
TileType r, s;
Rule *rp;
rp = &dbSavedRules[dbNumSavedRules++];
rp->r_ruleType = ruleType;
rp->r_result = t;
rp->r_npairs = 0;
for ( ; argc > 0; argc -= 2, argv += 2)
{
r = DBTechNoisyNameType(argv[0]);
s = DBTechNoisyNameType(argv[1]);
if (r < 0 || s < 0)
return FALSE;
/* At most one of r and s may be a contact */
if (IsContact(r) && IsContact(s))
{
TechError("Only one type in each pair may be a contact\n");
return FALSE;
}
/*
* The planes comprising 't' must be a superset of the
* planes comprising 'r' and the planes comprising 's'.
*/
if (((LayerPlaneMask(r) | LayerPlaneMask(s)) & ~LayerPlaneMask(t)) != 0)
{
TechError("Component planes are a superset of result planes\n");
return FALSE;
}
if (ruleType == RULE_COMPOSE)
{
/* Types r and s can't appear on planes outside of t's */
if ((LayerPlaneMask(r) | LayerPlaneMask(s)) != LayerPlaneMask(t))
{
TechError("Union of pair planes must = result planes\n");
return FALSE;
}
/* The following restriction has been lifted due to */
/* the recursive plane painting method added to */
/* routine DBPaint(). (Tim, 5/11/04) */
// if (!dbTechCheckImages(t, r, s) || !dbTechCheckImages(t, s, r))
// return FALSE;
}
rp->r_pairs[rp->r_npairs].rp_a = r;
rp->r_pairs[rp->r_npairs].rp_b = s;
rp->r_npairs++;
}
return TRUE;
}
#if 0 /* deprecated function (5/11/04) */
/*
* ----------------------------------------------------------------------------
*
* dbTechCheckImages --
*
* When processing a compose rule for 't' with RHS components
* 'r' and 's', check to be sure that the images of 't' on
* those planes present in 'r' but not in 's' are identical to
* the images of 'r' on those planes. This is necessary in order
* that the result on these planes not depend on types present on
* other planes.
*
* Results:
* Returns TRUE if successful, FALSE on error.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
bool
dbTechCheckImages(t, r, s)
TileType t; /* Type that is composed */
TileType r; /* First constituent */
TileType s; /* Second constituent */
{
int pNum;
PlaneMask pMask;
if (pMask = (LayerPlaneMask(r) & ~LayerPlaneMask(s)))
{
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if (PlaneMaskHasPlane(pMask, pNum) && (t != r))
{
TechError("Result image on plane %s must be the same "
"as image of %s on plane %s\n",
DBPlaneLongName(pNum),
DBTypeLongName(r),
DBPlaneLongName(pNum));
return FALSE;
}
}
return TRUE;
}
#endif /* 0 */
/*
* ----------------------------------------------------------------------------
*
* dbTechAddPaintErase --
*
* Add a new entry to the paint or erase table.
* The semantics is that painting a tile of type1 with paint type2
* yields a tile of typeres.
*
* Results:
* Returns TRUE if successful, FALSE on error.
*
* Side effects:
* Updates the database technology variables.
*
* ----------------------------------------------------------------------------
*/
bool
dbTechAddPaintErase(type, sectionName, argc, argv)
int type;
char *sectionName;
int argc;
char *argv[];
{
int pNum;
PlaneMask pMask, rMask;
TileType t1, t2, tres;
TileTypeBitMask tMask;
if (argc < 3)
{
TechError("Line must contain at least 3 types\n");
return FALSE;
}
if ((t1 = DBTechNoisyNameType(argv[0])) < 0) return FALSE;
if ((t2 = DBTechNoisyNameType(argv[1])) < 0) return FALSE;
/* Modified 9/22/2020 to allow multiple types to paint, for example */
/* to replace a contact type with types on both residue planes. */
rMask = DBTechNoisyNameMask(argv[2], &tMask);
if (TTMaskIsZero(&tMask)) return FALSE;
if (argc == 3)
{
if (t1 == TT_SPACE)
{
TechError("<%s, %s, %s>:\n"
"Must specify plane in paint table for "
"painting space\n",
argv[0], argv[1], argv[2]);
return FALSE;
}
else
pMask = LayerPlaneMask(t1);
}
else
{
if ((pNum = DBTechNoisyNamePlane(argv[3])) < 0) return FALSE;
else
pMask = PlaneNumToMaskBit(pNum);
}
pMask &= ~rMask;
for (tres = 0; tres < DBNumTypes; tres++)
{
if (TTMaskHasType(&tMask, tres))
{
if (type == RULE_PAINT)
{
/* Apply to all planes of rMask. */
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if (PlaneMaskHasPlane(rMask, pNum))
if (DBTypeOnPlane(tres, pNum))
dbSetPaintEntry(t1, t2, pNum, tres);
}
else /* (type == RULE_ERASE) */
{
/* Apply to all planes of rMask. */
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if (PlaneMaskHasPlane(rMask, pNum))
if (DBTypeOnPlane(tres, pNum))
dbSetEraseEntry(t1, t2, pNum, tres);
}
}
}
if (type == RULE_PAINT)
{
/* For all planes of pMask which are not in rMask, result is space */
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if (PlaneMaskHasPlane(pMask, pNum))
dbSetPaintEntry(t1, t2, pNum, TT_SPACE);
}
else /* (type == RULE_ERASE) */
{
/* For all planes of pMask which are not in rMask, result is space */
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
if (PlaneMaskHasPlane(pMask, pNum))
dbSetEraseEntry(t1, t2, pNum, TT_SPACE);
}
TTMaskSetType(&dbNotDefaultPaintTbl[t1], t2);
return TRUE;
}
/*
* ----------------------------------------------------------------------------
*
* dbTechCheckPaint --
*
* DEBUGGING.
* Check painting and erasing rules to make sure that the result
* type is legal for the plane being affected.
*
* Results:
* None.
*
* Side effects:
* Prints stuff in the event of an error.
*
* ----------------------------------------------------------------------------
*/
void
dbTechCheckPaint(where)
char *where; /* If non-null, print this as header */
{
TileType have, t, result;
bool printedHeader = FALSE;
for (have = TT_TECHDEPBASE; have < DBNumTypes; have++)
{
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
{
result = DBStdPaintEntry(have, t, DBPlane(have));
if (result != TT_SPACE && DBPlane(result) != DBPlane(have))
{
if (!printedHeader && where)
TxPrintf("\n%s:\n", where), printedHeader = TRUE;
TxPrintf("%s + %s -> %s\n",
DBTypeShortName(have), DBTypeShortName(t),
DBTypeShortName(result));
}
result = DBStdEraseEntry(have, t, DBPlane(have));
if (result != TT_SPACE && DBPlane(result) != DBPlane(have))
{
if (!printedHeader && where)
TxPrintf("\n%s:\n", where), printedHeader = TRUE;
TxPrintf("%s - %s -> %s\n",
DBTypeShortName(have), DBTypeShortName(t),
DBTypeShortName(result));
}
}
}
}
/*
* ----------------------------------------------------------------------------
*
* dbTechPrintPaint --
*
* DEBUGGING.
* Print painting and erasing rules. If contactsOnly is TRUe, only
* print those rules involving pairs of contact types. The argument
* "where" is printed as a header if it is non-NULL. If doPaint is
* TRUE, we print the paint rules, else we print the erase rules.
*
* Results:
* None.
*
* Side effects:
* Prints stuff.
*
* ----------------------------------------------------------------------------
*/
void
dbTechPrintPaint(where, doPaint, contactsOnly)
char *where; /* If non-null, print this as header */
bool doPaint; /* TRUE -> print paint tables, FALSE -> print erase */
bool contactsOnly;
{
TileType have, paint, erase, result;
int plane;
LayerInfo *lp;
if (where)
TxPrintf("\n%s:\n\n", where);
if (doPaint)
{
TxPrintf("PAINTING RULES:\n");
for (have = TT_TECHDEPBASE; have < DBNumTypes; have++)
{
if (contactsOnly && !IsContact(have)) continue;
for (paint = TT_TECHDEPBASE; paint < DBNumTypes; paint++)
{
if (contactsOnly && !IsContact(paint)) continue;
for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
{
lp = &dbLayerInfo[have];
if (!PlaneMaskHasPlane(lp->l_pmask, plane))
continue;
result = DBStdPaintEntry(have, paint, plane);
if (result != have)
{
TxPrintf("%s ",
DBTypeShortName(have));
if (IsContact(have))
TxPrintf("(on %s) ",
DBPlaneLongName(plane));
TxPrintf(" + %s -> %s\n",
DBTypeShortName(paint),
DBTypeShortName(result));
}
}
}
}
}
else
{
TxPrintf("ERASING RULES:\n");
for (have = TT_TECHDEPBASE; have < DBNumTypes; have++)
{
if (contactsOnly && !IsContact(have)) continue;
for (erase = TT_TECHDEPBASE; erase < DBNumTypes; erase++)
{
if (contactsOnly && !IsContact(erase)) continue;
for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
{
lp = &dbLayerInfo[have];
if (!PlaneMaskHasPlane(lp->l_pmask, plane))
continue;
result = DBStdEraseEntry(have, erase, plane);
if (result != have)
{
TxPrintf("%s ",
DBTypeShortName(have));
if (IsContact(have))
TxPrintf("(on %s) ",
DBPlaneLongName(plane));
TxPrintf(" - %s -> %s\n",
DBTypeShortName(erase),
DBTypeShortName(result));
}
}
}
}
}
}