Logo Search packages:      
Sourcecode: magic version File versions  Download package

selCreate.c

/* selCreate.c -
 *
 *    This file provides routines to make selections by copying
 *    things into a special cell named "__SELECT__".
 *
 *     ********************************************************************* 
 *     * 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-7.5/select/selCreate.c,v 1.17 2008/11/25 13:40:06 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/malloc.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 "utils/undo.h"
#include "commands/commands.h"
#include "select/selInt.h"
#include "select/select.h"
#include "drc/drc.h"
#include "utils/main.h"
#include "utils/signals.h"

/* Two cells worth of information are kept around by the selection
 * module.  SelectDef and SelectUse are for the cells whose contents
 * are the current selection.  Select2Def and Select2Use provide a
 * temporary working space for procedures that manipulate the selection.
 * for example, Select2Def is used to hold nets or regions while they
 * are being extracted by SelectRegion or SelectNet.  Once completely
 * extracted, information is copied to SelectDef.  Changes to
 * SelectDef are undo-able and redo-able (so that the undo package
 * can deal with selection changes), but changes to Select2Def are
 * not undo-able (undoing is always disabled when the cell is modified).
 */

global CellDef *SelectDef, *Select2Def;
global CellUse *SelectUse, *Select2Use;

/* The CellDef below points to the definition FROM which the selection
 * is extracted.  This is the root definition of a window.  Everything
 * in the selection must have come from the same place, so we clear the
 * selection whenever the user tries to select from a new hierarchy.
 */

CellDef *SelectRootDef = NULL;

/* The CellUse below is the last use selected by SelectUse.  It is
 * kept around to support the "replace" feature of SelectUse.
 *
 * Procedures which deselect a cell must reset this to null if they
 * happen to deselect this usage.  (Danger Will Robinson)
 */

global CellUse *selectLastUse = NULL;


/*
 * ----------------------------------------------------------------------------
 *
 * SelectInit --
 *
 *    This procedure initializes the selection code by creating
 *    the selection cells.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The select cells are created if they don't already exist.
 *    Selection undo-ing is also initialized.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectInit()
{
    static bool initialized = FALSE;

    if (initialized) return;
    else initialized = TRUE;

    /* Create the working cells used internally to this module to
     * hold selected information.  Don't allow any of this to be
     * undone, or else it could invalidate all the pointers we
     * keep around to the cells.
     */

    UndoDisable();
    SelectDef = DBCellLookDef("__SELECT__");
    if (SelectDef == (CellDef *) NULL)
    {
      SelectDef = DBCellNewDef("__SELECT__",(char *) NULL);
      ASSERT(SelectDef != (CellDef *) NULL, "SelectInit");
      DBCellSetAvail(SelectDef);
      SelectDef->cd_flags |= CDINTERNAL;
      TTMaskZero(&SelectDef->cd_types);
    }
    SelectUse = DBCellNewUse(SelectDef, (char *) NULL);
    DBSetTrans(SelectUse, &GeoIdentityTransform);
    SelectUse->cu_expandMask = CU_DESCEND_SPECIAL;    /* This is always expanded. */
    SelectUse->cu_flags = 0;  /* never locked down */

    Select2Def = DBCellLookDef("__SELECT2__");
    if (Select2Def == (CellDef *) NULL)
    {
      Select2Def = DBCellNewDef("__SELECT2__",(char *) NULL);
      ASSERT(Select2Def != (CellDef *) NULL, "SelectInit");
      DBCellSetAvail(Select2Def);
      Select2Def->cd_flags |= CDINTERNAL;
    }
    Select2Use = DBCellNewUse(Select2Def, (char *) NULL);
    DBSetTrans(Select2Use, &GeoIdentityTransform);
    Select2Use->cu_expandMask = CU_DESCEND_SPECIAL;   /* This is always expanded. */
    Select2Use->cu_flags = 0; /* never locked down */
    UndoEnable();

    SelUndoInit();
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectClear --
 *
 *    This procedure clears the current selection.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    All information is removed from the select cell, and selection
 *    information is also taken off the screen.
 *
 * ----------------------------------------------------------------------------
 */

/* The variables below are used to record information about subcells that
 * must be cleared from the select cell.
 */

#define MAXUSES 30
static CellUse *(selDeleteUses[MAXUSES]);
static int selNDelete;

