
#ifndef	lint
static char rcsid[] = "$Header: /ufs/repository/magic/drc/DRCextend.c,v 1.2 2001/01/12 22:12:29 jsolomon Exp $";
#endif	

#include <sys/types.h>
#include <stdio.h>
#include "misc/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "dbwind/dbwtech.h"
#include "drc/drc.h"
#include "signals/signals.h"
#include "utils/stack.h"

#ifdef DRC_EXTENSIONS

Stack	*DRCstack = NULL;

#define PUSHTILE(tp) \
    if ((tp)->ti_client == (ClientData) DRC_UNPROCESSED) { \
        (tp)->ti_client = (ClientData)  DRC_PENDING; \
        STACKPUSH((ClientData) (tp), DRCstack); \
    }

/*
 *-------------------------------------------------------------------------
 *
 * drcCheckArea- checks to see that a collection of tiles of a given 
 *	type have more than a minimum area.
 *
 * Results: none
 *
 * Side Effects: may cause errors to be painted.
 *
 *-------------------------------------------------------------------------
 */
int
drcCheckArea(starttile,arg,cptr)
	Tile	*starttile;
	struct drcClientData	*arg;
	DRCCookie	*cptr;

{
     int		arealimit = cptr->drcc_cdist;
     int		area=0;
     TileTypeBitMask	*oktypes = &cptr->drcc_mask;
     Tile		*tile,*tp;
     Rect		*cliprect = arg->dCD_rect;
     static Stack		*DRCstack= NULL;
     
    arg->dCD_cptr = cptr;
    if (DRCstack == (Stack *) NULL)
	DRCstack = StackNew(64);

    /* Mark this tile as pending and push it */
    PUSHTILE(starttile);

    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *) STACKPOP(DRCstack);
	if (tile->ti_client != (ClientData)DRC_PENDING) continue;
	area += (RIGHT(tile)-LEFT(tile))*(TOP(tile)-BOTTOM(tile));
	tile->ti_client = (ClientData)DRC_PROCESSED;
	/* are we at the clip boundary? If so, skip to the end */
	if (RIGHT(tile) == cliprect->r_xtop ||
	    LEFT(tile) == cliprect->r_xbot ||
	    BOTTOM(tile) == cliprect->r_ybot ||
	    TOP(tile) == cliprect->r_ytop) goto forgetit;

        if (area >= arealimit) goto forgetit;

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp)))	PUSHTILE(tp);

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);
     }
     if (area < arealimit)
     {
	 Rect	rect;
	 TiToRect(starttile,&rect);
	 GeoClip(&rect, arg->dCD_clip);
	 if (!GEO_RECTNULL(&rect)) {
	     (*(arg->dCD_function)) (arg->dCD_celldef, &rect,
		 arg->dCD_cptr, arg->dCD_clientData);
	     /***
	     DBWAreaChanged(arg->dCD_celldef,&rect, DBW_ALLWINDOWS, 
						    &DBAllButSpaceBits);
	     ***/
	     (*(arg->dCD_errors))++;
	 }
     }
forgetit:
     /* reset the tiles */
     starttile->ti_client = (ClientData)DRC_UNPROCESSED;
     STACKPUSH(starttile, DRCstack);
     while (!StackEmpty(DRCstack))
     {
	tile = (Tile *) STACKPOP(DRCstack);

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

     }
     return 0;
}


/*
 *-------------------------------------------------------------------------
 *
 * drcCheckMaxwidth - checks to see that at least one dimension of a region
 *	does not exceed some amount.
 *
 *  This should really be folded together with drcCheckArea, since the routines
 *	are nearly identical, but I'm feeling lazy, so I'm just duplicating
 *	the code for now.
 *
 * Results: 1 if within max bounds, 0 otherwise.
 *
 * Side Effects: may cause errors to be painted, unless this is a check for
 *	DRC_WIDESPACING, in which case this routine only checks to see if
 *	the rule applies, and is not an indication of an error.
 *
 * See DRCtech.c for explanation of use of DRC_WIDESPACING.  ---Tim 01/08/02
 *-------------------------------------------------------------------------
 */

int
drcCheckMaxwidth(starttile,arg,cptr)
	Tile	*starttile;
	struct drcClientData	*arg;
	DRCCookie	*cptr;

