2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* selOps.c --
|
|
|
|
|
*
|
|
|
|
|
* This file contains top-level procedures to manipulate the selection,
|
|
|
|
|
* e.g. to delete it, move it, etc.
|
|
|
|
|
*
|
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
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/select/selOps.c,v 1.11 2010/08/22 21:58:26 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2021-03-21 17:02:43 +01:00
|
|
|
#include <string.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
2022-01-01 04:19:11 +01:00
|
|
|
#include "utils/stack.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "utils/main.h"
|
|
|
|
|
#include "select/select.h"
|
|
|
|
|
#include "select/selInt.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "utils/undo.h"
|
|
|
|
|
#include "plow/plow.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "drc/drc.h"
|
|
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "commands/commands.h"
|
|
|
|
|
#include "netmenu/netmenu.h"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* The following variables are shared between SelectStretch and the
|
|
|
|
|
* search functions that it causes to be invoked.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int selStretchX, selStretchY; /* Stretch distances. Only one should
|
|
|
|
|
* ever be non-zero.
|
|
|
|
|
*/
|
|
|
|
|
static TileType selStretchType; /* Type of material being stretched. */
|
2021-03-04 20:00:31 +01:00
|
|
|
unsigned char SelectDoLabels = SEL_DO_LABELS; /* Whether or not to select subcell labels */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
typedef struct planeAndArea
|
|
|
|
|
{
|
|
|
|
|
int pa_plane; /* Plane of interest */
|
|
|
|
|
Rect *pa_area; /* Area affected */
|
|
|
|
|
TileTypeBitMask *pa_mask; /* Mask used in plane search */
|
|
|
|
|
} planeAndArea;
|
|
|
|
|
|
|
|
|
|
/* The following structure type is used to build up a list of areas
|
|
|
|
|
* to be painted. It's used to save information while a search of
|
|
|
|
|
* the edit cell is in progress: can't do the paints until the
|
|
|
|
|
* search has finished.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef struct stretchArea
|
|
|
|
|
{
|
|
|
|
|
Rect sa_area; /* Area to be painted. */
|
|
|
|
|
TileType sa_type; /* Type of material to paint. */
|
|
|
|
|
struct stretchArea *sa_next; /* Next element in list. */
|
|
|
|
|
} StretchArea;
|
|
|
|
|
|
|
|
|
|
static StretchArea *selStretchList; /* List of areas to paint. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectDelete --
|
|
|
|
|
*
|
|
|
|
|
* Delete everything in the edit cell that's selected.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Stuff is removed from the edit cell. If there's selected
|
|
|
|
|
* stuff that isn't in the edit cell, the user is warned.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectDelete(msg, do_clear)
|
|
|
|
|
char *msg; /* Some information to print in error messages.
|
|
|
|
|
* For example, if called as part of a move procedure,
|
|
|
|
|
* supply "moved". This will appear in messages of
|
|
|
|
|
* the form "only edit cell information was moved".
|
|
|
|
|
*/
|
|
|
|
|
bool do_clear; /* If TRUE, clear the select def before returning. */
|
|
|
|
|
{
|
|
|
|
|
bool nonEdit;
|
|
|
|
|
Rect editArea;
|
|
|
|
|
|
|
|
|
|
extern int selDelPaintFunc(), selDelCellFunc(), selDelLabelFunc();
|
|
|
|
|
|
2021-10-28 21:57:17 +02:00
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("The current cell is not editable.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
(void) SelEnumPaint(&DBAllButSpaceAndDRCBits, TRUE, &nonEdit,
|
|
|
|
|
selDelPaintFunc, (ClientData) NULL);
|
|
|
|
|
if (nonEdit)
|
|
|
|
|
{
|
|
|
|
|
TxError("You selected paint outside the edit cell. Only\n");
|
|
|
|
|
TxError(" the paint in the edit cell was %s.\n", msg);
|
|
|
|
|
}
|
|
|
|
|
(void) SelEnumCells(TRUE, &nonEdit, (SearchContext *) NULL,
|
|
|
|
|
selDelCellFunc, (ClientData) NULL);
|
|
|
|
|
if (nonEdit)
|
|
|
|
|
{
|
|
|
|
|
TxError("You selected one or more subcells that aren't children\n");
|
|
|
|
|
TxError(" of the edit cell. Only those in the edit cell were\n");
|
|
|
|
|
TxError(" %s.\n", msg);
|
|
|
|
|
}
|
|
|
|
|
(void) SelEnumLabels(&DBAllTypeBits, TRUE, &nonEdit,
|
|
|
|
|
selDelLabelFunc, (ClientData) NULL);
|
|
|
|
|
if (nonEdit)
|
|
|
|
|
{
|
|
|
|
|
TxError("You selected one or more labels that aren't in the\n");
|
|
|
|
|
TxError(" edit cell. Only the label(s) in the edit cell\n");
|
|
|
|
|
TxError(" were %s.\n", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBReComputeBbox(EditCellUse->cu_def);
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &SelectDef->cd_extended, &editArea);
|
|
|
|
|
DBWAreaChanged(EditCellUse->cu_def, &editArea, DBW_ALLWINDOWS,
|
|
|
|
|
(TileTypeBitMask *) NULL);
|
|
|
|
|
DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editArea);
|
|
|
|
|
if (do_clear) SelectClear();
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 21:25:42 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectDeleteUses --
|
|
|
|
|
*
|
|
|
|
|
* Delete all cell uses in the edit cell that are selected.
|
|
|
|
|
* This function is used by "flatten -doinplace" when operating
|
|
|
|
|
* on a selection. Since selected instances are flattened, the
|
|
|
|
|
* instances need to be deleted from the cell. This routine is
|
|
|
|
|
* the same as SelectDelete() without the paint and label delete
|
|
|
|
|
* functions.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Cell uses are removed from the edit cell. If there are selected
|
|
|
|
|
* uses that aren't in the edit cell, the user is warned.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectDeleteUses(msg, do_clear)
|
|
|
|
|
char *msg; /* Some information to print in error messages.
|
|
|
|
|
* For example, if called as part of a move procedure,
|
|
|
|
|
* supply "moved". This will appear in messages of
|
|
|
|
|
* the form "only edit cell information was moved".
|
|
|
|
|
*/
|
|
|
|
|
bool do_clear; /* If TRUE, clear the select def before returning. */
|
|
|
|
|
{
|
|
|
|
|
bool nonEdit;
|
|
|
|
|
Rect editArea;
|
|
|
|
|
|
|
|
|
|
extern int selDelPaintFunc(), selDelCellFunc(), selDelLabelFunc();
|
|
|
|
|
|
|
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("The current cell is not editable.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
(void) SelEnumCells(TRUE, &nonEdit, (SearchContext *) NULL,
|
|
|
|
|
selDelCellFunc, (ClientData) NULL);
|
|
|
|
|
if (nonEdit)
|
|
|
|
|
{
|
|
|
|
|
TxError("You selected one or more subcells that aren't children\n");
|
|
|
|
|
TxError(" of the edit cell. Only those in the edit cell were\n");
|
|
|
|
|
TxError(" %s.\n", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBReComputeBbox(EditCellUse->cu_def);
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &SelectDef->cd_extended, &editArea);
|
|
|
|
|
DBWAreaChanged(EditCellUse->cu_def, &editArea, DBW_ALLWINDOWS,
|
|
|
|
|
(TileTypeBitMask *) NULL);
|
|
|
|
|
DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editArea);
|
|
|
|
|
if (do_clear) SelectClear();
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Search function to delete paint. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selDelPaintFunc(rect, type)
|
|
|
|
|
Rect *rect; /* Area of paint, in root coords. */
|
|
|
|
|
TileType type; /* Type of paint to delete. */
|
|
|
|
|
{
|
|
|
|
|
Rect editRect;
|
|
|
|
|
TileTypeBitMask tmask;
|
|
|
|
|
TileType dinfo;
|
|
|
|
|
|
|
|
|
|
/* Change diagonal side & direction according to the transform */
|
|
|
|
|
|
|
|
|
|
if (type & TT_DIAGONAL)
|
|
|
|
|
{
|
|
|
|
|
dinfo = DBTransformDiagonal(type, &RootToEditTransform);
|
2020-05-23 23:13:14 +02:00
|
|
|
TTMaskSetOnlyType(&tmask, type & TT_LEFTMASK);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dinfo = 0;
|
2020-05-23 23:13:14 +02:00
|
|
|
TTMaskSetOnlyType(&tmask, type);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GeoTransRect(&RootToEditTransform, rect, &editRect);
|
|
|
|
|
|
|
|
|
|
DBEraseValid(EditCellUse->cu_def, &editRect, &tmask, dinfo);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function to delete subcell uses. */
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
selDelCellFunc(selUse, use)
|
|
|
|
|
CellUse *selUse; /* Not used. */
|
|
|
|
|
CellUse *use; /* What to delete. */
|
|
|
|
|
{
|
|
|
|
|
if (use->cu_flags & CU_LOCKED) return 0;
|
|
|
|
|
|
|
|
|
|
DBUnLinkCell(use, use->cu_parent);
|
|
|
|
|
DBDeleteCell(use);
|
|
|
|
|
(void) DBCellDeleteUse(use);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Search function to delete labels. Delete any label at the right
|
|
|
|
|
* place with the right name, regardless of layer attachment, because
|
|
|
|
|
* the selection can differ from the edit cell in this regard. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selDelLabelFunc(label)
|
|
|
|
|
Label *label; /* Label to delete. */
|
|
|
|
|
{
|
|
|
|
|
DBEraseLabelsByContent(EditCellUse->cu_def, &label->lab_rect, -1,
|
|
|
|
|
label->lab_text);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectCopy --
|
|
|
|
|
*
|
|
|
|
|
* This procedure makes a copy of the selection.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The selection is copied, with the copy being transformed by
|
|
|
|
|
* "transform" relative to the current selection. The copy is
|
|
|
|
|
* made the new selection.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectCopy(transform)
|
|
|
|
|
Transform *transform; /* How to displace the copy relative
|
|
|
|
|
* to the original. This displacement
|
|
|
|
|
* is given in root coordinates.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
|
2022-04-01 02:02:12 +02:00
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("The current cell is not editable.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Copy from SelectDef to Select2Def while transforming, then
|
|
|
|
|
* let SelectAndCopy2 do the rest of the work. Don't record
|
|
|
|
|
* anything involving Select2Def for undo-ing.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
UndoDisable();
|
|
|
|
|
DBCellClearDef(Select2Def);
|
|
|
|
|
scx.scx_use = SelectUse;
|
|
|
|
|
scx.scx_area = SelectUse->cu_bbox;
|
|
|
|
|
GeoTransTrans(transform, &SelectUse->cu_transform, &scx.scx_trans);
|
|
|
|
|
(void) DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_NO_LOCK,
|
|
|
|
|
Select2Use);
|
|
|
|
|
(void) DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_NO_LOCK, Select2Use,
|
|
|
|
|
(Rect *) NULL);
|
|
|
|
|
(void) DBCellCopyAllCells(&scx, CU_DESCEND_NO_LOCK, Select2Use, (Rect *) NULL);
|
2025-03-26 19:45:46 +01:00
|
|
|
|
|
|
|
|
/* The selection now has the same instance names as the cell def. If
|
|
|
|
|
* copies are made, then there will be name conflicts, and the original
|
|
|
|
|
* instance may be the one renamed. The sensible thing to do is to make
|
|
|
|
|
* sure that all conflicts are resolved within the select CellDef before
|
|
|
|
|
* the copy is made.
|
|
|
|
|
*/
|
|
|
|
|
DBSelectionUniqueIds(Select2Def, EditRootDef);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
DBReComputeBbox(Select2Def);
|
|
|
|
|
UndoEnable();
|
2025-03-26 19:45:46 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
SelectClear();
|
|
|
|
|
SelectAndCopy2(EditRootDef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectFlat --
|
|
|
|
|
*
|
|
|
|
|
* This procedure copies the selection into Select2Def, flattening
|
|
|
|
|
* it as it copies, then copies the result back into the selection
|
|
|
|
|
* cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The selection is flattened. No changes are made in the edit cell.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectFlat()
|
|
|
|
|
{
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
|
|
|
|
|
/* Copy from SelectDef to Select2Def while transforming, then
|
|
|
|
|
* let SelectAndCopy2 do the rest of the work. Don't record
|
|
|
|
|
* anything involving Select2Def for undo-ing.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
UndoDisable();
|
|
|
|
|
DBCellClearDef(Select2Def);
|
|
|
|
|
scx.scx_use = SelectUse;
|
|
|
|
|
scx.scx_area = SelectUse->cu_bbox;
|
|
|
|
|
GeoTransTrans(&GeoIdentityTransform, &SelectUse->cu_transform, &scx.scx_trans);
|
|
|
|
|
DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_ALL, Select2Use);
|
|
|
|
|
FlatCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_ALL, Select2Use);
|
|
|
|
|
DBReComputeBbox(Select2Def);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
|
|
|
|
|
/* Move everything from Select2 to Select */
|
|
|
|
|
|
|
|
|
|
SelectClear();
|
|
|
|
|
SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
|
|
|
|
|
|
|
|
|
|
scx.scx_use = Select2Use;
|
|
|
|
|
scx.scx_area = Select2Use->cu_bbox;
|
|
|
|
|
GeoTransTrans(&GeoIdentityTransform, &Select2Use->cu_transform, &scx.scx_trans);
|
|
|
|
|
|
|
|
|
|
DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_SPECIAL,
|
|
|
|
|
SelectUse);
|
|
|
|
|
DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, SelectUse,
|
|
|
|
|
(Rect *)NULL);
|
2022-02-16 17:28:06 +01:00
|
|
|
DBReComputeBbox(SelectDef);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
SelRememberForUndo(FALSE, SelectRootDef, &SelectUse->cu_bbox);
|
|
|
|
|
|
|
|
|
|
/* Redisplay the select cell */
|
|
|
|
|
DBWHLRedraw(SelectRootDef, &SelectDef->cd_extended, TRUE);
|
|
|
|
|
DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS,
|
|
|
|
|
&DBAllButSpaceBits);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-06 19:29:43 +01:00
|
|
|
/* Structure used by selShortTileProc() below to save a cost and Tile pointer */
|
|
|
|
|
|
|
|
|
|
typedef struct _shortsearchdata {
|
|
|
|
|
int cost;
|
|
|
|
|
Tile *tile;
|
|
|
|
|
} ShortSearchData;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* selShortTileProc --
|
|
|
|
|
*
|
|
|
|
|
* Callback function for DBSrPaintArea in selShortFindReverse(). When
|
|
|
|
|
* checking connected types on other planes, record the tile with the
|
|
|
|
|
* minimum cost.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selShortTileProc(tile, ssd)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
ShortSearchData *ssd;
|
|
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
const int curr = (int)TiGetClientINT(tile);
|
|
|
|
|
if (curr < ssd->cost)
|
2022-01-06 19:29:43 +01:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
ssd->cost = curr;
|
2022-01-06 19:29:43 +01:00
|
|
|
ssd->tile = tile;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2022-01-03 22:00:31 +01:00
|
|
|
* selShortFindReverse --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
2022-01-03 22:00:31 +01:00
|
|
|
* Trace back through a path found by selShortFindForward from destination
|
2017-04-25 14:41:48 +02:00
|
|
|
* to source, picking the lowest cost return path, and adding each tile
|
|
|
|
|
* found to the linked list.
|
|
|
|
|
*
|
2022-01-03 22:00:31 +01:00
|
|
|
* Algorithm notes (for this and selShortFindForward): Note that by not
|
2017-04-25 14:41:48 +02:00
|
|
|
* using one of the standard database search routines, TT_SIDE is NOT
|
|
|
|
|
* set on any tile. To find out what side we're looking at, we keep
|
|
|
|
|
* a record of what direction we were traveling from the previous
|
|
|
|
|
* tile. Given this and the diagonal direction, we know the two
|
|
|
|
|
* valid sides to search.
|
|
|
|
|
*
|
2022-01-01 04:19:11 +01:00
|
|
|
* Return value:
|
|
|
|
|
* 0 search completed successfully
|
|
|
|
|
* 1 search terminated with an error
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2022-01-03 22:00:31 +01:00
|
|
|
selShortFindReverse(rlist, tile, pnum, fdir)
|
2022-01-01 04:19:11 +01:00
|
|
|
ExtRectList **rlist;
|
2017-04-25 14:41:48 +02:00
|
|
|
Tile *tile;
|
|
|
|
|
int pnum;
|
|
|
|
|
int fdir;
|
|
|
|
|
{
|
|
|
|
|
Tile *tp, *mintp;
|
|
|
|
|
ExtRectList *newrrec;
|
2022-01-06 19:29:43 +01:00
|
|
|
int mincost, minp, p, mindir;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType ttype;
|
|
|
|
|
|
2022-01-06 19:29:43 +01:00
|
|
|
mindir = fdir;
|
2025-02-21 18:54:11 +01:00
|
|
|
mincost = (int)TiGetClientINT(tile);
|
2022-01-06 19:29:43 +01:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
while (TRUE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-01 04:19:11 +01:00
|
|
|
newrrec = mallocMagic(sizeof(ExtRectList));
|
|
|
|
|
|
|
|
|
|
if (IsSplit(tile))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-01 04:19:11 +01:00
|
|
|
newrrec->r_type = TiGetTypeExact(tile) & ~TT_SIDE;
|
|
|
|
|
switch(fdir)
|
|
|
|
|
{
|
|
|
|
|
case GEO_NORTH:
|
|
|
|
|
ttype = SplitBottomType(tile);
|
|
|
|
|
if (!SplitDirection(tile)) newrrec->r_type |= TT_SIDE;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_SOUTH:
|
|
|
|
|
ttype = SplitTopType(tile);
|
|
|
|
|
if (SplitDirection(tile)) newrrec->r_type |= TT_SIDE;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_EAST:
|
2017-04-25 14:41:48 +02:00
|
|
|
ttype = SplitLeftType(tile);
|
2022-01-01 04:19:11 +01:00
|
|
|
break;
|
|
|
|
|
case GEO_WEST:
|
|
|
|
|
ttype = SplitRightType(tile);
|
2017-04-25 14:41:48 +02:00
|
|
|
newrrec->r_type |= TT_SIDE;
|
2022-01-01 04:19:11 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ttype = SplitRightType(tile);
|
|
|
|
|
if (ttype == TT_SPACE)
|
|
|
|
|
ttype = SplitLeftType(tile);
|
|
|
|
|
else
|
|
|
|
|
newrrec->r_type |= TT_SIDE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ttype = TiGetTypeExact(tile);
|
|
|
|
|
newrrec->r_type = ttype;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Add this tile (area and type) to the linked list */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
TiToRect(tile, &newrrec->r_r);
|
|
|
|
|
newrrec->r_next = *rlist;
|
|
|
|
|
*rlist = newrrec;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-02-21 18:54:11 +01:00
|
|
|
if ((int)TiGetClientINT(tile) == 0) return 0; /* We're done */
|
2022-01-06 19:29:43 +01:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
minp = pnum;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search top */
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (fdir == GEO_NORTH) goto leftside;
|
|
|
|
|
else if (SplitDirection(tile) && fdir == GEO_EAST) goto leftside;
|
|
|
|
|
else if (!SplitDirection(tile) && fdir == GEO_WEST) goto leftside;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
|
|
|
if (ticlient == CLIENTDEFAULT) continue;
|
|
|
|
|
const int curr = (int)CD2INT(ticlient);
|
|
|
|
|
if (curr < mincost)
|
2022-01-01 04:19:11 +01:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
mincost = curr;
|
2022-01-01 04:19:11 +01:00
|
|
|
mintp = tp;
|
|
|
|
|
mindir = GEO_NORTH;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search left */
|
2017-04-25 14:41:48 +02:00
|
|
|
leftside:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (fdir == GEO_WEST) goto bottomside;
|
|
|
|
|
else if (SplitDirection(tile) && fdir == GEO_SOUTH) goto bottomside;
|
|
|
|
|
else if (!SplitDirection(tile) && fdir == GEO_NORTH) goto bottomside;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
|
|
|
if (ticlient == CLIENTDEFAULT) continue;
|
|
|
|
|
const int curr = (int)CD2INT(ticlient);
|
|
|
|
|
if (curr < mincost)
|
2022-01-01 04:19:11 +01:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
mincost = curr;
|
2022-01-01 04:19:11 +01:00
|
|
|
mintp = tp;
|
|
|
|
|
mindir = GEO_WEST;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search bottom */
|
2017-04-25 14:41:48 +02:00
|
|
|
bottomside:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (fdir == GEO_SOUTH) goto rightside;
|
|
|
|
|
else if (SplitDirection(tile) && fdir == GEO_WEST) goto rightside;
|
|
|
|
|
else if (!SplitDirection(tile) && fdir == GEO_EAST) goto rightside;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
|
|
|
if (ticlient == CLIENTDEFAULT) continue;
|
|
|
|
|
const int curr = (int)CD2INT(ticlient);
|
|
|
|
|
if (curr < mincost)
|
2022-01-01 04:19:11 +01:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
mincost = curr;
|
2022-01-01 04:19:11 +01:00
|
|
|
mintp = tp;
|
|
|
|
|
mindir = GEO_SOUTH;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search right */
|
2017-04-25 14:41:48 +02:00
|
|
|
rightside:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (fdir == GEO_EAST) goto donesides;
|
|
|
|
|
else if (SplitDirection(tile) && fdir == GEO_NORTH) goto donesides;
|
|
|
|
|
else if (!SplitDirection(tile) && fdir == GEO_SOUTH) goto donesides;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
const ClientData ticlient = TiGetClient(tp);
|
|
|
|
|
if (ticlient == CLIENTDEFAULT) continue;
|
|
|
|
|
const int curr = (int)CD2INT(ticlient);
|
|
|
|
|
if (curr < mincost)
|
2022-01-01 04:19:11 +01:00
|
|
|
{
|
2025-02-21 18:57:13 +01:00
|
|
|
mincost = curr;
|
2022-01-01 04:19:11 +01:00
|
|
|
mintp = tp;
|
|
|
|
|
mindir = GEO_EAST;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search other connecting planes */
|
2017-04-25 14:41:48 +02:00
|
|
|
donesides:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (DBIsContact(ttype))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-06 19:29:43 +01:00
|
|
|
ShortSearchData ssd;
|
2022-01-01 04:19:11 +01:00
|
|
|
PlaneMask pmask;
|
|
|
|
|
|
|
|
|
|
pmask = DBConnPlanes[ttype];
|
2022-01-06 19:29:43 +01:00
|
|
|
|
|
|
|
|
ssd.cost = mincost;
|
|
|
|
|
ssd.tile = (Tile *)NULL;
|
2022-01-01 04:19:11 +01:00
|
|
|
for (p = PL_TECHDEPBASE; p < DBNumPlanes; p++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-01 04:19:11 +01:00
|
|
|
if (PlaneMaskHasPlane(pmask, p) && (p != pnum))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-06 19:29:43 +01:00
|
|
|
DBSrPaintArea((Tile *)NULL, SelectDef->cd_planes[p],
|
|
|
|
|
&newrrec->r_r, &DBAllButSpaceAndDRCBits,
|
|
|
|
|
selShortTileProc, &ssd);
|
|
|
|
|
if (ssd.cost < mincost)
|
2022-01-01 04:19:11 +01:00
|
|
|
{
|
2022-01-06 19:29:43 +01:00
|
|
|
mincost = ssd.cost;
|
|
|
|
|
mintp = ssd.tile;
|
2022-01-01 04:19:11 +01:00
|
|
|
minp = p;
|
|
|
|
|
mindir = GEO_CENTER;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
|
|
|
|
|
/* If mincost is still set to INT_MAX we have a real serious problem! */
|
|
|
|
|
if (mincost == INT_MAX) return 1;
|
|
|
|
|
|
2022-01-06 19:29:43 +01:00
|
|
|
/* Failsafe: Avoid infinite recursion */
|
2025-02-21 18:54:11 +01:00
|
|
|
if ((tile == mintp) || (TiGetClient(tile) == CLIENTDEFAULT))
|
2022-01-06 19:29:43 +01:00
|
|
|
{
|
|
|
|
|
TxError("Failed to trace back shorting path.\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
|
|
|
|
|
/* Now we have the minimum cost neighboring tile; continue search with it */
|
|
|
|
|
tile = mintp;
|
|
|
|
|
pnum = minp;
|
|
|
|
|
fdir = mindir;
|
2022-01-03 23:49:54 +01:00
|
|
|
|
2022-11-02 14:40:20 +01:00
|
|
|
/* Diagnostic */
|
2022-01-06 19:29:43 +01:00
|
|
|
// TxPrintf("Cost = %d Tile @ %d %d plane %d dir %d\n",
|
|
|
|
|
// mincost, tile->ti_ll.p_x, tile->ti_ll.p_y, pnum, fdir);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
2022-01-01 04:19:11 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
/* Data structure used by selShortFindForward() to store a tile and */
|
2022-01-01 04:19:11 +01:00
|
|
|
/* the current search parameters, including cost, direction, plane, */
|
|
|
|
|
/* and the mask of connecting types. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
typedef struct _shortdata {
|
|
|
|
|
int cost;
|
|
|
|
|
Tile *tile;
|
2022-01-03 22:00:31 +01:00
|
|
|
TileType type;
|
2022-01-01 04:19:11 +01:00
|
|
|
int pnum;
|
|
|
|
|
} ShortData;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* NewSD --
|
|
|
|
|
*
|
|
|
|
|
* Create and populate a ShortData structure.
|
|
|
|
|
*
|
|
|
|
|
* Return value:
|
|
|
|
|
* A pointer to an allocated and populated ShortData structure.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
ShortData *NewSD(cost, tile, type, pnum)
|
2022-01-01 04:19:11 +01:00
|
|
|
int cost;
|
|
|
|
|
Tile *tile;
|
2022-01-03 22:00:31 +01:00
|
|
|
TileType type;
|
2022-01-01 04:19:11 +01:00
|
|
|
int pnum;
|
|
|
|
|
{
|
|
|
|
|
ShortData *sd;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
sd = (ShortData *)mallocMagic(sizeof(ShortData));
|
|
|
|
|
|
|
|
|
|
sd->cost = cost;
|
|
|
|
|
sd->tile = tile;
|
2022-01-03 22:00:31 +01:00
|
|
|
sd->type = type;
|
2022-01-01 04:19:11 +01:00
|
|
|
sd->pnum = pnum;
|
|
|
|
|
|
|
|
|
|
return sd;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
2022-01-03 22:00:31 +01:00
|
|
|
* selShortProcessTile --
|
|
|
|
|
*
|
|
|
|
|
* Process a tile, finding if it extends the connection from the
|
|
|
|
|
* last position, and setting its cost to be one higher than the
|
|
|
|
|
* previous position.
|
|
|
|
|
*
|
|
|
|
|
* Return value:
|
|
|
|
|
* 0 if tile was updated with new cost.
|
|
|
|
|
* 1 if tile was unchanged.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Writes cost to the tile's ClientData record.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selShortProcessTile(tile, cost, fdir, mask)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
int cost;
|
|
|
|
|
int fdir;
|
|
|
|
|
TileTypeBitMask *mask;
|
|
|
|
|
{
|
|
|
|
|
TileType ttype;
|
|
|
|
|
|
2022-01-03 23:49:54 +01:00
|
|
|
/* Ignore tiles that were already processed. This causes the */
|
|
|
|
|
/* algorithm to find any valid path but not the best path. That */
|
|
|
|
|
/* choice keeps the algorithm fast and efficient. */
|
|
|
|
|
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(tile) != CLIENTDEFAULT) return 1;
|
2022-01-03 23:49:54 +01:00
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
switch(fdir)
|
|
|
|
|
{
|
|
|
|
|
case GEO_NORTH:
|
|
|
|
|
ttype = SplitBottomType(tile);
|
|
|
|
|
break;
|
|
|
|
|
case GEO_SOUTH:
|
|
|
|
|
ttype = SplitTopType(tile);
|
|
|
|
|
break;
|
|
|
|
|
case GEO_EAST:
|
|
|
|
|
ttype = SplitLeftType(tile);
|
|
|
|
|
break;
|
|
|
|
|
case GEO_WEST:
|
|
|
|
|
ttype = SplitRightType(tile);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ttype = SplitLeftType(tile);
|
|
|
|
|
if (ttype == TT_SPACE) ttype = SplitRightType(tile);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ttype = TiGetTypeExact(tile);
|
|
|
|
|
|
|
|
|
|
/* Ignore space tiles */
|
|
|
|
|
if (ttype == TT_SPACE) return 1;
|
|
|
|
|
|
|
|
|
|
/* Ignore non-connecting tiles */
|
|
|
|
|
if (!TTMaskHasType(mask, ttype)) return 1;
|
|
|
|
|
|
|
|
|
|
/* If this tile is unvisited, or has a lower cost, then return and */
|
|
|
|
|
/* keep going. Otherwise, return 1 to stop the search this direction */
|
|
|
|
|
|
2025-02-21 18:57:13 +01:00
|
|
|
const ClientData ticlient = TiGetClient(tile);
|
|
|
|
|
const int curr = (int)CD2INT(ticlient);
|
|
|
|
|
if (ticlient == CLIENTDEFAULT)
|
2025-02-21 18:54:11 +01:00
|
|
|
TiSetClientINT(tile, cost);
|
2025-02-21 18:57:13 +01:00
|
|
|
else if (curr > cost)
|
2025-02-21 18:54:11 +01:00
|
|
|
TiSetClientINT(tile, cost);
|
2022-01-03 22:00:31 +01:00
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* selShortFindForward --
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
2022-01-03 22:00:31 +01:00
|
|
|
* Function for finding shorts. The cell searched is always SelectDef.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
2022-01-03 23:49:54 +01:00
|
|
|
* None
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Each tile visited has its ClientData record set to the current
|
|
|
|
|
* cost, in units equal to the steps from the source.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2022-01-03 23:49:54 +01:00
|
|
|
void
|
2022-01-03 22:00:31 +01:00
|
|
|
selShortFindForward(srctile, srctype, srcpnum, desttile)
|
|
|
|
|
Tile *srctile;
|
|
|
|
|
TileType srctype;
|
|
|
|
|
int srcpnum;
|
|
|
|
|
Tile *desttile;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
TileType type;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileTypeBitMask *lmask;
|
2022-01-03 22:00:31 +01:00
|
|
|
Tile *tile, *tp;
|
|
|
|
|
int pnum;
|
2022-01-01 04:19:11 +01:00
|
|
|
ShortData *sd;
|
|
|
|
|
static Stack *ShortStack = (Stack *)NULL;
|
|
|
|
|
|
|
|
|
|
int cost = 0;
|
|
|
|
|
|
|
|
|
|
if (ShortStack == (Stack *)NULL)
|
|
|
|
|
ShortStack = StackNew(64);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
/* Set the cost of the source tile to zero */
|
2025-02-21 18:54:11 +01:00
|
|
|
TiSetClientINT(srctile, 0);
|
2022-01-03 22:00:31 +01:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Drop the first entry on the stack */
|
2022-01-03 22:00:31 +01:00
|
|
|
sd = NewSD(cost, srctile, srctype, srcpnum);
|
2022-01-01 04:19:11 +01:00
|
|
|
STACKPUSH(sd, ShortStack);
|
|
|
|
|
|
|
|
|
|
while (!StackEmpty(ShortStack))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-01 04:19:11 +01:00
|
|
|
sd = (ShortData *)STACKPOP(ShortStack);
|
|
|
|
|
tile = sd->tile;
|
|
|
|
|
cost = sd->cost;
|
|
|
|
|
pnum = sd->pnum;
|
2022-01-03 22:00:31 +01:00
|
|
|
type = sd->type;
|
2022-01-01 04:19:11 +01:00
|
|
|
freeMagic((char *)sd);
|
|
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
lmask = &DBConnectTbl[type];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search top */
|
|
|
|
|
if (IsSplit(tile))
|
2022-01-03 22:00:31 +01:00
|
|
|
if (TiGetTopType(tile) != type)
|
|
|
|
|
goto srchleft;
|
2022-01-01 04:25:22 +01:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
|
|
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
if (selShortProcessTile(tp, cost + 1, GEO_NORTH, lmask) == 0)
|
2022-01-01 04:25:22 +01:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
sd = NewSD(cost + 1, tp, TiGetBottomType(tp), pnum);
|
2022-01-01 04:25:22 +01:00
|
|
|
STACKPUSH(sd, ShortStack);
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search left */
|
2017-04-25 14:41:48 +02:00
|
|
|
srchleft:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (IsSplit(tile))
|
2022-01-03 22:00:31 +01:00
|
|
|
if (TiGetLeftType(tile) != type)
|
|
|
|
|
goto srchbot;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
if (selShortProcessTile(tp, cost + 1, GEO_WEST, lmask) == 0)
|
2022-01-01 04:25:22 +01:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
sd = NewSD(cost + 1, tp, TiGetRightType(tp), pnum);
|
2022-01-01 04:25:22 +01:00
|
|
|
STACKPUSH(sd, ShortStack);
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search bottom */
|
2017-04-25 14:41:48 +02:00
|
|
|
srchbot:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (IsSplit(tile))
|
2022-01-03 22:00:31 +01:00
|
|
|
if (TiGetBottomType(tile) != type)
|
|
|
|
|
goto srchright;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
|
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
if (selShortProcessTile(tp, cost + 1, GEO_SOUTH, lmask) == 0)
|
2022-01-01 04:25:22 +01:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
sd = NewSD(cost + 1, tp, TiGetTopType(tp), pnum);
|
2022-01-01 04:25:22 +01:00
|
|
|
STACKPUSH(sd, ShortStack);
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search right */
|
2017-04-25 14:41:48 +02:00
|
|
|
srchright:
|
2022-01-01 04:19:11 +01:00
|
|
|
if (IsSplit(tile))
|
2022-01-03 22:00:31 +01:00
|
|
|
if (TiGetRightType(tile) != type)
|
|
|
|
|
goto donesrch;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
|
|
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
if (selShortProcessTile(tp, cost + 1, GEO_EAST, lmask) == 0)
|
2022-01-01 04:25:22 +01:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
sd = NewSD(cost + 1, tp, TiGetLeftType(tp), pnum);
|
2022-01-01 04:25:22 +01:00
|
|
|
STACKPUSH(sd, ShortStack);
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-01-01 04:19:11 +01:00
|
|
|
/* Search other connecting planes */
|
2017-04-25 14:41:48 +02:00
|
|
|
donesrch:
|
2022-01-03 22:00:31 +01:00
|
|
|
if ((!IsSplit(tile)) && DBIsContact(type))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-01 04:19:11 +01:00
|
|
|
PlaneMask pmask;
|
|
|
|
|
int p;
|
|
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
pmask = DBConnPlanes[type];
|
2022-01-01 04:19:11 +01:00
|
|
|
for (p = PL_TECHDEPBASE; p < DBNumPlanes; p++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-01 04:19:11 +01:00
|
|
|
if (PlaneMaskHasPlane(pmask, p) && (p != pnum))
|
|
|
|
|
{
|
2025-02-21 19:15:02 +01:00
|
|
|
tp = PlaneGetHint(SelectDef->cd_planes[p]);
|
2022-01-01 04:19:11 +01:00
|
|
|
GOTOPOINT(tp, &tile->ti_ll);
|
2025-02-21 19:15:02 +01:00
|
|
|
PlaneSetHint(SelectDef->cd_planes[p], tp);
|
2022-01-03 22:00:31 +01:00
|
|
|
if (selShortProcessTile(tp, cost + 1, GEO_CENTER, lmask) == 0)
|
2022-01-01 04:25:22 +01:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
sd = NewSD(cost + 1, tp, TiGetLeftType(tp), p);
|
2022-01-01 04:25:22 +01:00
|
|
|
STACKPUSH(sd, ShortStack);
|
|
|
|
|
}
|
2022-01-01 04:19:11 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectShort --
|
|
|
|
|
*
|
|
|
|
|
* This procedure, given two labels, finds the location of those
|
|
|
|
|
* labels in SelectDef. One is marked as source, the other as
|
|
|
|
|
* destination. Then the SelectDef paint (which is assumed to be
|
|
|
|
|
* a net selection that is all local to SelectDef) is recursively
|
|
|
|
|
* searched for connecting material. Each tile ClientData is
|
|
|
|
|
* given a cost which is the number of steps from the source.
|
|
|
|
|
* At the end, the minimum cost path is traced from the destination
|
|
|
|
|
* back to the source, and the path is saved as a linked list and
|
|
|
|
|
* passed back to the calling procedure.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* A linked list of tiles representing the connecting path with the
|
|
|
|
|
* fewest steps between source and destination.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Tile database left with non-default ClientData. It may be necessary
|
|
|
|
|
* to re-run the search routine to return all tiles to the default
|
|
|
|
|
* ClientData value.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ExtRectList *
|
|
|
|
|
SelectShort(char *lab1, char *lab2)
|
|
|
|
|
{
|
|
|
|
|
Label *selLabel, *srclab = NULL, *destlab = NULL;
|
2022-01-03 22:00:31 +01:00
|
|
|
Tile *srctile, *desttile;
|
|
|
|
|
TileType srctype, desttype;
|
|
|
|
|
int srcpnum, destpnum;
|
2017-04-25 14:41:48 +02:00
|
|
|
Plane *plane;
|
|
|
|
|
PlaneMask pmask;
|
|
|
|
|
ExtRectList *rlist;
|
|
|
|
|
|
2022-01-06 19:29:43 +01:00
|
|
|
CellUse *use;
|
|
|
|
|
TileType ttype;
|
|
|
|
|
Rect rect;
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
MagWindow *window;
|
|
|
|
|
DBWclientRec *crec;
|
|
|
|
|
int windowMask;
|
|
|
|
|
|
|
|
|
|
window = ToolGetBoxWindow(&rect, &windowMask);
|
|
|
|
|
if (!window) return NULL;
|
|
|
|
|
use = (CellUse *)window->w_surfaceID;
|
|
|
|
|
|
|
|
|
|
/* Run the equivalent of "goto lab1 ; select net" */
|
|
|
|
|
/* Make sure the selection is clear before starting */
|
|
|
|
|
|
|
|
|
|
SelectClear();
|
2024-09-30 00:00:00 +02:00
|
|
|
ttype = CmdFindNetProc(lab1, use, &rect, FALSE, NULL);
|
2022-01-06 19:29:43 +01:00
|
|
|
if (ttype == TT_SPACE) return NULL;
|
|
|
|
|
|
|
|
|
|
bzero(&scx, sizeof(SearchContext));
|
|
|
|
|
scx.scx_use = use;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
scx.scx_area = rect;
|
|
|
|
|
crec = (DBWclientRec *)window->w_clientData;
|
|
|
|
|
|
|
|
|
|
SelectNet(&scx, ttype, crec->dbw_bitmask, (Rect *)NULL, FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (selLabel = SelectDef->cd_labels; selLabel != NULL; selLabel =
|
2022-01-06 19:29:43 +01:00
|
|
|
selLabel->lab_next)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if ((srclab == NULL) && Match(lab1, selLabel->lab_text))
|
|
|
|
|
srclab = selLabel;
|
|
|
|
|
|
|
|
|
|
if ((destlab == NULL) && Match(lab2, selLabel->lab_text))
|
|
|
|
|
destlab = selLabel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Must be able to find both labels */
|
2022-01-01 04:19:11 +01:00
|
|
|
if ((srclab == NULL) || (destlab == NULL)) return NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Must be able to find tiles associated with each label */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2022-01-03 22:00:31 +01:00
|
|
|
pmask = DBTypePlaneMaskTbl[destlab->lab_type];
|
|
|
|
|
for (destpnum = PL_TECHDEPBASE; destpnum < DBNumPlanes; destpnum++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
if (PlaneMaskHasPlane(pmask, destpnum))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
plane = SelectDef->cd_planes[destpnum];
|
2025-02-21 19:15:02 +01:00
|
|
|
desttile = PlaneGetHint(plane);
|
2022-01-03 22:00:31 +01:00
|
|
|
GOTOPOINT(desttile, &destlab->lab_rect.r_ll)
|
|
|
|
|
desttype = TiGetTopType(desttile);
|
|
|
|
|
if (TTMaskHasType(&DBConnectTbl[destlab->lab_type], desttype)) break;
|
|
|
|
|
desttype = TiGetBottomType(desttile);
|
|
|
|
|
if (TTMaskHasType(&DBConnectTbl[destlab->lab_type], desttype)) break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-03 22:00:31 +01:00
|
|
|
pmask = DBTypePlaneMaskTbl[srclab->lab_type];
|
|
|
|
|
for (srcpnum = PL_TECHDEPBASE; srcpnum < DBNumPlanes; srcpnum++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
if (PlaneMaskHasPlane(pmask, srcpnum))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2022-01-03 22:00:31 +01:00
|
|
|
plane = SelectDef->cd_planes[srcpnum];
|
2025-02-21 19:15:02 +01:00
|
|
|
srctile = PlaneGetHint(plane);
|
2022-01-03 22:00:31 +01:00
|
|
|
GOTOPOINT(srctile, &srclab->lab_rect.r_ll)
|
|
|
|
|
srctype = TiGetTopType(srctile);
|
|
|
|
|
if (TTMaskHasType(&DBConnectTbl[srclab->lab_type], srctype)) break;
|
|
|
|
|
srctype = TiGetBottomType(srctile);
|
|
|
|
|
if (TTMaskHasType(&DBConnectTbl[srclab->lab_type], srctype)) break;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-04 19:42:01 +02:00
|
|
|
selShortFindForward(srctile, srctype, srcpnum, desttile/*, desttype*/);
|
2022-01-03 22:00:31 +01:00
|
|
|
|
|
|
|
|
/* Now see if destination has been counted */
|
2025-02-21 18:54:11 +01:00
|
|
|
if (TiGetClient(desttile) == CLIENTDEFAULT) return NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Now find the shortest path between source and destination */
|
|
|
|
|
rlist = NULL;
|
2022-01-03 22:00:31 +01:00
|
|
|
selShortFindReverse(&rlist, desttile, destpnum, GEO_CENTER);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
return rlist;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* selTransTo2 --
|
|
|
|
|
*
|
|
|
|
|
* This local procedure makes a transformed copy of the selection
|
|
|
|
|
* in Select2Def, ignoring everything that's not in the edit cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Select2Def gets modified to hold the transformed selection.
|
|
|
|
|
* Error messages get printed if the selection contains any
|
|
|
|
|
* non-edit material.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
selTransTo2(transform)
|
|
|
|
|
Transform *transform; /* How to transform stuff before copying
|
|
|
|
|
* it to Select2Def.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
int selTransPaintFunc(); /* Forward references. */
|
|
|
|
|
int selTransCellFunc();
|
|
|
|
|
int selTransLabelFunc();
|
|
|
|
|
|
|
|
|
|
UndoDisable();
|
|
|
|
|
DBCellClearDef(Select2Def);
|
|
|
|
|
(void) SelEnumPaint(&DBAllButSpaceAndDRCBits, TRUE, (bool *) NULL,
|
|
|
|
|
selTransPaintFunc, (ClientData) transform);
|
|
|
|
|
(void) SelEnumCells(TRUE, (bool *) NULL, (SearchContext *) NULL,
|
|
|
|
|
selTransCellFunc, (ClientData) transform);
|
|
|
|
|
(void) SelEnumLabels(&DBAllTypeBits, TRUE, (bool *) NULL,
|
|
|
|
|
selTransLabelFunc, (ClientData) transform);
|
|
|
|
|
DBReComputeBbox(Select2Def);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function to copy paint. Always return 1 to keep the search alive. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selTransPaintFunc(rect, type, transform)
|
|
|
|
|
Rect *rect; /* Area of paint. */
|
|
|
|
|
TileType type; /* Type of paint. */
|
|
|
|
|
Transform *transform; /* How to change coords before painting. */
|
|
|
|
|
{
|
|
|
|
|
Rect newarea;
|
|
|
|
|
TileType loctype;
|
|
|
|
|
|
|
|
|
|
/* Change diagonal direction according to the transform */
|
|
|
|
|
if (type & TT_DIAGONAL)
|
|
|
|
|
{
|
|
|
|
|
loctype = DBTransformDiagonal(type, transform);
|
|
|
|
|
loctype |= (loctype & TT_SIDE) ? (type & TT_LEFTMASK) << 14 :
|
|
|
|
|
(type & TT_LEFTMASK);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
loctype = type;
|
|
|
|
|
|
|
|
|
|
GeoTransRect(transform, rect, &newarea);
|
|
|
|
|
DBPaint(Select2Def, &newarea, loctype);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function to copy subcells. Always return 1 to keep the
|
|
|
|
|
* search alive.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
selTransCellFunc(selUse, realUse, realTrans, transform)
|
|
|
|
|
CellUse *selUse; /* Use from selection. */
|
|
|
|
|
CellUse *realUse; /* Corresponding use from layout (used to
|
|
|
|
|
* get id). */
|
|
|
|
|
Transform *realTrans; /* Transform for realUse (ignored). */
|
|
|
|
|
Transform *transform; /* How to change coords of selUse before
|
|
|
|
|
* copying.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
CellUse *newUse;
|
|
|
|
|
Transform newTrans;
|
|
|
|
|
|
|
|
|
|
if (selUse->cu_flags & CU_LOCKED) return 0;
|
|
|
|
|
|
|
|
|
|
newUse = DBCellNewUse(selUse->cu_def, (char *) realUse->cu_id);
|
|
|
|
|
if (!DBLinkCell(newUse, Select2Def))
|
|
|
|
|
{
|
|
|
|
|
freeMagic((char *) newUse->cu_id);
|
|
|
|
|
newUse->cu_id = NULL;
|
|
|
|
|
(void) DBLinkCell(newUse, Select2Def);
|
|
|
|
|
}
|
|
|
|
|
GeoTransTrans(&selUse->cu_transform, transform, &newTrans);
|
|
|
|
|
DBSetArray(selUse, newUse);
|
|
|
|
|
DBSetTrans(newUse, &newTrans);
|
|
|
|
|
newUse->cu_expandMask = selUse->cu_expandMask;
|
|
|
|
|
newUse->cu_flags = selUse->cu_flags;
|
|
|
|
|
DBPlaceCell(newUse, Select2Def);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function to copy labels. Return 0 always to avoid
|
|
|
|
|
* aborting search.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
selTransLabelFunc(label, cellUse, defTransform, transform)
|
|
|
|
|
Label *label; /* Label to copy. This points to label
|
|
|
|
|
* in cellDef.
|
|
|
|
|
*/
|
|
|
|
|
CellUse *cellUse; /* (unused) */
|
|
|
|
|
Transform *defTransform; /* Transform from cellDef to root. */
|
|
|
|
|
Transform *transform; /* How to modify coords before copying to
|
|
|
|
|
* Select2Def.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Rect rootArea, finalArea;
|
|
|
|
|
int rootJust, finalJust;
|
|
|
|
|
Point rootOffset, finalOffset;
|
|
|
|
|
int rootRotate, finalRotate;
|
|
|
|
|
|
|
|
|
|
GeoTransRect(defTransform, &label->lab_rect, &rootArea);
|
|
|
|
|
rootJust = GeoTransPos(defTransform, label->lab_just);
|
|
|
|
|
GeoTransPointDelta(defTransform, &label->lab_offset, &rootOffset);
|
|
|
|
|
rootRotate = GeoTransAngle(defTransform, label->lab_rotate);
|
|
|
|
|
|
|
|
|
|
GeoTransRect(transform, &rootArea, &finalArea);
|
|
|
|
|
finalJust = GeoTransPos(transform, rootJust);
|
|
|
|
|
GeoTransPointDelta(transform, &rootOffset, &finalOffset);
|
|
|
|
|
finalRotate = GeoTransAngle(transform, rootRotate);
|
|
|
|
|
|
|
|
|
|
(void) DBPutFontLabel(Select2Def, &finalArea, label->lab_font,
|
|
|
|
|
label->lab_size, finalRotate, &finalOffset, finalJust,
|
2021-12-13 04:09:31 +01:00
|
|
|
label->lab_text, label->lab_type, label->lab_flags,
|
|
|
|
|
label->lab_port);
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectTransform --
|
|
|
|
|
*
|
|
|
|
|
* This procedure modifies the selection by transforming
|
|
|
|
|
* it geometrically.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The selection is modified and redisplayed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectTransform(transform)
|
|
|
|
|
Transform *transform; /* How to displace the selection.
|
|
|
|
|
* The transform is in root (user-
|
|
|
|
|
* visible) coordinates.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2021-10-28 21:57:17 +02:00
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("The current cell is not editable.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Copy from SelectDef to Select2Def, transforming along the way. */
|
|
|
|
|
|
|
|
|
|
selTransTo2(transform);
|
|
|
|
|
|
|
|
|
|
/* Now just delete the selection and recreate it from Select2Def,
|
|
|
|
|
* copying into the edit cell along the way.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
SelectDelete("modified", TRUE);
|
|
|
|
|
SelectAndCopy2(EditRootDef);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectExpand --
|
|
|
|
|
*
|
|
|
|
|
* Expand all of the selected cells that are unexpanded, and
|
|
|
|
|
* unexpand all of those that are expanded.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The contents of the selected cells will become visible or
|
|
|
|
|
* invisible on the display in the indicated window(s).
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectExpand(mask)
|
|
|
|
|
int mask; /* Bits of this word indicate which
|
|
|
|
|
* windows the selected cells will be
|
|
|
|
|
* expanded in.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
extern int selExpandFunc(); /* Forward reference. */
|
|
|
|
|
|
|
|
|
|
(void) SelEnumCells(FALSE, (bool *) NULL, (SearchContext *) NULL,
|
2024-10-21 10:13:23 +02:00
|
|
|
selExpandFunc, INT2CD(mask));
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
selExpandFunc(selUse, use, transform, mask)
|
|
|
|
|
CellUse *selUse; /* Use from selection. */
|
|
|
|
|
CellUse *use; /* Use to expand (in actual layout). */
|
|
|
|
|
Transform *transform; /* Not used. */
|
|
|
|
|
int mask; /* Windows in which to expand. */
|
|
|
|
|
{
|
|
|
|
|
/* Don't change expansion status of root cell: screws up
|
|
|
|
|
* DBWAreaChanged (need to always have at least top-level
|
|
|
|
|
* cell be expanded).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (use->cu_parent == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Can't unexpand root cell of window.\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Be sure to modify the expansion bit in the selection as well as
|
|
|
|
|
* the one in the layout in order to keep them consistent.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (DBDescendSubcell(use, mask))
|
|
|
|
|
{
|
|
|
|
|
DBExpand(selUse, mask, FALSE);
|
|
|
|
|
DBExpand(use, mask, FALSE);
|
|
|
|
|
DBWAreaChanged(use->cu_parent, &use->cu_bbox, mask,
|
|
|
|
|
(TileTypeBitMask *) NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DBExpand(selUse, mask, TRUE);
|
|
|
|
|
DBExpand(use, mask, TRUE);
|
|
|
|
|
DBWAreaChanged(use->cu_parent, &use->cu_bbox, mask, &DBAllButSpaceBits);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectArray --
|
|
|
|
|
*
|
|
|
|
|
* Array everything in the selection. Cells get turned into
|
|
|
|
|
* arrays, and paint and labels get replicated.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The edit cell is modified in a big way. It's also redisplayed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectArray(arrayInfo)
|
|
|
|
|
ArrayInfo *arrayInfo; /* Describes desired shape of array, all in
|
|
|
|
|
* root coordinates.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
extern int selArrayPFunc(), selArrayCFunc(), selArrayLFunc();
|
|
|
|
|
|
|
|
|
|
/* The way arraying is done is similar to moving: make an
|
|
|
|
|
* arrayed copy of everything in Select2Def, then delete the
|
|
|
|
|
* selection, then copy everything back from Select2Def and
|
|
|
|
|
* select it.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
UndoDisable();
|
|
|
|
|
DBCellClearDef(Select2Def);
|
|
|
|
|
(void) SelEnumPaint(&DBAllButSpaceAndDRCBits, TRUE, (bool *) NULL,
|
|
|
|
|
selArrayPFunc, (ClientData) arrayInfo);
|
|
|
|
|
(void) SelEnumCells(TRUE, (bool *) NULL, (SearchContext *) NULL,
|
|
|
|
|
selArrayCFunc, (ClientData) arrayInfo);
|
|
|
|
|
(void) SelEnumLabels(&DBAllTypeBits, TRUE, (bool *) NULL,
|
|
|
|
|
selArrayLFunc, (ClientData) arrayInfo);
|
|
|
|
|
DBReComputeBbox(Select2Def);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
|
|
|
|
|
/* Now just delete the selection and recreate it from Select2Def,
|
|
|
|
|
* copying into the edit cell along the way.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
SelectDelete("arrayed", TRUE);
|
|
|
|
|
SelectAndCopy2(EditRootDef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function for paint. Just make many copies of the paint
|
|
|
|
|
* into Select2Def. Always return 0 to keep the search alive.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selArrayPFunc(rect, type, arrayInfo)
|
|
|
|
|
Rect *rect; /* Rectangle to be arrayed. */
|
|
|
|
|
TileType type; /* Type of tile. */
|
|
|
|
|
ArrayInfo *arrayInfo; /* How to array. */
|
|
|
|
|
{
|
|
|
|
|
int y, nx, ny;
|
|
|
|
|
Rect current;
|
|
|
|
|
|
|
|
|
|
nx = arrayInfo->ar_xhi - arrayInfo->ar_xlo;
|
|
|
|
|
if (nx < 0) nx = -nx;
|
|
|
|
|
ny = arrayInfo->ar_yhi - arrayInfo->ar_ylo;
|
|
|
|
|
if (ny < 0) ny = -ny;
|
|
|
|
|
|
|
|
|
|
current = *rect;
|
|
|
|
|
for ( ; nx >= 0; nx -= 1)
|
|
|
|
|
{
|
|
|
|
|
current.r_ybot = rect->r_ybot;
|
|
|
|
|
current.r_ytop = rect->r_ytop;
|
|
|
|
|
for (y = ny; y >= 0; y -= 1)
|
|
|
|
|
{
|
|
|
|
|
DBPaint(Select2Def, ¤t, type);
|
|
|
|
|
current.r_ybot += arrayInfo->ar_ysep;
|
|
|
|
|
current.r_ytop += arrayInfo->ar_ysep;
|
|
|
|
|
}
|
|
|
|
|
current.r_xbot += arrayInfo->ar_xsep;
|
|
|
|
|
current.r_xtop += arrayInfo->ar_xsep;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function for cells. Just make an arrayed copy of
|
|
|
|
|
* each subcell found.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
selArrayCFunc(selUse, use, transform, arrayInfo)
|
|
|
|
|
CellUse *selUse; /* Use from selection (not used). */
|
|
|
|
|
CellUse *use; /* Use to be copied and arrayed. */
|
|
|
|
|
Transform *transform; /* Transform from use->cu_def to root. */
|
|
|
|
|
ArrayInfo *arrayInfo; /* Array characteristics desired. */
|
|
|
|
|
{
|
|
|
|
|
CellUse *newUse;
|
|
|
|
|
Transform tinv, newTrans;
|
|
|
|
|
Rect tmp, oldBbox;
|
|
|
|
|
|
|
|
|
|
/* 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, Select2Def))
|
|
|
|
|
{
|
|
|
|
|
freeMagic((char *) newUse->cu_id);
|
|
|
|
|
newUse->cu_id = NULL;
|
|
|
|
|
(void) DBLinkCell(newUse, Select2Def);
|
|
|
|
|
}
|
|
|
|
|
newUse->cu_expandMask = use->cu_expandMask;
|
|
|
|
|
newUse->cu_flags = use->cu_flags;
|
|
|
|
|
|
|
|
|
|
DBSetTrans(newUse, transform);
|
|
|
|
|
GeoInvertTrans(transform, &tinv);
|
|
|
|
|
DBMakeArray(newUse, &tinv, arrayInfo->ar_xlo,
|
|
|
|
|
arrayInfo->ar_ylo, arrayInfo->ar_xhi, arrayInfo->ar_yhi,
|
|
|
|
|
arrayInfo->ar_xsep, arrayInfo->ar_ysep);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Set the array's transform so that its lower-left corner is in
|
|
|
|
|
* the same place that it used to be.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
GeoInvertTrans(&use->cu_transform, &tinv);
|
|
|
|
|
GeoTransRect(&tinv, &use->cu_bbox, &tmp);
|
|
|
|
|
GeoTransRect(transform, &tmp, &oldBbox);
|
|
|
|
|
GeoTranslateTrans(&newUse->cu_transform,
|
|
|
|
|
oldBbox.r_xbot - newUse->cu_bbox.r_xbot,
|
|
|
|
|
oldBbox.r_ybot - newUse->cu_bbox.r_ybot,
|
|
|
|
|
&newTrans);
|
|
|
|
|
DBSetTrans(newUse, &newTrans);
|
|
|
|
|
|
|
|
|
|
if (DBCellFindDup(newUse, Select2Def) != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBUnLinkCell(newUse, Select2Def);
|
|
|
|
|
(void) DBCellDeleteUse(newUse);
|
|
|
|
|
}
|
|
|
|
|
else DBPlaceCell(newUse, Select2Def);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search function for labels. Similar to paint search function. */
|
|
|
|
|
/* modified by harry eaton to increment numbers in array labels */
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
selArrayLFunc(label, use, transform, arrayInfo)
|
|
|
|
|
Label *label; /* Label to be copied and replicated. */
|
|
|
|
|
CellUse *use; /* (unused) */
|
|
|
|
|
Transform *transform; /* Transform from coords of def to root. */
|
|
|
|
|
ArrayInfo *arrayInfo; /* How to replicate. */
|
|
|
|
|
{
|
|
|
|
|
int y, nx, ny, rootJust, rootRotate;
|
|
|
|
|
Point rootOffset;
|
|
|
|
|
Rect original, current;
|
|
|
|
|
char *astr;
|
|
|
|
|
int labx, laby, xi, yi, only1; /* numeric parts of label */
|
|
|
|
|
|
|
|
|
|
char *nmPutNums(); /* Forward reference */
|
|
|
|
|
|
|
|
|
|
nx = arrayInfo->ar_xhi - arrayInfo->ar_xlo;
|
|
|
|
|
if (nx < 0) nx = -nx;
|
|
|
|
|
ny = arrayInfo->ar_yhi - arrayInfo->ar_ylo;
|
|
|
|
|
if (ny < 0) ny = -ny;
|
|
|
|
|
|
|
|
|
|
GeoTransRect(transform, &label->lab_rect, &original);
|
|
|
|
|
rootJust = GeoTransPos(transform, label->lab_just);
|
|
|
|
|
rootRotate = GeoTransAngle(transform, label->lab_rotate);
|
|
|
|
|
GeoTransPointDelta(transform, &label->lab_offset, &rootOffset);
|
|
|
|
|
current = original;
|
|
|
|
|
|
|
|
|
|
/* get the existing numbers in the label */
|
|
|
|
|
nmGetNums(label->lab_text, &labx, &laby);
|
|
|
|
|
|
|
|
|
|
xi = yi = 0;
|
|
|
|
|
if (nx > 0 && ny > 0)
|
|
|
|
|
only1 = 0;
|
|
|
|
|
else
|
|
|
|
|
only1 = 1;
|
|
|
|
|
for ( ; nx >= 0; nx -= 1)
|
|
|
|
|
{
|
|
|
|
|
current.r_ybot = original.r_ybot;
|
|
|
|
|
current.r_ytop = original.r_ytop;
|
|
|
|
|
for (y = ny; y >= 0; y -= 1)
|
|
|
|
|
{
|
|
|
|
|
/* Eliminate any duplicate labels. Don't use type in comparing
|
|
|
|
|
* for duplicates, because the selection's type could change
|
|
|
|
|
* after it gets added to the edit cell. Any label with
|
|
|
|
|
* the same text and position is considered a duplicate.
|
|
|
|
|
*/
|
|
|
|
|
yi = ny - y;
|
|
|
|
|
astr = nmPutNums(label->lab_text, labx + xi, laby + yi);
|
|
|
|
|
DBEraseLabelsByContent(Select2Def, ¤t, -1, astr);
|
|
|
|
|
DBPutFontLabel(Select2Def, ¤t, label->lab_font,
|
|
|
|
|
label->lab_size, rootRotate, &rootOffset,
|
2021-12-13 04:09:31 +01:00
|
|
|
rootJust, astr, label->lab_type,
|
|
|
|
|
label->lab_flags, label->lab_port);
|
2017-04-25 14:41:48 +02:00
|
|
|
current.r_ybot += arrayInfo->ar_ysep;
|
|
|
|
|
current.r_ytop += arrayInfo->ar_ysep;
|
|
|
|
|
xi += only1;
|
|
|
|
|
}
|
|
|
|
|
xi += (1 - only1);
|
|
|
|
|
current.r_xbot += arrayInfo->ar_xsep;
|
|
|
|
|
current.r_xtop += arrayInfo->ar_xsep;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* SelectStretch --
|
|
|
|
|
*
|
|
|
|
|
* Move the selection a given amount in x (or y). While moving,
|
|
|
|
|
* erase everything that the selection passes over, and stretch
|
|
|
|
|
* material behind the selection.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The edit cell is modified. The selection is also modified
|
|
|
|
|
* and redisplayed.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectStretch(x, y)
|
|
|
|
|
int x; /* Amount to move in the x-direction. */
|
|
|
|
|
int y; /* Amount to move in the y-direction. Must
|
|
|
|
|
* be zero if x is non-zero.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Transform transform;
|
|
|
|
|
int plane;
|
|
|
|
|
Rect modifiedArea, editModified;
|
|
|
|
|
extern int selStretchEraseFunc(), selStretchFillFunc();
|
|
|
|
|
/* Forward declarations. */
|
|
|
|
|
|
|
|
|
|
if ((x == 0) && (y == 0)) return;
|
|
|
|
|
|
2022-04-01 02:02:12 +02:00
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("The current cell is not editable.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* First of all, copy from SelectDef to Select2Def, moving the
|
|
|
|
|
* selection along the way.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
GeoTranslateTrans(&GeoIdentityTransform, x, y, &transform);
|
|
|
|
|
selTransTo2(&transform);
|
|
|
|
|
|
|
|
|
|
/* We're going to modify not just the old selection area or the new
|
|
|
|
|
* one, but everything in-between too. Remember this and tell the
|
|
|
|
|
* displayer and DRC about it later.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
modifiedArea = Select2Def->cd_extended;
|
|
|
|
|
(void) GeoInclude(&SelectDef->cd_extended, &modifiedArea);
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &modifiedArea, &editModified);
|
|
|
|
|
|
|
|
|
|
/* Delete the selection itself. */
|
|
|
|
|
|
|
|
|
|
SelectDelete("stretched", TRUE);
|
|
|
|
|
|
|
|
|
|
/* Next, delete all the material in front of each piece of paint in
|
|
|
|
|
* the selection.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
selStretchX = x;
|
|
|
|
|
selStretchY = y;
|
|
|
|
|
for (plane = PL_SELECTBASE; plane < DBNumPlanes; plane++)
|
|
|
|
|
{
|
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL, Select2Def->cd_planes[plane],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits, selStretchEraseFunc,
|
|
|
|
|
(ClientData) &plane);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* To achieve the stretch affect, fill in material behind the selection
|
|
|
|
|
* everywhere that it used to touch other material in the edit cell.
|
|
|
|
|
* This code first builds up a list of areas to paint, then paints them
|
|
|
|
|
* (can't paint as we go because the new paint interacts with the
|
|
|
|
|
* computation of what to stretch).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
selStretchList = NULL;
|
|
|
|
|
for (plane = PL_SELECTBASE; plane < DBNumPlanes; plane++)
|
|
|
|
|
{
|
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL, Select2Def->cd_planes[plane],
|
|
|
|
|
&TiPlaneRect, &DBAllButSpaceAndDRCBits, selStretchFillFunc,
|
|
|
|
|
(ClientData) &plane);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Paint back the areas in the list. */
|
|
|
|
|
|
|
|
|
|
while (selStretchList != NULL)
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask tmask;
|
|
|
|
|
TileType type = selStretchList->sa_type;
|
|
|
|
|
|
|
|
|
|
TileType tloc;
|
|
|
|
|
tloc = (type & TT_DIAGONAL) ? ((type & TT_SIDE) ?
|
|
|
|
|
(type & TT_RIGHTMASK) >> 14 : (type & TT_LEFTMASK)) : type;
|
|
|
|
|
TTMaskSetOnlyType(&tmask, tloc);
|
|
|
|
|
|
|
|
|
|
DBPaintValid(EditCellUse->cu_def, &selStretchList->sa_area, &tmask, type);
|
|
|
|
|
freeMagic((char *) selStretchList);
|
|
|
|
|
selStretchList = selStretchList->sa_next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Paint the new translated selection back into the edit cell,
|
|
|
|
|
* select it again, and tell DRC and display about what we
|
|
|
|
|
* changed.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
SelectAndCopy2(EditRootDef);
|
|
|
|
|
DBWAreaChanged(EditCellUse->cu_def, &editModified, DBW_ALLWINDOWS,
|
|
|
|
|
(TileTypeBitMask *) NULL);
|
|
|
|
|
DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editModified);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* selStretchEraseFunc --
|
|
|
|
|
*
|
|
|
|
|
* Called by DBSrPaintArea during stretching for each tile in the
|
|
|
|
|
* new selection. Erase the area that the tile swept out as it
|
|
|
|
|
* moved.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Always returns 0 to keep the search alive.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The edit cell is modified.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selStretchEraseFunc(tile, plane)
|
|
|
|
|
Tile *tile; /* Tile being moved in a stretch operation. */
|
|
|
|
|
int *plane; /* Plane of tiles being searched */
|
|
|
|
|
{
|
|
|
|
|
Rect area, editArea;
|
|
|
|
|
int planeNum;
|
|
|
|
|
planeAndArea pa;
|
|
|
|
|
TileType type, t;
|
|
|
|
|
TileTypeBitMask tmpmask, mask, *residueMask;
|
|
|
|
|
PaintUndoInfo ui;
|
|
|
|
|
PaintResultType selStretchEraseTbl[NT];
|
|
|
|
|
extern int selStretchEraseFunc2();
|
|
|
|
|
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
type = (SplitSide(tile)) ? SplitRightType(tile) :
|
|
|
|
|
SplitLeftType(tile);
|
|
|
|
|
else
|
|
|
|
|
type = TiGetType(tile);
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &area);
|
|
|
|
|
|
|
|
|
|
/* Compute the area that this tile swept out (the current area is
|
|
|
|
|
* its location AFTER moving), and erase everything that was in
|
|
|
|
|
* its path.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (selStretchX > 0)
|
|
|
|
|
area.r_xbot -= selStretchX;
|
|
|
|
|
else area.r_xtop -= selStretchX;
|
|
|
|
|
if (selStretchY > 0)
|
|
|
|
|
area.r_ybot -= selStretchY;
|
|
|
|
|
else area.r_ytop -= selStretchY;
|
|
|
|
|
|
|
|
|
|
/* Translate into edit coords and erase all material on the
|
|
|
|
|
* tile's plane.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &area, &editArea);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* We need to erase all types that interact with "type", *not* all */
|
|
|
|
|
/* types on "plane", due to the way stacked contacts are handled. */
|
|
|
|
|
/* Contacts on different planes may stretch across one another */
|
|
|
|
|
/* without effect, if the types are stackable. Likewise, layer */
|
|
|
|
|
/* types may stretch across contacts for which they are a residue. */
|
|
|
|
|
|
|
|
|
|
planeNum = *plane;
|
|
|
|
|
mask = DBPlaneTypes[planeNum];
|
|
|
|
|
if (DBIsContact(type))
|
|
|
|
|
{
|
|
|
|
|
for (t = DBNumUserLayers; t < DBNumTypes; t++)
|
|
|
|
|
{
|
|
|
|
|
if (t == type) continue;
|
|
|
|
|
|
|
|
|
|
/* Exclude all contact types for which this type should "pass through" */
|
|
|
|
|
|
|
|
|
|
if (TTMaskHasType(&mask, t))
|
|
|
|
|
{
|
|
|
|
|
tmpmask = *DBResidueMask(t);
|
|
|
|
|
if (TTMaskHasType(&tmpmask, type))
|
|
|
|
|
{
|
|
|
|
|
TTMaskClearType(&tmpmask, type);
|
|
|
|
|
TTMaskClearMask(&mask, &tmpmask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* For stacked type tiles do not erase common residue
|
|
|
|
|
* type of both contacts from layout.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (type >= DBNumUserLayers)
|
|
|
|
|
{
|
|
|
|
|
t = (DBPlaneToResidue(type, planeNum));
|
|
|
|
|
TTMaskClearType(&mask, t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TTMaskAndMask(&mask, &DBActiveLayerBits);
|
|
|
|
|
|
|
|
|
|
/* Erase all tile types specified in the processed mask.
|
|
|
|
|
* Erase contacts in mask first using DBErase. This prevents
|
|
|
|
|
* contacts from leaving partial images of themselves on other
|
|
|
|
|
* planes. Then, remove the non-contact layers on this plane
|
|
|
|
|
* only, such that contacts are not disturbed.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TTMaskZero(&tmpmask);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
selStretchEraseTbl[TT_SPACE] = (PaintResultType)TT_SPACE;
|
|
|
|
|
for (t = TT_SPACE + 1; t < DBNumUserLayers; t++)
|
|
|
|
|
{
|
|
|
|
|
selStretchEraseTbl[t] = (PaintResultType)t;
|
|
|
|
|
if (TTMaskHasType(&mask, t))
|
|
|
|
|
{
|
|
|
|
|
if (DBIsContact(t))
|
|
|
|
|
{
|
|
|
|
|
if (t == type)
|
|
|
|
|
DBErase(EditCellUse->cu_def, &editArea, t);
|
|
|
|
|
else
|
|
|
|
|
TTMaskSetType(&tmpmask, t);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
selStretchEraseTbl[t] = (PaintResultType)TT_SPACE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (; t < DBNumTypes; t++)
|
|
|
|
|
selStretchEraseTbl[t] = (PaintResultType)t;
|
|
|
|
|
|
|
|
|
|
/* Remove conflicting contact types, where they exist */
|
|
|
|
|
|
|
|
|
|
pa.pa_area = &editArea;
|
|
|
|
|
pa.pa_plane = planeNum;
|
|
|
|
|
pa.pa_mask = &tmpmask;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, EditCellUse->cu_def->cd_planes[planeNum],
|
|
|
|
|
&editArea, &tmpmask, selStretchEraseFunc2, (ClientData)&pa);
|
|
|
|
|
|
|
|
|
|
ui.pu_pNum = planeNum;
|
|
|
|
|
ui.pu_def = EditCellUse->cu_def;
|
|
|
|
|
DBPaintPlane(EditCellUse->cu_def->cd_planes[planeNum], &editArea,
|
|
|
|
|
selStretchEraseTbl, &ui);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selStretchEraseFunc2(tile, pa)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
planeAndArea *pa;
|
|
|
|
|
{
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (TTMaskHasType(pa->pa_mask, TiGetLeftType(tile)))
|
|
|
|
|
DBErase(EditCellUse->cu_def, pa->pa_area,
|
|
|
|
|
DBPlaneToResidue(TiGetLeftType(tile), pa->pa_plane));
|
|
|
|
|
if (TTMaskHasType(pa->pa_mask, TiGetRightType(tile)))
|
|
|
|
|
DBErase(EditCellUse->cu_def, pa->pa_area,
|
|
|
|
|
DBPlaneToResidue(TiGetRightType(tile), pa->pa_plane));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
DBErase(EditCellUse->cu_def, pa->pa_area,
|
|
|
|
|
DBPlaneToResidue(TiGetType(tile), pa->pa_plane));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* selStretchFillFunc --
|
|
|
|
|
*
|
|
|
|
|
* This function is invoked during stretching for each paint tile in
|
|
|
|
|
* the (new) selection. It finds places in where the back-side of this
|
|
|
|
|
* tile borders space in the (new) selection, then looks for paint in
|
|
|
|
|
* the edit cell that borders the old location of the paint. If the
|
|
|
|
|
* selection has been moved away from paint in the edit cell, additional
|
|
|
|
|
* material is filled in behind the selection.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Always returns 0 to keep the search alive.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Modifies the edit cell by painting material.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selStretchFillFunc(tile, plane)
|
|
|
|
|
Tile *tile; /* Tile in the old selection. */
|
|
|
|
|
int *plane; /* Plane of tile being searched */
|
|
|
|
|
{
|
|
|
|
|
Rect area;
|
|
|
|
|
extern int selStretchFillFunc2();
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &area);
|
|
|
|
|
|
|
|
|
|
/* Check the material just behind this paint (in the sense of the
|
|
|
|
|
* stretch direction) for space in the selection and non-space in
|
|
|
|
|
* the edit cell.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (selStretchX > 0)
|
|
|
|
|
{
|
|
|
|
|
area.r_xtop = area.r_xbot;
|
|
|
|
|
area.r_xbot -= 1;
|
|
|
|
|
}
|
|
|
|
|
else if (selStretchX < 0)
|
|
|
|
|
{
|
|
|
|
|
area.r_xbot = area.r_xtop;
|
|
|
|
|
area.r_xtop += 1;
|
|
|
|
|
}
|
|
|
|
|
else if (selStretchY > 0)
|
|
|
|
|
{
|
|
|
|
|
area.r_ytop = area.r_ybot;
|
|
|
|
|
area.r_ybot -= 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
area.r_ybot = area.r_ytop;
|
|
|
|
|
area.r_ytop += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (selStretchX > 0)
|
|
|
|
|
selStretchType = SplitLeftType(tile);
|
|
|
|
|
else if (selStretchX < 0)
|
|
|
|
|
selStretchType = SplitRightType(tile);
|
|
|
|
|
else if (selStretchY > 0)
|
|
|
|
|
selStretchType = (SplitDirection(tile) ? SplitLeftType(tile) :
|
|
|
|
|
SplitRightType(tile));
|
|
|
|
|
else if (selStretchY < 0)
|
|
|
|
|
selStretchType = (SplitDirection(tile) ? SplitRightType(tile) :
|
|
|
|
|
SplitLeftType(tile));
|
|
|
|
|
if (selStretchType == TT_SPACE) return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
selStretchType = TiGetType(tile);
|
|
|
|
|
|
|
|
|
|
/* The search functions invoked indirectly by the following procedure
|
|
|
|
|
* call build up a list of areas to paint.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL,
|
|
|
|
|
Select2Def->cd_planes[*plane], &area,
|
|
|
|
|
&DBSpaceBits, selStretchFillFunc2, (ClientData) &area);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Second-level filling search function: find all of the edit material
|
|
|
|
|
* that intersects areas where space borders a selected paint tile.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selStretchFillFunc2(tile, area)
|
|
|
|
|
Tile *tile; /* Space tile that borders selected
|
|
|
|
|
* paint.
|
|
|
|
|
*/
|
|
|
|
|
Rect *area; /* A one-unit wide strip along the
|
|
|
|
|
* border (i.e. the area in which
|
|
|
|
|
* we're interested in space).
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Rect spaceArea, editArea;
|
|
|
|
|
int pNum;
|
|
|
|
|
|
|
|
|
|
extern int selStretchFillFunc3();
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &spaceArea);
|
|
|
|
|
|
|
|
|
|
/* Find out which portion of this space tile borders the selected
|
|
|
|
|
* tile, transform it back to coords of the old selection and then
|
|
|
|
|
* to edit coords, and find all the edit material that borders the
|
|
|
|
|
* selected tile in this area.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
GeoClip(&spaceArea, area);
|
|
|
|
|
spaceArea.r_xbot -= selStretchX;
|
|
|
|
|
spaceArea.r_xtop -= selStretchX;
|
|
|
|
|
spaceArea.r_ybot -= selStretchY;
|
|
|
|
|
spaceArea.r_ytop -= selStretchY;
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &spaceArea, &editArea);
|
|
|
|
|
|
|
|
|
|
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
|
|
|
|
|
{
|
|
|
|
|
if (DBTypeOnPlane(selStretchType, pNum))
|
|
|
|
|
(void) DBSrPaintArea((Tile *) NULL,
|
|
|
|
|
EditCellUse->cu_def->cd_planes[pNum],
|
|
|
|
|
&editArea, &DBActiveLayerBits, selStretchFillFunc3,
|
|
|
|
|
(ClientData) &spaceArea);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK, now we've found a piece of material in the edit cell that is
|
|
|
|
|
* right next to a piece of selected material that's about to move
|
|
|
|
|
* away from it. Stretch one or the other to fill the gap. Use the
|
|
|
|
|
* material that's moving as the stretch material unless it's a fixed-size
|
|
|
|
|
* material and the other stuff is stretchable.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
selStretchFillFunc3(tile, area)
|
|
|
|
|
Tile *tile; /* Tile of edit material that's about to
|
|
|
|
|
* be left behind selection.
|
|
|
|
|
*/
|
|
|
|
|
Rect *area; /* The border area we're interested in,
|
|
|
|
|
* in root coords.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Rect editArea, rootArea;
|
|
|
|
|
TileType type, stype;
|
|
|
|
|
TileTypeBitMask *mask, *sMask, *tMask;
|
|
|
|
|
StretchArea *sa;
|
|
|
|
|
|
|
|
|
|
/* Compute the area to be painted. */
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &editArea);
|
|
|
|
|
GeoTransRect(&EditToRootTransform, &editArea, &rootArea);
|
|
|
|
|
GeoClip(&rootArea, area);
|
|
|
|
|
if (selStretchX > 0)
|
|
|
|
|
{
|
|
|
|
|
rootArea.r_xbot = rootArea.r_xtop;
|
|
|
|
|
rootArea.r_xtop += selStretchX;
|
|
|
|
|
}
|
|
|
|
|
else if (selStretchX < 0)
|
|
|
|
|
{
|
|
|
|
|
rootArea.r_xtop = rootArea.r_xbot;
|
|
|
|
|
rootArea.r_xbot += selStretchX;
|
|
|
|
|
}
|
|
|
|
|
else if (selStretchY > 0)
|
|
|
|
|
{
|
|
|
|
|
rootArea.r_ybot = rootArea.r_ytop;
|
|
|
|
|
rootArea.r_ytop += selStretchY;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
rootArea.r_ytop = rootArea.r_ybot;
|
|
|
|
|
rootArea.r_ybot += selStretchY;
|
|
|
|
|
}
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &rootArea, &editArea);
|
|
|
|
|
|
|
|
|
|
/* Compute the material to be painted. Be careful: for contacts,
|
|
|
|
|
* must use the master image.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
|
|
|
|
if (selStretchX > 0)
|
|
|
|
|
type = SplitRightType(tile);
|
|
|
|
|
else if (selStretchX < 0)
|
|
|
|
|
type = SplitLeftType(tile);
|
|
|
|
|
else if (selStretchY > 0)
|
|
|
|
|
type = (SplitDirection(tile) ? SplitRightType(tile) :
|
|
|
|
|
SplitLeftType(tile));
|
|
|
|
|
else if (selStretchY < 0)
|
|
|
|
|
type = (SplitDirection(tile) ? SplitLeftType(tile) :
|
|
|
|
|
SplitRightType(tile));
|
2024-10-04 20:45:07 +02:00
|
|
|
else
|
|
|
|
|
type = TT_SPACE;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (type == TT_SPACE) return 0; /* nothing to stretch */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
type = TiGetType(tile);
|
|
|
|
|
sMask = DBResidueMask(selStretchType);
|
|
|
|
|
|
|
|
|
|
/* If have type and stretch type are both contacts (fixed types) */
|
|
|
|
|
/* and there exists a stacking contact type between them, then */
|
|
|
|
|
/* we should be filling in the original type. */
|
|
|
|
|
|
|
|
|
|
if (!DBIsContact(type) || !DBIsContact(selStretchType) ||
|
|
|
|
|
(((stype = DBTechFindStacking(type, selStretchType))
|
|
|
|
|
< DBNumUserLayers) && (stype >= TT_TECHDEPBASE)))
|
|
|
|
|
{
|
|
|
|
|
/* If type is a residue of selStretchType then we always */
|
|
|
|
|
/* fill with type. Otherwise, we check the list of plow */
|
|
|
|
|
/* types to see what is and isn't fixed. */
|
|
|
|
|
|
|
|
|
|
if (!TTMaskHasType(sMask, type))
|
|
|
|
|
if (TTMaskHasType(&PlowFixedTypes, type)
|
|
|
|
|
|| !TTMaskHasType(&PlowFixedTypes, selStretchType))
|
|
|
|
|
type = selStretchType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise, if we have two fixed types, and they both border the */
|
|
|
|
|
/* selection area (as opposed to overlapping), and they share a */
|
|
|
|
|
/* residue, then the type to paint is the shared residue type. */
|
|
|
|
|
/* We must ignore the case type == selStretchType (fixed by Nishit, */
|
|
|
|
|
/* 6/29/04. */
|
|
|
|
|
|
|
|
|
|
else if (DBIsContact(type) && DBIsContact(selStretchType)
|
|
|
|
|
&& (type != selStretchType))
|
|
|
|
|
{
|
|
|
|
|
if (((selStretchX < 0) && (editArea.r_xtop == area->r_xbot)) ||
|
|
|
|
|
((selStretchX > 0) && (editArea.r_xbot == area->r_xtop)) ||
|
|
|
|
|
((selStretchY < 0) && (editArea.r_ytop == area->r_ybot)) ||
|
|
|
|
|
((selStretchY > 0) && (editArea.r_ybot == area->r_ytop)))
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask rmask;
|
|
|
|
|
tMask = DBResidueMask(type);
|
|
|
|
|
TTMaskAndMask3(&rmask, tMask, sMask);
|
|
|
|
|
for (type = TT_TECHDEPBASE; type < DBNumUserLayers; type++)
|
|
|
|
|
if (TTMaskHasType(&rmask, type))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (type == DBNumUserLayers)
|
|
|
|
|
return 0; /* Types do not share any residues */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save around the area we just found. */
|
|
|
|
|
|
|
|
|
|
sa = (StretchArea *) mallocMagic(sizeof(StretchArea));
|
|
|
|
|
sa->sa_area = editArea;
|
|
|
|
|
sa->sa_type = type;
|
|
|
|
|
sa->sa_next = selStretchList;
|
|
|
|
|
selStretchList = sa;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* SelectDump --
|
|
|
|
|
*
|
|
|
|
|
* Copies an area of one cell into the edit cell, selecting the
|
|
|
|
|
* copy so that it can be manipulated later.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The edit cell is modified.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
SelectDump(scx)
|
|
|
|
|
SearchContext *scx; /* Describes the cell from which
|
|
|
|
|
* material is to be copied, the
|
|
|
|
|
* area to copy, and the transform
|
|
|
|
|
* to root coordinates in the edit
|
|
|
|
|
* cell's window.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
/* Copy from the source cell to Select2Def while transforming,
|
|
|
|
|
* then let SelectandCopy2 do the rest of the work. Don't
|
|
|
|
|
* record any of the Select2Def changes for undo-ing.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
UndoDisable();
|
|
|
|
|
DBCellClearDef(Select2Def);
|
|
|
|
|
(void) DBCellCopyAllPaint(scx, &DBAllButSpaceAndDRCBits, CU_DESCEND_SPECIAL,
|
|
|
|
|
Select2Use);
|
|
|
|
|
(void) DBCellCopyAllLabels(scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, Select2Use,
|
|
|
|
|
(Rect *) NULL);
|
|
|
|
|
(void) DBCellCopyAllCells(scx, CU_DESCEND_SPECIAL, Select2Use, (Rect *) NULL);
|
|
|
|
|
DBReComputeBbox(Select2Def);
|
|
|
|
|
UndoEnable();
|
|
|
|
|
|
|
|
|
|
SelectClear();
|
|
|
|
|
SelectAndCopy2(EditRootDef);
|
|
|
|
|
}
|