void
SelectClear()
{
    SearchContext scx;
    Rect r;
    extern int selClearFunc();            /* Forward declaration. */

    if (SelectRootDef == NULL) return;

    scx.scx_area = SelectDef->cd_bbox;

    /* Special handling when the cell is flagged as a net selection */

    if (SelectUse->cu_flags & CU_SELECT_NET)
    {
      SelNetRememberForUndo((CellDef *)NULL, (Point *)NULL,
                  TT_SPACE, FALSE, FALSE);
      SelectUse->cu_flags = 0;
      DBCellClearDef(SelectDef);
    }
    else
    {
      SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
      SelectUse->cu_flags = 0;

      /* Erase all the paint from the select cell. */
      DBEraseMask(SelectDef, &TiPlaneRect, &DBAllButSpaceBits);

      /* For the line below to work, paint tables need to be set up correctly! */
      /* DBErase(SelectDef, &TiPlaneRect, TT_SPACE); */

      /* Erase all of the labels from the select cell. */

      (void) DBEraseLabel(SelectDef, &TiPlaneRect, &DBAllTypeBits);

      /* Erase all of the subcells from the select cell.  This is a bit tricky,
       * because we can't erase the subcells while searching for them (it will
       * cause problems for the database).  The code below first grabs up a
       * few subcells, then deletes them, then grabs up a few more, then deletes
       * them, and so on until done.
       */

      scx.scx_use = SelectUse;
      scx.scx_trans = GeoIdentityTransform;
      while (TRUE)
      {
          int i;

          selNDelete = 0;
          (void) DBCellSrArea(&scx, selClearFunc, (ClientData) NULL);
          for (i = 0; i < selNDelete; i += 1)
          {
            DBUnLinkCell(selDeleteUses[i], SelectDef);
            DBDeleteCell(selDeleteUses[i]);
            (void) DBCellDeleteUse(selDeleteUses[i]);
          }
          if (selNDelete < MAXUSES) break;
      }
      selectLastUse = NULL;

      SelRememberForUndo(FALSE, SelectRootDef, &scx.scx_area);
    }
    TTMaskZero(&SelectDef->cd_types);

    /* Reset the transform, if we have been moving the selection around */
    GeoTransRect(&SelectUse->cu_transform, &scx.scx_area, &r);
    SelectUse->cu_transform = GeoIdentityTransform;

    /* Erase the selection from the screen. */
    DBWHLRedraw(SelectRootDef, &r, TRUE);

    DBReComputeBbox(SelectDef);
    DBWAreaChanged(SelectDef, &scx.scx_area, DBW_ALLWINDOWS,
      (TileTypeBitMask *) NULL);
}

/* Search function to help clear subcells from the selection.  It just
 * records information about several subcells (up to MAXUSES).
 */

int
selClearFunc(scx)
    SearchContext *scx;       /* Describes a cell that was found. */
{
    selDeleteUses[selNDelete] = scx->scx_use;
    selNDelete += 1;
    if (selNDelete == MAXUSES) return 1;
    else return 2;
}


/*
 * ----------------------------------------------------------------------------
 *
 * SelectArea --
 *
 *    This procedure selects all information of given types that
 *    falls in a given area.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The indicated information is added to the select cell, and
 *    outlined on the screen.  Only information of particular
 *    types, and in expanded cells (according to xMask) is
 *    selected.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectArea(scx, types, xMask)
    SearchContext *scx;       /* Describes the area in which material
                         * is to be selected.  The resulting
                         * coordinates should map to the coordinates
                         * of EditRootDef.  The cell use should be
                         * the root of a window.
                         */
    TileTypeBitMask *types;   /* Indicates which layers to select.  Can
                         * include L_CELL and L_LABELS to select
                         * labels and unexpanded subcells.  If L_LABELS
                         * is specified then all labels touching the
                         * area are selected.  If L_LABELS isn't
                         * specified, then only labels attached to
                         * selected material are selected.
                         */
    int xMask;                /* Indicates window (or windows) where cells
                         * must be expanded for their contents to be
                         * considered.  0 means treat everything as
                         * expanded.
                         */
{
    Rect labelArea, cellArea;

    /* If the source definition is changing, clear the old selection. */

    if (SelectRootDef != scx->scx_use->cu_def)
    {
      if (SelectRootDef != NULL)
          SelectClear();
      SelectRootDef = scx->scx_use->cu_def;
      SelSetDisplay(SelectUse, SelectRootDef);
    }

    SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);

    /* Select paint. */

    (void) DBCellCopyAllPaint(scx, types, xMask, SelectUse);

    SelectDef->cd_types = *types;   /* Remember what types were requested */

    /* Select labels. */

    if (TTMaskHasType(types, L_LABEL))
        (void) DBCellCopyAllLabels(scx, &DBAllTypeBits, xMask,
            SelectUse, &labelArea);
    else (void) DBCellCopyAllLabels(scx, types, xMask, SelectUse, &labelArea);

    /* Select unexpanded cell uses. */

    if (TTMaskHasType(types, L_CELL))
        (void) DBCellCopyAllCells(scx, xMask, SelectUse, &cellArea);
    else
    {
      cellArea.r_xbot = 0;
      cellArea.r_xtop = -1;
    }

    /* Display the new selection. */

    (void) GeoIncludeAll(&scx->scx_area, &labelArea);
    (void) GeoIncludeAll(&cellArea, &labelArea);
    SelRememberForUndo(FALSE, SelectRootDef, &labelArea);
    DBReComputeBbox(SelectDef);
    DBWHLRedraw(SelectRootDef, &labelArea, TRUE);
    DBWAreaChanged(SelectDef, &SelectDef->cd_bbox, DBW_ALLWINDOWS,
      &DBAllButSpaceBits);
}


/*
 * ----------------------------------------------------------------------------
 *
 * selFindChunk --
 *
 *    This is a recursive procedure to find the largest chunk of
 *    material in a particular area.  It locates a rectangular
 *    area of given materials whose minimum dimension is as
 *    large as possible, and whose maximum dimension is also as
 *    large as possible (but minimum dimension is more important).
 *    Furthermore, the chunk must lie within a particular area and
 *    must contain a given area.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 * Notes:
 *    This routine is O(2^N) in the worst-case (caused by stair-stepped
 *    geometry at single-unit spacing).  Limiting the recursion level to
 *    22 causes the routine to fail to find the maximum-sized rectangle
 *    in such cases but also ensures a completion time of less than a
 *    second or so on a decent machine (e.g., 2GHz CPU).  The inability
 *    to find the maximum-area rectangle is not really such a big deal
 *    in these (rare) cases.  However, there must be better area-
 *    partitioning algorithms out there, and a replacement would be nice.
 *
 * ----------------------------------------------------------------------------
 */

#define MAX_RECURSION_LEVEL 22