{
    int			edgelimit;
    int			retval = 0;
    Rect		boundrect;
    TileTypeBitMask	*oktypes;
    Tile		*tile,*tp;
     
    if (cptr->drcc_flags & DRC_WIDESPACING)
    {
	oktypes = &DBLayerTypeMaskTbl[TiGetType(starttile)];
	edgelimit = cptr->drcc_cdist;
    }
    else
    {
	oktypes = &cptr->drcc_mask;
	edgelimit = cptr->drcc_dist;
    }
    arg->dCD_cptr = cptr;
    if (DRCstack == (Stack *) NULL)
	DRCstack = StackNew(64);

    /* if bends are allowed, just check on a tile-by-tile basis that
        one dimension is the max.  This is pretty stupid, but it correctly
	calculates the trench width rule. dcs 12.06.89 */

    if (cptr->drcc_flags & DRC_BENDS)
    {
	Rect	rect;
	TiToRect(starttile,&rect);
	if (rect.r_xtop-rect.r_xbot > edgelimit &&
	    rect.r_ytop-rect.r_ybot > edgelimit)
	{
	    GeoClip(&rect, arg->dCD_clip);
	    if (!GEO_RECTNULL(&rect))
	    {
		if (!(cptr->drcc_flags & DRC_WIDESPACING))
		{
		    (*(arg->dCD_function)) (arg->dCD_celldef, &rect,
			arg->dCD_cptr, arg->dCD_clientData);
		    (*(arg->dCD_errors))++;
		}
		retval = 1;
	     }
	}
	return retval;
    }

    /* Mark this tile as pending and push it */

    PUSHTILE(starttile);
    TiToRect(starttile,&boundrect);

    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *) STACKPOP(DRCstack);
	if (tile->ti_client != (ClientData)DRC_PENDING) continue;
	
	if (boundrect.r_xbot > LEFT(tile)) boundrect.r_xbot = LEFT(tile);
	if (boundrect.r_xtop < RIGHT(tile)) boundrect.r_xtop = RIGHT(tile);
	if (boundrect.r_ybot > BOTTOM(tile)) boundrect.r_ybot = BOTTOM(tile);
	if (boundrect.r_ytop < TOP(tile)) boundrect.r_ytop = TOP(tile);
	tile->ti_client = (ClientData)DRC_PROCESSED;

        if (boundrect.r_xtop - boundrect.r_xbot > edgelimit &&
             boundrect.r_ytop - boundrect.r_ybot > edgelimit)
	{
	    break;
	}

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp)))	PUSHTILE(tp);

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (TTMaskHasType(oktypes, TiGetType(tp))) PUSHTILE(tp);
    }

    if (boundrect.r_xtop - boundrect.r_xbot > edgelimit &&
             boundrect.r_ytop - boundrect.r_ybot > edgelimit) 
    {
	Rect	rect;
	TiToRect(starttile,&rect);
	GeoClip(&rect, arg->dCD_clip);
	if (!GEO_RECTNULL(&rect)) {
	    if (!(cptr->drcc_flags & DRC_WIDESPACING))
	    {
		(*(arg->dCD_function)) (arg->dCD_celldef, &rect,
			arg->dCD_cptr, arg->dCD_clientData);
		(*(arg->dCD_errors))++;
	    }
	    retval = 1;
	}
	 
    }
    /* reset the tiles */
    starttile->ti_client = (ClientData)DRC_UNPROCESSED;
    STACKPUSH(starttile, DRCstack);
    while (!StackEmpty(DRCstack))
    {
	tile = (Tile *) STACKPOP(DRCstack);

	/* Top */
	for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Left */
	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Bottom */
	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

	/* Right */
	for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
	    if (tp->ti_client != (ClientData)DRC_UNPROCESSED)
	    {
	    	 tp->ti_client = (ClientData)DRC_UNPROCESSED;
		 STACKPUSH(tp,DRCstack);
	    }

    }
    return retval;
}


/*
 *-------------------------------------------------------------------------
 *
 * drcCheckRectSize- 
 *
 *	Checks to see that a collection of tiles of given 
 *	types have the proper size (max size and also even or odd size).
 *
 * Results: none
 *
 * Side Effects: may cause errors to be painted.
 *
 *-------------------------------------------------------------------------
 */

drcCheckRectSize(starttile, arg, cptr)
    Tile *starttile;
    struct drcClientData *arg;
    DRCCookie *cptr;
{
    int maxsize = cptr->drcc_dist;
    int even = cptr->drcc_cdist;
    TileTypeBitMask *oktypes = &cptr->drcc_mask;
    int width;
    int height;
    int errwidth;
    int errheight;
    Tile *t;
    bool error = FALSE;

    /* This code only has to work for rectangular regions, since we always
     * check for rectangular-ness using normal edge rules produced when
     * we read in the tech file.
     */
    arg->dCD_cptr = cptr;
    ASSERT(TTMaskHasType(oktypes, TiGetType(starttile)), "drcCheckRectSize");
    for (t = starttile; TTMaskHasType(oktypes, TiGetType(t)); t = TR(t)) 
	/* loop has empty body */ ;
    errwidth = width = LEFT(t) - LEFT(starttile);
    for (t = starttile; TTMaskHasType(oktypes, TiGetType(t)); t = RT(t)) 
	/* loop has empty body */ ;
    errheight = height = BOTTOM(t) - BOTTOM(starttile);
    ASSERT(width > 0 && height > 0, "drcCheckRectSize");

    if (width > maxsize) {error = TRUE; errwidth = (width - maxsize);}
    else if (height > maxsize) {error = TRUE; errheight = (height - maxsize);}
    else if (even >= 0) {
	/* meaning of "even" variable:  -1, any; 0, even; 1, odd */
	if (ABS(width - ((width/2)*2)) != even) {error = TRUE; errwidth = 1;}
	else if (ABS(height - ((height/2)*2)) != even) {error = TRUE; errheight = 1;}
    }

    if (error) {
	Rect rect;
	TiToRect(starttile, &rect);
	rect.r_xtop = rect.r_xbot + errwidth;
	rect.r_ytop = rect.r_ybot + errheight;
	GeoClip(&rect, arg->dCD_clip);
	if (!GEO_RECTNULL(&rect)) {
	    (*(arg->dCD_function)) (arg->dCD_celldef, &rect,
		arg->dCD_cptr, arg->dCD_clientData);
	    (*(arg->dCD_errors))++;
	}
	
    }
    return 0;
}

#endif
