/*
 * DRCarray.c --
 *
 * This file provides routines that check arrays to be sure
 * there are no unpleasant interactions between adjacent
 * elements.  Note:  the routines in this file are NOT generally
 * re-entrant.
 *
 *     *********************************************************************
 *     * 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/drc/DRCarray.c,v 1.2 2009/05/01 18:59:44 tim Exp $";
#endif	/* not lint */

#include <sys/types.h>
#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "drc/drc.h"
#include "windows/windows.h"
#include "commands/commands.h"

/* Forward references: */

extern int drcArrayYankFunc(), drcArrayOverlapFunc();

/* Dummy DRC cookie used to pass the error message to DRC error
 * routines.
 */

static DRCCookie drcArrayCookie = {
    0, 0, 0, 0,
    { {0} }, { {0} },
    0, DRC_EXCEPTION_NONE, 0, 0,
    DRC_ARRAY_OVERLAP_TAG,
    (DRCCookie *) NULL
};


/*
 * ----------------------------------------------------------------------------
 *
 * drcArrayFunc --
 *
 * 	This procedure is invoked by DBCellSrArea once for each cell
 *	overlapping the area being checked.  If the celluse is for
 *	an array, then it is checked for array correctness.
 *
 * Results:
 *	Always returns 2, to skip the remaining instances in the
 *	current array.
 *
 * Side effects:
 *	Design rules are checked for the subcell, if it is an array,
 *	and the count of errors is added into drcSubCount.
 *
 * Design:
 *	To verify that an array is correct, we only have to check
 *	four interaction areas, shaded as A, B, C, and D in the diagram
 *	below.  The exact size of the interaction areas depends on
 *	how much overlap there is.  In the extreme cases, there may be
 *	no areas to check at all (instances widely separated), or there
 *	may even be areas with more than four instances overlapping
 *	(spacing less than half the size of the instance).
 *
 * 	--------------DDDDD------------------------------
 *	|             DDDDD             |               |
 *	|               |               |               |
 *	|               |               |               |
 *	|               |               |               |
 * 	-------------------------------------------------
 *	|               |               |               |
 *	|               |               |               |
 *	|               |               |               |
 *	AAAAAAAAAAAAAAAAAAA             |             CCC
 * 	AAAAAAAAAAAAAAAAAAA---------------------------CCC
 *	AAAAAAAAAAAAAAAAAAA             |             CCC
 *	|             BBBBB             |               |
 *	|             BBBBB             |               |
 *	|             BBBBB             |               |
 * 	--------------BBBBB------------------------------
 *
 * ----------------------------------------------------------------------------
 */

int
drcArrayFunc(scx, arg)
    SearchContext *scx;		/* Information about the search. */
    struct drcClientData *arg;	/* Information used in overlap */
{
    int xsep, ysep;
    int xsize, ysize;
    int rval, oldTiles;
    Rect errorArea, yankArea, tmp, tmp2, saveClip;
    DRCCookie *save_cptr;
    CellUse *use = scx->scx_use;
    Rect *area;
    int drcArrayCount;			/* Count of number of errors found. */
    void (*drcArrayErrorFunc)();	/* Function to call on violations. */
    ClientData drcArrayClientData;	/* Extra parameter to pass to func. */
    PaintResultType (*savedPaintTable)[NT][NT];
    PaintResultType (*savedEraseTable)[NT][NT];
    int (*savedPaintPlane)();

    if ((use->cu_xlo == use->cu_xhi) && (use->cu_ylo == use->cu_yhi))
	return 2;

    oldTiles = DRCstatTiles;

    /* During array processing, switch the paint table to catch
     * illegal overlaps.
     */
    savedPaintTable = DBNewPaintTable(DRCCurStyle->DRCPaintTable);
    savedPaintPlane = DBNewPaintPlane(DBPaintPlaneMark);

    /* Set up the client data that will be passed down during
     * checks for exact overlaps.
     */

    save_cptr = arg->dCD_cptr;
    arg->dCD_cptr = &drcArrayCookie;
    area = arg->dCD_clip;
    drcArrayCount = *arg->dCD_errors;
    drcArrayErrorFunc = arg->dCD_function;
    drcArrayClientData = arg->dCD_clientData;