void
selFindChunk(plane, wrongTypes, searchArea, containedArea, bestMin,
      bestMax, bestChunk, level)
    Plane *plane;             /* Plane on which to hunt for chunk. */
    TileTypeBitMask *wrongTypes;    /* Types that are not allowed to be
                               * part of the chunk.
                               */
    Rect *searchArea;               /* Largest allowable size for the
                               * chunk.  Note:  don't overestimate
                               * this or the procedure will take a
                               * long time!  (it processes every
                               * tile in this area).
                               */
    Rect *containedArea;            /* The chunk returned must contain
                               * this entire area.
                               */
    int *bestMin;             /* Largest minimum dimension seen so
                               * far: skip any chunks that can't
                               * match this.  Updated by this
                               * procedure.
                               */
    int *bestMax;             /* Largest maximum dimension seen
                               * so far.
                               */
    Rect *bestChunk;                /* Filled in with largest chunk seen
                               * so far, if we find one better than
                               * bestMin and bestMax.
                               */
    int level;                      /* Recursion level, to avoid deep
                               * recursion (this routine is worst-
                               * case O(2^N)!)
                               */
{
    Rect smaller;
    int min, max;
    extern int selChunkFunc();
    Rect wrong;

    if (level == MAX_RECURSION_LEVEL) return;

    /* If the search area is already smaller than the chunk to beat,
     * there's no point in even examining this chunk.
     */

    min = searchArea->r_xtop - searchArea->r_xbot;
    max = searchArea->r_ytop - searchArea->r_ybot;
    if (min > max)
    {
      int tmp;
      tmp = min; min = max; max = tmp;
    }

    if (min < *bestMin) return;
    if ((min == *bestMin) && (max <= *bestMax)) return;

    /* At each stage, search the area that's left for material of the
     * wrong type.
     */

    if (DBSrPaintArea((Tile *) NULL, plane, searchArea, wrongTypes,
          selChunkFunc, (ClientData) &wrong) == 0)
    {
      /* The area contains nothing but material of the right type,
       * so it is now the "chunk to beat".
       */

      *bestMin = min;
      *bestMax = max;
      *bestChunk = *searchArea;
      return;
    }

    if (SigInterruptPending)
      return;

    /* At this point the current search area contains some material of
     * the wrong type.  We have to reduce the search area to exclude this
     * material.  There are two ways that this can be done while still
     * producing areas that contain containedArea.  Try both of those,
     * and repeat the whole thing recursively on the smaller areas.
     */

    /* First, try reducing the x-range. */

    smaller = *searchArea;
    if (wrong.r_xbot >= containedArea->r_xtop)
      smaller.r_xtop = wrong.r_xbot;
    else if (wrong.r_xtop <= containedArea->r_xbot)
      smaller.r_xbot = wrong.r_xtop;
    else goto tryY;  /* Bad material overlaps containedArea in x. */
    selFindChunk(plane, wrongTypes, &smaller, containedArea,
          bestMin, bestMax, bestChunk, level + 1);
    

    /* Also try reducing the y-range to see if that works better. */

    tryY: smaller = *searchArea;
    if (wrong.r_ybot >= containedArea->r_ytop)
      smaller.r_ytop = wrong.r_ybot;
    else if (wrong.r_ytop <= containedArea->r_ybot)
      smaller.r_ybot = wrong.r_ytop;
    else return;  /* Bad material overlaps containedArea in y. */
    selFindChunk(plane, wrongTypes, &smaller, containedArea,
          bestMin, bestMax, bestChunk, level + 1);
}

#ifdef NONMANHATTAN
/* Search function to find split tiles in the area of interest.
 */

int
selSplitFunc(tile, cxp)
   Tile *tile;
   TreeContext *cxp;
{
    SearchContext *scx = cxp->tc_scx;
    Rect *area = (Rect *) cxp->tc_filter->tf_arg;
    Rect locarea;

    if (IsSplit(tile))
    {
        TiToRect(tile, &locarea);  
      GeoTransRect(&scx->scx_trans, &locarea, area);
        return 1;
    }
    return 0;
}

#endif

/* This procedure is called for each tile of the wrong type in an
 * area that is supposed to contain only tiles of other types.  It
 * just returns the area of the wrong material and aborts the search.
 */

int
selChunkFunc(tile, wrong)
    Tile *tile;               /* The offending tile. */
    Rect *wrong;        /* Place to store the tile's area. */
{
    TiToRect(tile, wrong);
    return 1;                 /* Abort the search. */
}