    /* Compute the sizes and separations of elements, in coordinates
     * of the parend.  If the array is 1-dimensional, we set the
     * corresponding spacing to an impossibly large distance.
     */

    tmp.r_xbot = 0;
    tmp.r_ybot = 0;
    if (use->cu_xlo == use->cu_xhi)
	tmp.r_xtop = DRCTechHalo + use->cu_def->cd_bbox.r_xtop
	    - use->cu_def->cd_bbox.r_xbot;
    else tmp.r_xtop = use->cu_xsep;
    if (use->cu_ylo == use->cu_yhi)
	tmp.r_ytop = DRCTechHalo + use->cu_def->cd_bbox.r_ytop
	    - use->cu_def->cd_bbox.r_ybot;
    else tmp.r_ytop = use->cu_ysep;
    GeoTransRect(&use->cu_transform, &tmp, &tmp2);
    xsep = tmp2.r_xtop - tmp2.r_xbot;
    ysep = tmp2.r_ytop - tmp2.r_ybot;
    GeoTransRect(&use->cu_transform, &use->cu_def->cd_bbox, &tmp2);
    xsize = tmp2.r_xtop - tmp2.r_xbot;
    ysize = tmp2.r_ytop - tmp2.r_ybot;

    /* Check each of the four areas A, B, C, and D.  Remember that
     * absolutely arbitrary overlaps between cells are allowed.
     * Skip some or all of the areas if the cell isn't arrayed in
     * that direction or if the instances are widely spaced.
     */

    if (ysep < ysize + DRCTechHalo)
    {
	/* A */

	errorArea.r_xbot = use->cu_bbox.r_xbot;
	errorArea.r_xtop = use->cu_bbox.r_xbot + xsize + DRCTechHalo;
	errorArea.r_ybot = use->cu_bbox.r_ybot + ysep - DRCTechHalo;
	errorArea.r_ytop = use->cu_bbox.r_ybot + ysize + DRCTechHalo;
	GeoClip(&errorArea, area);
	if (!GEO_RECTNULL(&errorArea))
	{
	    GEO_EXPAND(&errorArea, DRCTechHalo, &yankArea);
	    DBCellClearDef(DRCdef);
	    (void) DBArraySr(use, &yankArea, drcArrayYankFunc,
		(ClientData) &yankArea);
	    drcArrayCount += DRCBasicCheck(DRCdef, &yankArea, &errorArea,
		drcArrayErrorFunc, drcArrayClientData);
	    *arg->dCD_clip = *area;
	    GeoClip(arg->dCD_clip, &yankArea);
	    (void) DBArraySr(use, &errorArea, drcArrayOverlapFunc,
		(ClientData) arg);
	}

	/* C */

	errorArea.r_xtop = use->cu_bbox.r_xtop;
	errorArea.r_xbot = use->cu_bbox.r_xtop - DRCTechHalo;
	GeoClip(&errorArea, area);
	if (!GEO_RECTNULL(&errorArea))
	{
	    GEO_EXPAND(&errorArea, DRCTechHalo, &yankArea);
	    DBCellClearDef(DRCdef);
	    (void) DBArraySr(use, &yankArea, drcArrayYankFunc,
		(ClientData) &yankArea);
	    drcArrayCount += DRCBasicCheck(DRCdef, &yankArea, &errorArea,
		drcArrayErrorFunc, drcArrayClientData);
	    *arg->dCD_clip = *area;
	    GeoClip(arg->dCD_clip, &yankArea);
	    (void) DBArraySr(use, &errorArea, drcArrayOverlapFunc,
		(ClientData) arg);
	}
    }

    if (xsep < xsize + DRCTechHalo)
    {
	/* B */

	errorArea.r_xbot = use->cu_bbox.r_xbot + xsep - DRCTechHalo;
	errorArea.r_xtop = use->cu_bbox.r_xbot + xsize + DRCTechHalo;
	errorArea.r_ybot = use->cu_bbox.r_ybot;
	errorArea.r_ytop = errorArea.r_ybot + ysep - DRCTechHalo;
	GeoClip(&errorArea, area);
	if (!GEO_RECTNULL(&errorArea))
	{
	    GEO_EXPAND(&errorArea, DRCTechHalo, &yankArea);
	    DBCellClearDef(DRCdef);
	    (void) DBArraySr(use, &yankArea, drcArrayYankFunc,
		(ClientData) &yankArea);
	    drcArrayCount += DRCBasicCheck(DRCdef, &yankArea, &errorArea,
		drcArrayErrorFunc, drcArrayClientData);
	    *arg->dCD_clip = *area;
	    GeoClip(arg->dCD_clip, &yankArea);
	    (void) DBArraySr(use, &errorArea, drcArrayOverlapFunc,
		(ClientData) arg);
	}

	/* D */

	errorArea.r_ytop = use->cu_bbox.r_ytop;
	errorArea.r_ybot = use->cu_bbox.r_ytop - DRCTechHalo;
	GeoClip(&errorArea, area);
	if (!GEO_RECTNULL(&errorArea))
	{
	    GEO_EXPAND(&errorArea, DRCTechHalo, &yankArea);
	    DBCellClearDef(DRCdef);
	    (void) DBArraySr(use, &yankArea, drcArrayYankFunc,
		(ClientData) &yankArea);
	    drcArrayCount += DRCBasicCheck(DRCdef, &yankArea, &errorArea,
		drcArrayErrorFunc, drcArrayClientData);
	    *arg->dCD_clip = *area;
	    GeoClip(arg->dCD_clip, &yankArea);
	    (void) DBArraySr(use, &errorArea, drcArrayOverlapFunc,
		(ClientData) arg);
	}
    }

    /* Restore original clip rect */
    *arg->dCD_clip = *area;

    (void) DBNewPaintTable(savedPaintTable);
    (void) DBNewPaintPlane(savedPaintPlane);

    /* Update count of array tiles processed. */
    DRCstatArrayTiles += DRCstatTiles - oldTiles;

    /* Restore original DRC cookie pointer in the argument */
    arg->dCD_cptr = save_cptr;
    return 2;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcArrayYankFunc --
 *
 * 	Search action function called while yanking pieces of an array.
 *
 * Results:
 *	Always returns 0, to keep the search going.
 *
 * Side effects:
 *	Yanks from an array element into the DRC yank buffer.
 *
 * ----------------------------------------------------------------------------
 */

	/* ARGSUSED */
int
drcArrayYankFunc(use, transform, x, y, yankArea)
    CellUse *use;			/* CellUse being array-checked. */
    Transform *transform;		/* Transform from instance to parent.*/
    int x, y;				/* Element indices (not used). */
    Rect *yankArea;			/* Area to yank (in parent coords). */

{
    SearchContext scx;
    Transform tinv;

    GeoInvertTrans(transform, &tinv);
    GeoTransRect(&tinv, yankArea, &scx.scx_area);
    scx.scx_use = use;
    scx.scx_trans = *transform;
    (void) DBCellCopyAllPaint(&scx, &DBAllButSpaceBits, 0, DRCuse);
    (void) DBFlatCopyMaskHints(&scx, 0, DRCuse);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * drcArrayOverlapFunc --
 *
 * 	This is a search action function called while checking pieces
 *	of an array to be sure that there aren't any illegal partial
 *	overlaps.  It just invokes overlap checking facilities in
 *	DRCsubcell.c
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	The client's error function may be invoked.
 *
 * ----------------------------------------------------------------------------
 */

	/* ARGSUSED */
int
drcArrayOverlapFunc(use, transform, x, y, arg)
    CellUse *use;		/* CellUse for array element. */
    Transform *transform;	/* Transform from use to parent. */
    int x, y;			/* Indices of element. */
    struct drcClientData *arg;	/* Information used in overlap
				 * checking.  See drcExactOverlapTile.
				 */
{
    Transform tinv;
    SearchContext scx;

    GeoInvertTrans(transform, &tinv);
    GeoTransRect(&tinv, arg->dCD_clip, &scx.scx_area);
    scx.scx_use = use;
    scx.scx_trans = *transform;
    (void) DBTreeSrTiles(&scx, &DRCCurStyle->DRCExactOverlapTypes, 0,
	drcExactOverlapTile, (ClientData) arg);
    return 0;
}