/*
 * ----------------------------------------------------------------------------
 *
 * SelectChunk --
 *
 *    This procedure selects a single rectangular chunk of 
 *    homogeneous material.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    More material is added to the select cell and displayed
 *    on the screen.  This procedure finds the largest rectangular
 *    chunk of material "type" that contains the area given in
 *    in scx.  The material need not all be in one cell, but it
 *    must all be in cells that are expanded according to "xMask".
 *    If pArea is given, the rectangle it points to is filled in
 *    with the area of the chunk that was selected.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectChunk(scx, type, xMask, pArea, less)
    SearchContext *scx;       /* Area to tree-search for material.  The
                         * transform must map to root coordinates
                         * of the edit cell.
                         */
    TileType type;            /* The type of material to be considered. */
    int xMask;                /* Indicates window (or windows) where cells
                         * must be expanded for their contents to be
                         * considered.  0 means treat everything as
                         * expanded.
                         */
    Rect *pArea;        /* If non-NULL, gets filled in with the area
                         * of the selection.
                         */
    bool less;
{
#define INITIALSIZE 10
    SearchContext newscx;
    TileTypeBitMask wrongTypes, typeMask;
    TileType ttype;
    Rect bestChunk;
    int bestMin, bestMax, width, height;

#ifdef NONMANHATTAN
    extern int selSplitFunc();            /* Forward reference. */
#endif

    /* If the source definition is changing, clear the old selection. */

    if (SelectRootDef != scx->scx_use->cu_def)
    {
      if (SelectRootDef != NULL)
          SelectClear();
      SelectRootDef = scx->scx_use->cu_def;
      SelSetDisplay(SelectUse, SelectRootDef);
    }

    /* The chunk is computed iteratively.  First extract a small
     * region (defined by INITIALSIZE) into Select2Def.  Then find
     * the largest chunk in the region.  If the chunk touches a
     * side of the region, then extract a larger region and try
     * again.  Keep making the region larger and larger until we
     * eventually find a region that completely contains the chunk
     * with space left over around the edges.
     */

    UndoDisable();
    TTMaskSetOnlyType(&typeMask, type);

    /* Stacked types containing "type" need to be added to the mask */
    if (DBIsContact(type)) DBMaskAddStacking(&typeMask);

    TTMaskCom2(&wrongTypes, &typeMask);

    newscx = *scx;

#ifdef NONMANHATTAN
    /* If the tile under the area of interest is a split tile,    */
    /* then we use that tile as the chunk.  This routine should   */
    /* work like the while() loop underneath, looking for the     */
    /* largest split tile, but this is a minor enhancement. */
    /* *However*, if it is done, the functions              */
    /* DBCellCopyManhattanPaint()/dbCopyManhattanPaint() should */
    /* be reconsidered.                               */

    if (DBTreeSrTiles(&newscx, &typeMask, 0, selSplitFunc,
          (ClientData)&bestChunk) != 0)
    {
        goto chunkdone;
    }

#endif

    bestMin = bestMax = 0;
    bestChunk = GeoNullRect;

    GEO_EXPAND(&newscx.scx_area, INITIALSIZE, &newscx.scx_area);
    while (TRUE)
    {
      /* Extract a bunch of junk. */

      DBCellClearDef(Select2Def);
#ifdef NONMANHATTAN
      DBCellCopyManhattanPaint(&newscx, &typeMask, xMask, Select2Use);
#else
      DBCellCopyAllPaint(&newscx, &typeMask, xMask, Select2Use);
#endif

      /* Now find the best chunk in the area. */

      selFindChunk(Select2Def->cd_planes[DBPlane(type)],
          &wrongTypes, &newscx.scx_area, &scx->scx_area,
          &bestMin, &bestMax, &bestChunk, 0);
      if (GEO_RECTNULL(&bestChunk))
      {
          /* No chunk was found, so return. */

          UndoEnable();
          if (pArea != NULL) *pArea = bestChunk;
          return;
      }

      /* If the chunk is completely inside the area we yanked, then we're
       * done.
       */
      
      if (GEO_SURROUND_STRONG(&newscx.scx_area, &bestChunk)) break;

      /* The chunk extends to the edge of the area.  Anyplace that the
       * chunk touches an edge, move that edge out by a factor of two.
       * Anyplace it doesn't touch, move the edge in to be just one
       * unit out from the chunk.
       */
      
      width = newscx.scx_area.r_xtop - newscx.scx_area.r_xbot;
      height = newscx.scx_area.r_ytop - newscx.scx_area.r_ybot;

      if (bestChunk.r_xbot == newscx.scx_area.r_xbot)
          newscx.scx_area.r_xbot -= width;
      else newscx.scx_area.r_xbot = bestChunk.r_xbot - 1;
      if (bestChunk.r_ybot == newscx.scx_area.r_ybot)
          newscx.scx_area.r_ybot -= height;
      else newscx.scx_area.r_ybot= bestChunk.r_ybot - 1;
      if (bestChunk.r_xtop == newscx.scx_area.r_xtop)
          newscx.scx_area.r_xtop += width;
      else newscx.scx_area.r_xtop = bestChunk.r_xtop + 1;
      if (bestChunk.r_ytop == newscx.scx_area.r_ytop)
          newscx.scx_area.r_ytop += height;
      else newscx.scx_area.r_ytop = bestChunk.r_ytop + 1;
    }

chunkdone:

    /* Flag the cell as a chunk selection so we can treat       */
    /* the contents differently when doing a move op.           */

    SelectUse->cu_flags |= CU_SELECT_CHUNK; 
    UndoEnable();
    if (less)
      {
      SelRemoveArea(&bestChunk, &typeMask);
      }
    else
      {
      newscx.scx_area = bestChunk;

      /* Remove any stacked contact types for SelectArea */
      if (DBIsContact(type))
          TTMaskSetOnlyType(&typeMask, type);

      SelectArea(&newscx, &typeMask, xMask);
      }

    if (pArea != NULL) *pArea = bestChunk;
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectRegion --
 *
 *    Select an entire region of material, no matter what its
 *    shape.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    This procedure traces out the region consisting entirely
 *    of type "type", and selects all that material.  The search
 *    starts from "type" material under scx and continues outward
 *    to get all material in all cells connected to the area under
 *    scx by material of type "type".  If pArea is specified, then
 *    the rectangle that it points to is filled in with the bounding
 *    box of the region that was selected.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectRegion(scx, type, xMask, pArea, less)
    SearchContext *scx;       /* Area to tree-search for material.  The
                         * transform must map to EditRoot coordinates.
                         */
    TileType type;            /* The type of material to be considered. */
    int xMask;                /* Indicates window (or windows) where cells
                         * must be expanded for their contents to be
                         * considered.  0 means treat everything as
                         * expanded.
                         */
    Rect *pArea;        /* If non-NULL, points to rectangle to be
                         * filled in with region's bounding box.
                         */
    bool less;
{
    TileTypeBitMask connections[TT_MAXTYPES];
    int i;
    SearchContext scx2;

    /* If the source definition is changing, clear the old selection. */

    if (SelectRootDef != scx->scx_use->cu_def)
    {
      if (SelectRootDef != NULL)
          SelectClear();
      SelectRootDef = scx->scx_use->cu_def;
      SelSetDisplay(SelectUse, SelectRootDef);
    }

    /* Set up a connection table that allows only a particular type
     * of material to be considered in the region.
     */

    for (i=0; i<DBNumTypes; i+=1)
      TTMaskZero(&connections[i]);
    TTMaskSetType(&connections[type], type);

    /* Clear out the temporary selection cell and yank all of the
     * connected paint into it.
     */

    UndoDisable();
    DBCellClearDef(Select2Def);
    DBTreeCopyConnect(scx, &connections[type], xMask, connections,
          &TiPlaneRect, Select2Use);
    UndoEnable();

    /* Now transfer what we found into the main selection cell.  Pick
     * up all the labels that correspond to the selected material.
     */
    
    SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
    if (less)
      {
      (void) SelRemoveSel2();
      }
    else
      {
      scx2.scx_use = Select2Use;
      scx2.scx_area = Select2Def->cd_bbox;
      scx2.scx_trans = GeoIdentityTransform;
      DBCellCopyAllPaint(&scx2, &DBAllButSpaceAndDRCBits,
                  0, SelectUse);
      DBCellCopyAllLabels(&scx2, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse,
                  (Rect *) NULL);
      }

    /* Display the new selection. */

    SelRememberForUndo(FALSE, SelectRootDef, &Select2Def->cd_bbox);

    DBReComputeBbox(SelectDef);
    DBComputeUseBbox(SelectUse);

    DBWHLRedraw(SelectRootDef, &Select2Def->cd_bbox, TRUE);
    DBWAreaChanged(SelectDef, &Select2Def->cd_bbox, DBW_ALLWINDOWS,
       &DBAllButSpaceBits);

    if (pArea != NULL) *pArea = Select2Def->cd_bbox;
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectNet --
 *
 *    This procedure selects an entire electrically-connected net.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Starting from material of type "type" under scx, this procedure
 *    finds and highlights all material in all expanded cells that
 *    is electrically-connected to the starting material through a
 *    chain of expanded cells.  If pArea is specified, then the
 *    rectangle that it points to is filled in with the bounding box
 *    of the net that was selected.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectNet(scx, type, xMask, pArea, less)
    SearchContext *scx;       /* Area to tree-search for material.  The
                         * transform must map to EditRoot coordinates.
                         */
    TileType type;            /* The type of material to be considered. */
    int xMask;                /* Indicates window (or windows) where cells
                         * must be expanded for their contents to be
                         * considered.  0 means treat everything as
                         * expanded.
                         */
    Rect *pArea;        /* If non-NULL, points to rectangle to be
                         * filled in with net's bounding box.
                         */
    bool less;                /* Whether to remove material from the
                         * selection using SelDeleteSel2
                         */
{
    TileTypeBitMask mask;
    SearchContext scx2;
    Point savePoint = scx->scx_area.r_ll;

    /* If the source definition is changing, clear the old selection. */

    if (SelectRootDef != scx->scx_use->cu_def)
    {
      if (SelectRootDef != NULL)
          SelectClear();
      SelectRootDef = scx->scx_use->cu_def;
      SelSetDisplay(SelectUse, SelectRootDef);
    }

    TTMaskZero(&mask);
    TTMaskSetType(&mask, type);

    /* Clear out the temporary selection cell and yank all of the
     * connected paint into it.
     */

    UndoDisable();
    DBCellClearDef(Select2Def);
    DBTreeCopyConnect(scx, &mask, xMask, DBConnectTbl,
          &TiPlaneRect, Select2Use);
    UndoEnable();

    /* Network undo method added by Nishit and Tim, July 8-10, 2004 */
    SelNetRememberForUndo(SelectRootDef, &savePoint, type, less, TRUE);

    /* Now transfer what we found into the main selection cell.  Pick
     * up all the labels that correspond to the selected material.
     */
    
    UndoDisable();
    if (less)
    {
      (void) SelRemoveSel2();
    }
    else
    {
      scx2.scx_use = Select2Use;
      scx2.scx_area = Select2Def->cd_bbox;
      scx2.scx_trans = GeoIdentityTransform;
      DBCellCopyAllPaint(&scx2, &DBAllButSpaceAndDRCBits,
                     0, SelectUse);
      DBCellCopyAllLabels(&scx2, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse,
                  (Rect *) NULL);
    }

    /* Set the cell use flags to mark this as a net selection,  */
    /* so we can treat it differently when it comes time to     */
    /* unselect it.                                   */

    SelectUse->cu_flags |= CU_SELECT_NET;
    UndoEnable(); 

    /* Display the newly-selected material. */
    DBReComputeBbox(SelectDef);
    DBComputeUseBbox(SelectUse);

    DBWHLRedraw(SelectRootDef, &Select2Def->cd_bbox, TRUE);
    DBWAreaChanged(SelectDef, &Select2Def->cd_bbox, DBW_ALLWINDOWS,
      &DBAllButSpaceBits);

    if (pArea != NULL) *pArea = Select2Def->cd_bbox;
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectCell --
 *
 *    Select a subcell by making a copy of it in the __SELECT__ cell.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The given use is copied into the selection.  If replace is TRUE,
 *    then the last subcell to be selected via this procedure is
 *    deselected.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectCell(use, rootDef, trans, replace)
    CellUse *use;       /* Cell use to be selected. */
    CellDef *rootDef;         /* Root definition of window in which selection
                         * is being made.
                         */
    Transform *trans;         /* Transform from the coordinates of use's
                         * definition to the coordinates of rootDef.
                         */
    bool replace;       /* TRUE means deselect the last cell selected
                         * by this procedure, if it's still selected.
                         */
{
    CellUse *newUse;

    /* If the source definition is changing, clear the old selection. */

    if (SelectRootDef != rootDef)
    {
      if (SelectRootDef != NULL)
          SelectClear();
      SelectRootDef = rootDef;
      SelSetDisplay(SelectUse, SelectRootDef);
    }

    /* Deselect the last cell selected, if requested. */

    if (replace && (selectLastUse != NULL))
    {
      Rect area;

      SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
      area = selectLastUse->cu_bbox;
      DBUnLinkCell(selectLastUse, SelectDef);
      DBDeleteCell(selectLastUse);
      (void) DBCellDeleteUse(selectLastUse);
      SelRememberForUndo(FALSE, SelectRootDef, &area);
      DBWHLRedraw(SelectRootDef, &area, TRUE);
    }

    /* When creating a new use, try to re-use the id from the old
     * one.  Only create a new one if the old id can't be used.
     */

    newUse = DBCellNewUse(use->cu_def, (char *) use->cu_id);
    if (!DBLinkCell(newUse, SelectDef))
    {
      freeMagic((char *) newUse->cu_id);
      newUse->cu_id = NULL;
      (void) DBLinkCell(newUse, SelectDef);
    }

    DBSetArray(use, newUse);
    DBSetTrans(newUse, trans);
    newUse->cu_expandMask = use->cu_expandMask;
    newUse->cu_flags = use->cu_flags;

    /* If this cell is already selected, there's nothing more to do.
     * Since we didn't change the selection here, be sure NOT to remember
     * it for future deselection!
     */

    if (DBCellFindDup(newUse, SelectDef) != NULL)
    {
      DBUnLinkCell(newUse, SelectDef);
      (void) DBCellDeleteUse(newUse);
      selectLastUse = (CellUse *) NULL;
      return;
    }

    SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
    DBPlaceCell(newUse, SelectDef);
    selectLastUse = newUse;

    SelRememberForUndo(FALSE, SelectRootDef, &newUse->cu_bbox);
    DBReComputeBbox(SelectDef);
    DBWHLRedraw(SelectRootDef, &newUse->cu_bbox, TRUE);
    DBWAreaChanged(SelectDef, &newUse->cu_bbox, DBW_ALLWINDOWS,
      &DBAllButSpaceBits);
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectAndCopy1 --
 *
 *    This procedure takes the contents of SelectDef,  and makes a
 *    copy of them in the edit cell.  Unlike SelectAndCopy2, the
 *    original selection is unchanged, and SelectDef is not cleared.
 *    This allows the implementation of "drag and drop" selections.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The selection is augmented with what's in Select2Def.  The caller
 *    should normally have cleared the selection before calling us.
 *    The edit cell is modified to include everything that was in
 *    Select2Def.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectAndCopy1()
{
    SearchContext scx;
    Rect editArea;

    /* Just copy the information in Select2Def twice, once into the
     * edit cell and once into the main selection cell.
     */
    
    scx.scx_use = SelectUse;
    scx.scx_area = SelectUse->cu_bbox;
    GeoTransTrans(&SelectUse->cu_transform, &RootToEditTransform, &scx.scx_trans);
    (void) DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_SPECIAL,
            EditCellUse);
    (void) DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, EditCellUse,
            (Rect *) NULL);
    (void) DBCellCopyAllCells(&scx, CU_DESCEND_SPECIAL, EditCellUse, (Rect *) NULL);
    GeoTransRect(&scx.scx_trans, &scx.scx_area, &editArea);
    DBAdjustLabels(EditCellUse->cu_def, &editArea);
    DBWAreaChanged(EditCellUse->cu_def, &editArea, DBW_ALLWINDOWS,
      (TileTypeBitMask *) NULL);
    DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editArea);
    DBReComputeBbox(EditCellUse->cu_def);
}

/*
 * ----------------------------------------------------------------------------
 *
 * SelectAndCopy2 --
 *
 *    This procedure is intended for use only within the selection
 *    module.  It takes what's in Select2Def, makes a copy of it in the
 *    edit cell, and makes the copy the selection.  It's used, for
 *    example, by the transformation and copying routines.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The selection is augmented with what's in Select2Def.  The caller
 *    should normally have cleared the selection before calling us.
 *    The edit cell is modified to include everything that was in
 *    Select2Def.
 *
 * ----------------------------------------------------------------------------
 */

void
SelectAndCopy2(newSourceDef)
    CellDef *newSourceDef;          /* The new selection is to be
                               * associated with this cell in the
                               * user's layout.
                               */
{
    SearchContext scx;
    Rect editArea, labelArea;
    int plane;
    void (*savedPaintPlane)();
    extern int selACPaintFunc();    /* Forward reference. */
    extern int selACCellFunc();

    /* Just copy the information in Select2Def twice, once into the
     * edit cell and once into the main selection cell.
     */
    
    scx.scx_use = Select2Use;
    scx.scx_area = Select2Use->cu_bbox;
    scx.scx_trans = RootToEditTransform;
    savedPaintPlane = DBNewPaintPlane(DBPaintPlaneActive);
    (void) DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_SPECIAL,
            EditCellUse);
    DBNewPaintPlane(savedPaintPlane);
    (void) DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, EditCellUse,
            (Rect *) NULL);
    (void) DBCellCopyAllCells(&scx, CU_DESCEND_SPECIAL, EditCellUse, (Rect *) NULL);
    GeoTransRect(&scx.scx_trans, &scx.scx_area, &editArea);

    DBAdjustLabels(EditCellUse->cu_def, &editArea);
    DBWAreaChanged(EditCellUse->cu_def, &editArea, DBW_ALLWINDOWS,
      (TileTypeBitMask *) NULL);
    DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editArea);
    DBReComputeBbox(EditCellUse->cu_def);

    SelectRootDef = newSourceDef;
    SelSetDisplay(SelectUse, SelectRootDef);

    SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
    scx.scx_trans = GeoIdentityTransform;

    /* In copying stuff into SelectUse, we have to be careful.  The problem
     * is that the stuff now in the edit cell may have switched layers.
     * (for example, Select2Def might have diff, which got painted
     * over poly in the edit cell to form transistor).  As a result, we
     * use Select2Def to figure out what areas of what planes to put into
     * SelectUse, but use the actual tile types from the edit cell.
     */
    
    for (plane = PL_SELECTBASE; plane < DBNumPlanes; plane++)
    {
      (void) DBSrPaintArea((Tile *) NULL, Select2Def->cd_planes[plane],
            &TiPlaneRect, &DBAllButSpaceAndDRCBits, selACPaintFunc,
            (ClientData) plane);
    }

    (void) DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse,
          &labelArea);

    /* We also have to be careful about copying subcells into the
     * main selection cell.  It might not have been possible to copy
     * a subcell into the edit cell (above), because the copying
     * would have formed a circularity.  In that case, we need to
     * drop that subcell from the new selection.  The code below just
     * copies those that are still in the edit cell.
     */

    (void) SelEnumCells(TRUE, (bool *) NULL, &scx, selACCellFunc,
          (ClientData) NULL);

    DBReComputeBbox(SelectDef);
    
    /* A little hack here:  don't do explicit redisplay of the selection,
     * or record a very large redisplay area for undo-ing.  It's not
     * necessary since the layout redisplay also redisplays the highlights.
     * If we do it too, then we're just double-displaying and wasting
     * time.  (note: must record something for undo-ing in order to get
     * SelectRootDef set right... just don't pass a redisplay area).
     */

    SelRememberForUndo(FALSE, SelectRootDef, (Rect *) NULL);
    DBWAreaChanged(SelectDef, &SelectDef->cd_bbox, DBW_ALLWINDOWS,
      (TileTypeBitMask *) NULL);
}

/* Structure to hold information about an edit area, which may be
 * a triangular region.
 */
01175 typedef struct {
    Rect *editClip;
    TileType ttype;
} acparg;

/* Utility function: for each tile, copy information over its area from
 * the given edit cell plane to SelectDef.  Always return 0 to keep the
 * search alive.
 */

int
selACPaintFunc(tile, plane)
    Tile *tile;               /* Tile in Select2Def. */
    int plane;                /* Index of plane this tile came from. */
{
    Rect area, editArea;
    acparg selACarg;
    int selACPaintFunc2();    /* Forward reference. */

    TiToRect(tile, &area);
    /* we want editClip in root coordinates. . . */
    selACarg.editClip = &area;
    GeoTransRect(&RootToEditTransform, &area, &editArea);

#ifndef NONMANHATTAN
    selACarg.ttype = TiGetType(tile);
#else
    selACarg.ttype = TiGetTypeExact(tile);

    if (IsSplit(tile))
    {
      DBSrPaintNMArea((Tile *) NULL, EditCellUse->cu_def->cd_planes[plane],
            selACarg.ttype, &editArea, &DBAllButSpaceAndDRCBits,
            selACPaintFunc2, (ClientData) &selACarg);
    }
    else
#endif
    (void) DBSrPaintArea((Tile *) NULL, EditCellUse->cu_def->cd_planes[plane],
          &editArea, &DBAllButSpaceAndDRCBits, selACPaintFunc2,
          (ClientData) &selACarg);
    return 0;
}

/* Second-level paint function:  just paint the overlap between
 * tile and editClip into SelectDef.
 *
 * This function is like dbCopyAllPaint() but differs just enough
 * that a separate function is required.  However, much of the code 
 * could be shared between the two functions if it were properly
 * broken out into subroutines.
 */

int
selACPaintFunc2(tile, selACarg)
    Tile *tile;               /* Tile in edit cell. */
    acparg *selACarg;         /* Contains edit-cell area to clip to
                         * before painting into selection.
                         */
{
    Rect *editClip = selACarg->editClip;
    Rect area, selArea;
    TileType type = TiGetTypeExact(tile);
    TileTypeBitMask tmask, *rmask;
    TileType ttype, rtype;

#ifdef NONMANHATTAN
    TileType dinfo = selACarg->ttype & (TT_DIAGONAL | TT_DIRECTION | TT_SIDE);
#endif

    TiToRect(tile, &area);
    GeoTransRect(&EditToRootTransform, &area, &selArea);

#ifdef NONMANHATTAN

    if (dinfo & TT_DIAGONAL)
    {
      /* If the select area is triangular, then we need to  */
      /* clip in a more complicated manner.                 */

      Point points[5];
      Rect rrect, orect;
      int np, i, j;

      ttype = (selACarg->ttype & TT_SIDE) ? ((ttype & TT_RIGHTMASK) >> 14) :
            ttype & TT_LEFTMASK;

      if (type & TT_DIAGONAL)
          rtype = (type & TT_SIDE) ? SplitRightType(tile) :
                  SplitLeftType(tile);
      else
          rtype = type;

      if (rtype >= DBNumUserLayers)
      {
          rmask = DBResidueMask(rtype);
          if (TTMaskHasType(rmask, ttype))
            rtype = ttype;
      }

      TTMaskSetOnlyType(&tmask, rtype);

      type = (dinfo & TT_SIDE) ? (rtype << 14) : rtype;
      type |= dinfo;

      GrClipTriangle(editClip, &selArea, TRUE, dinfo, points, &np);

      if (np == 0)
          return 0;
      if (np >= 3)
      {
          for (i = 0; i < np; i++)
          {
            j = (i + 1) % np;
            if (points[i].p_x != points[j].p_x && points[i].p_y !=
                        points[j].p_y)
            {
                /* Break out the triangle */
                rrect.r_xbot = points[i].p_x;
                rrect.r_xtop = points[j].p_x;
                rrect.r_ybot = points[i].p_y;
                rrect.r_ytop = points[j].p_y;
                GeoCanonicalRect(&rrect, &selArea);
                break;
            }
          }
          if (i == np)  /* Exactly one Manhattan rectangle */
          {
            rrect.r_xbot = points[0].p_x;
            rrect.r_xtop = points[2].p_x;
            rrect.r_ybot = points[0].p_y;
            rrect.r_ytop = points[2].p_y;
            GeoCanonicalRect(&rrect, &selArea);
            type = rtype;
          }
          else if (np >= 4) /* Process extra rectangles in the area */
          {
            /* "orect" is the bounding box of the polygon returned      */
            /* by ClipTriangle.                             */

            orect.r_xtop = orect.r_xbot = points[0].p_x;
            orect.r_ytop = orect.r_ybot = points[0].p_y;
            for (i = 0; i < np; i++)
                GeoIncludePoint(&points[i], &orect);

            /* Rectangle to left or right */
            rrect.r_ybot = orect.r_ybot;
            rrect.r_ytop = orect.r_ytop;
            if (selArea.r_xbot > orect.r_xbot)
            {
                rrect.r_xbot = orect.r_xbot;
                rrect.r_xtop = selArea.r_xbot;
            }
            else if (selArea.r_xtop < orect.r_xtop)
            {
                rrect.r_xtop = orect.r_xtop;
                rrect.r_xbot = selArea.r_xtop;
            }
            else
                goto topbottom;

            DBPaintValid(SelectDef, &rrect, &tmask, 0);
topbottom:
            /* Rectangle to top or bottom */
            rrect.r_xbot = selArea.r_xbot;
            rrect.r_xtop = selArea.r_xtop;
            if (selArea.r_ybot > orect.r_ybot)
            {
                rrect.r_ybot = orect.r_ybot;
                rrect.r_ytop = selArea.r_ybot;
            }
            else if (selArea.r_ytop < orect.r_ytop)
            {
                rrect.r_ytop = orect.r_ytop;
                rrect.r_ybot = selArea.r_ytop;
            }
            else
                goto splitdone;

            DBPaintValid(SelectDef, &rrect, &tmask, 0);
          }
      }
    }
    else
    {
      ttype = selACarg->ttype;

      if (type & TT_DIAGONAL)
          rtype = (type & TT_SIDE) ? SplitRightType(tile) : SplitLeftType(tile);
      else
          rtype = type;

      if (rtype >= DBNumUserLayers)
      {
          rmask = DBResidueMask(rtype);
          if (TTMaskHasType(rmask, ttype))
            rtype = ttype;
      }
      TTMaskSetOnlyType(&tmask, rtype);
    }
#else /* !NONMANHATTAN */

    ttype = selACarg->ttype;
    if (type >= DBNumUserLayers)
    {
      rmask = DBResidueMask(type);
      if (TTMaskHasType(rmask, ttype))
          type = ttype;
    }
    TTMaskSetOnlyType(&tmask, type);

#endif

    GeoClip(&selArea, editClip);    /* editClip already in root coords */

splitdone:
    DBPaintValid(SelectDef, &selArea, &tmask, type);
    return 0;
}

/* Cell search function:  invoked for each subcell in Select2Def that's
 * also in the edit cell.  Make a copy of the cell in SelectDef.
 */

int
selACCellFunc(selUse, realUse)
    CellUse *selUse;          /* Use to be copied into SelectDef.  This
                         * is the instance inside Select2Def.
                         */
    CellUse *realUse;         /* The cellUse (in the edit cell) corresponding
                         * to selUse.  We need this in order to use its
                         * instance id and expand mask in the selection.
                         */
{
    CellUse *newUse;

    newUse = DBCellNewUse(selUse->cu_def, realUse->cu_id);
    if (!DBLinkCell(newUse, SelectDef))
    {
      freeMagic((char *) newUse->cu_id);
      newUse->cu_id = NULL;
      (void) DBLinkCell(newUse, SelectDef);
    }
    newUse->cu_expandMask = realUse->cu_expandMask;
    newUse->cu_flags = realUse->cu_flags;
    DBSetArray(selUse, newUse);
    DBSetTrans(newUse, &selUse->cu_transform);
    DBPlaceCell(newUse, SelectDef);
    return 0;
}


Generated by  Doxygen 1.6.0   Back to index