Files
magic/plow/PlowYank.c
T
Darryl L. Miles 606f37cc80 Ti[GS]etClient() usage enforcement
All naked access to `ti_client` now uses the function-like-macro
to encapsulate this action.  This macro existed before this just
makes all sites utilize it.

Added additional INT and PTR variants to remove the programmer
load on thinking about casing and casts polluting the point
of use.  So the use now looks cleaner.

Equivalent prototypes:

 void TiSetClient(Tile*, ClientData)
 void TiSetClientINT(Tile*, intptr_t) /* pointertype */
 void TiSetClientPTR(Tile*, void*)

 ClientData TiGetClient(Tile*)
 intptr_t TiGetClientINT(Tile*) /* pointertype */
 void *TiGetClientPTR(Tile*)
2025-04-09 14:55:58 -04:00

563 lines
17 KiB
C

/*
* PlowYank.c --
*
* Plowing.
* Incremental yanking, and painting the results back into the
* original cell when done.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/plow/PlowYank.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/undo.h"
#include "debug/debug.h"
#include "plow/plow.h"
#include "plow/plowInt.h"
#include "textio/textio.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "drc/drc.h"
/* Yank buffer used to hold the actual geometry being plowed */
CellDef *plowYankDef;
CellUse *plowYankUse;
/* Incremental yanking information */
CellDef *plowSpareDef; /* Def into which we do each incremental yank */
CellUse *plowSpareUse; /* Use of above */
Rect plowYankedArea; /* Area yanked so far. Cells may extend outside
* this area, so when we do incremental yanking,
* we need to avoid copying any cells that were
* already yanked on a previous pass.
*/
int plowYankHalo; /* Halo around each edge. If this halo extends
* outside the area already yanked, or touches
* its border, we yank more.
*/
/* Imports from the rest of this module */
extern Rect plowCellBbox;
extern Transform plowYankTrans;
extern Transform plowInverseTrans;
extern int plowDirection;
extern bool plowLabelsChanged;
/* Dummy whose cu_def pointer is reset to point to the def being plowed */
extern CellUse *plowDummyUse;
/* Forward declarations */
int plowYankUpdateCell();
int plowYankUpdatePaint();
int plowCheckLabel();
/*
* ----------------------------------------------------------------------------
*
* plowYankMore --
*
* Yank more area from the original cell. This is a little tricky in
* the case of subcells, because we don't always update plowYankedArea
* to enclose all subcell bounding boxes. This means that a subcell
* may appear in more than a single yank area. To combat this, we do
* not actually copy a cell if it already has been copied (which we
* determine based on the use-id).
*
* The rectangle causing us to yank more area is 'area'. To try to
* make the cost of yanking stay roughly linear in the area plowed,
* we try to make the area grow by the same factor each time we yank
* more.
*
* We don't do anything if we don't have to yank any more area; this
* is the case if 'area' plus its halo lies entirely inside plowYankedArea.
*
* Results:
* TRUE if we yanked more, FALSE if not.
*
* Side effects:
* May yank more area from the original cell.
* The spare cell (plowSpareDef) is always left cleared.
*
* ----------------------------------------------------------------------------
*/
bool
plowYankMore(area, halo, back)
Rect *area;
int halo; /* Distance to right, top, bottom of area that
* must have been yanked.
*/
int back; /* Distance to left that must have been
* yanked.
*/
{
Rect grownR, newArea, oldArea;
SearchContext scx;
int xsize, ysize;
CellDef tmpDef;
int pNum;
grownR.r_xbot = area->r_xbot - back;
grownR.r_xtop = area->r_xtop + halo;
grownR.r_ybot = area->r_ybot - halo;
grownR.r_ytop = area->r_ytop + halo;
GEOCLIP(&grownR, &plowCellBbox);
/* Don't have to yank if entirely within the yanked area */
if (GEO_SURROUND_STRONG(&plowYankedArea, &grownR))
return (FALSE);
/* Figure out the additional area to yank */
xsize = (plowYankedArea.r_xtop - plowYankedArea.r_xbot) >> 1;
ysize = (plowYankedArea.r_ytop - plowYankedArea.r_ybot) >> 1;
newArea = plowYankedArea;
if (grownR.r_xbot <= plowYankedArea.r_xbot) newArea.r_xbot -= xsize >> 1;
if (grownR.r_xtop >= plowYankedArea.r_xtop) newArea.r_xtop += xsize;
if (grownR.r_ybot <= plowYankedArea.r_ybot) newArea.r_ybot -= ysize;
if (grownR.r_ytop >= plowYankedArea.r_ytop) newArea.r_ytop += ysize;
(void) GeoInclude(&grownR, &newArea);
/* Clip to the cell bbox; if inside the existing yanked area, we're done */
GEOCLIP(&newArea, &plowCellBbox);
if (GEO_SURROUND(&plowYankedArea, &newArea))
return (FALSE);
oldArea = plowYankedArea;
plowYankedArea = newArea;
/*
* Work to do.
* Right now, we use a very simple approach:
*
* Yank the complete area into a separate set of tile planes.
* Use the original set to update cu_clients and trailing tile
* coordinates.
*
* Run without undo since we're mucking with yank cells.
*/
/* Yank the larger area into the spare cell */
UndoDisable();
scx.scx_use = plowDummyUse;
scx.scx_trans = plowYankTrans;
GeoTransRect(&plowInverseTrans, &plowYankedArea, &scx.scx_area);
(void) DBCellCopyPaint(&scx, &DBAllButSpaceAndDRCBits, 0, plowSpareUse);
(void) DBCellCopyCells(&scx, plowSpareUse, (Rect *) NULL);
/*
* Update cell positions in the new cell.
* The following loop executes as many times as there
* are cells in the original yank def; each cell gets
* deleted and replaces its counterpart in the new yank
* buffer.
*/
while (DBCellEnum(plowYankDef, plowYankUpdateCell, (ClientData) NULL))
/* Nothing */;
/*
* Update paint edges in the new cell.
* We extend the search one lambda to the right to catch the
* trailing edges of space tiles in the original yank buffer.
*/
oldArea.r_xtop++;
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
(void) DBSrPaintArea((Tile *) NULL, plowYankDef->cd_planes[pNum],
&oldArea, &DBAllTypeBits, plowYankUpdatePaint,
INT2CD(pNum));
}
/* Switch the yank cell and the spare cell */
DBCellClearDef(plowYankDef);
DBCellSetAvail(plowYankDef);
DBCellCopyDefBody(plowYankDef, &tmpDef);
DBCellCopyDefBody(plowSpareDef, plowYankDef);
DBCellCopyDefBody(&tmpDef, plowSpareDef);
UndoEnable();
return (TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* plowYankUpdateCell --
*
* Called for each cell in the old yank buffer. We search the
* new yank buffer for the corresponding cell (same child def,
* same use-id) delete the cell from the new yank buffer, and
* replace it with the cell from the old one.
*
* We have to do this, rather than simply updating the deltas
* in the new yank buffer, because edges in the queue may have
* pointers to the uses from the old yank buffer.
*
* Results:
* Always returns 1.
*
* Side effects:
* Moves its argument cell over to plowSpareUse, replacing
* the cell by the same name in plowSpareUse.
*
* ----------------------------------------------------------------------------
*/
int
plowYankUpdateCell(yankChildUse)
CellUse *yankChildUse; /* Use in the yank cell */
{
CellUse *spareChildUse;
ClientData savedelta;
savedelta = yankChildUse->cu_client;
for (spareChildUse = yankChildUse->cu_def->cd_parents;
spareChildUse;
spareChildUse = spareChildUse->cu_nextuse)
{
if (spareChildUse->cu_parent == plowSpareDef
&& strcmp(spareChildUse->cu_id, yankChildUse->cu_id) == 0)
{
/* Delete the use from the new yank def */
DBDeleteCell(spareChildUse);
/* Move the use from the old yank def to the new yank def */
DBDeleteCell(yankChildUse);
DBPlaceCell(yankChildUse, plowSpareDef);
/* Restore the delta since DBPlaceCell re-initializes it to 0 */
yankChildUse->cu_client = savedelta;
/* Return 1 so DBSrCellPlaneArea doesn't bomb */
return (1);
}
}
TxError("Couldn't find use %s in spare yank buffer\n", yankChildUse->cu_id);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowYankUpdatePaint --
*
* Called for each paint tile in the old yank buffer. We search the new
* yank buffer for the corresponding tile. The new yank buffer's tile
* may need to be clipped at its top and bottom if it is bigger than the
* old yank buffer's tile. We set the TRAILING coordinate of the clipped
* tile to that of the old yank buffer's tile.
*
* Results:
* Always returns 0.
*
* Side effects:
* May update the TRAILING coordinate of the corresponding tile
* in plowSpareUse.
*
* ----------------------------------------------------------------------------
*/
int
plowYankUpdatePaint(yankTp, pNum)
Tile *yankTp;
int pNum;
{
Tile *spareTp;
Point startPoint;
Plane *plane;
/*
* Walk down the inside of the LHS of yankTp, finding all tiles
* spareTp along that LHS and updating their client values.
* There may be more than one spareTp along this edge, owing
* to additional material added outside the original yank buffer
* that caused re-merging into maximal horizontal strips.
*/
startPoint.p_x = LEFT(yankTp);
startPoint.p_y = TOP(yankTp) - 1;
plane = plowSpareDef->cd_planes[pNum];
spareTp = (Tile *) NULL;
do
{
spareTp = TiSrPoint(spareTp, plane, &startPoint);
/*
* Only update if both tiles are of the same type.
* This should always be true except when yankTp is a space tile
* from the RHS of the original yank buffer that lies over a
* non-space tile in the new yank buffer (spare).
*/
if (TiGetTypeExact(spareTp) == TiGetTypeExact(yankTp))
{
if (TOP(spareTp) > TOP(yankTp))
(void) plowSplitY(spareTp, TOP(yankTp));
if (BOTTOM(spareTp) < BOTTOM(yankTp))
spareTp = plowSplitY(spareTp, BOTTOM(yankTp));
TiSetClient(spareTp, TiGetClient(yankTp));
}
startPoint.p_y = BOTTOM(spareTp) - 1;
} while (startPoint.p_y >= BOTTOM(yankTp));
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowUpdateLabels --
*
* For each label in the original cell that is attached to geometry that
* has moved in the plow yank cell, determine how far the label must move
* and move it.
*
* Results:
* None.
*
* Side effects:
* Moves labels.
* Sets plowLabelsChanged if we actually move a label.
*
* ----------------------------------------------------------------------------
*/
struct labelUpdate
{
Rect lu_rect; /* Label itself */
int lu_adjust; /* How much to adjust it rightwards */
};
void
plowUpdateLabels(yankDef, origDef, origArea)
CellDef *yankDef; /* Def containing plowed paint */
CellDef *origDef; /* Original def whose labels are to be adjusted */
Rect *origArea; /* Area in original def that was modified */
{
extern void DBUndoPutLabel(), DBUndoEraseLabel();
Rect yankSearch;
TileTypeBitMask typeBits;
struct labelUpdate lu;
Label *origLab;
int pNum;
for (origLab = origDef->cd_labels; origLab; origLab = origLab->lab_next)
{
/* Labels attached to space don't ever move */
if (origLab->lab_type == TT_SPACE)
continue;
/* Labels outside the area changed don't move either */
if (!GEO_TOUCH(&origLab->lab_rect, origArea))
continue;
/*
* If any of the tiles to which this label "belongs"
* moved far enough to touch or pass this label, drag
* it along with them.
*/
pNum = DBPlane(origLab->lab_type);
GeoTransRect(&plowYankTrans, &origLab->lab_rect, &lu.lu_rect);
lu.lu_adjust = 0;
yankSearch = lu.lu_rect;
yankSearch.r_xbot--, yankSearch.r_xtop++;
yankSearch.r_ybot--, yankSearch.r_ytop++;
TTMaskSetOnlyType(&typeBits, origLab->lab_type);
(void) DBSrPaintArea((Tile *) NULL, yankDef->cd_planes[pNum],
&yankSearch, &typeBits, plowCheckLabel,
(ClientData) &lu);
if (lu.lu_adjust)
{
lu.lu_rect.r_xbot += lu.lu_adjust;
lu.lu_rect.r_xtop += lu.lu_adjust;
DBUndoEraseLabel(origDef, origLab);
GeoTransRect(&plowInverseTrans, &lu.lu_rect, &origLab->lab_rect);
DBUndoPutLabel(origDef, origLab);
plowLabelsChanged = TRUE;
}
}
}
int
plowCheckLabel(tile, lu)
Tile *tile;
struct labelUpdate *lu;
{
int adjust;
/*
* If the RHS of the label touches the RHS of this tile, move
* the label with the leading edge; otherwise, move it with the
* trailing edge.
*/
if (lu->lu_rect.r_xtop == RIGHT(tile))
adjust = LEADING(tile) - lu->lu_rect.r_xtop;
else
adjust = TRAILING(tile) - lu->lu_rect.r_xbot;
if (adjust > lu->lu_adjust)
lu->lu_adjust = adjust;
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowUpdateCell --
*
* Called by DBCellEnum().
* Update the positions of each cell moved by plowing. The cu_client
* fields of various cells in the cell plane of yankDef have been updated
* as a result of plowing. For each cell with a non-zero cu_client, we
* find the corresponding one in the parent (the use with the same
* instance-id), rip it up, and replace it.
*
* Results:
* Returns 0 always.
*
* Side effects:
* Sets plowLabelsChanged if we have to move a cell.
* May cause the cell corresponding to 'use' to be re-placed
* in the original def, 'origDef'.
*
* ----------------------------------------------------------------------------
*/
int
plowUpdateCell(use, origDef)
CellUse *use; /* Use from yank def; corresponding use in 'def' will
* be moved if use->cu_client is non-zero.
*/
CellDef *origDef; /* Original def whose cells are to be adjusted based
* on the corresponding cells in yankDef.
*/
{
CellUse *origUse;
Transform newTrans;
int x, y;
if (use->cu_client == (ClientData)CLIENTDEFAULT || use->cu_client == (ClientData)0)
return (0);
/*
* Find the corresponding use in the original parent.
* This will be a use of the same def with the same
* instance identifier; if we can't find it, something
* very strange is going on.
*/
for (origUse = use->cu_def->cd_parents;
origUse;
origUse = origUse->cu_nextuse)
{
if (origUse->cu_parent == plowDummyUse->cu_def
&& strcmp(origUse->cu_id, use->cu_id) == 0)
break;
}
if (origUse == NULL)
{
TxError("Oops! Can't find cell use %s in parent\n", use->cu_id);
return (0);
}
plowLabelsChanged = TRUE;
/* Figure out how much to translate by based on the plowing direction */
x = y = 0;
switch (plowDirection)
{
case GEO_NORTH:
y = (int)CD2INT(use->cu_client);
break;
case GEO_SOUTH:
y = -(int)CD2INT(use->cu_client);
break;
case GEO_WEST:
x = -(int)CD2INT(use->cu_client);
break;
case GEO_EAST:
x = (int)CD2INT(use->cu_client);
break;
}
GeoTranslateTrans(&origUse->cu_transform, x, y, &newTrans);
DBDeleteCell(origUse);
DBWAreaChanged(origDef, &origUse->cu_bbox,
DBW_ALLWINDOWS, (TileTypeBitMask *) NULL);
DBSetTrans(origUse, &newTrans);
DBPlaceCell(origUse, origDef);
DBWAreaChanged(origDef, &origUse->cu_bbox,
DBW_ALLWINDOWS, (TileTypeBitMask *) NULL);
return (0);
}
/*
* ----------------------------------------------------------------------------
*
* plowUpdatePaintTile --
*
* Copy back a tile from the plowed planes into the orignal cell def.
* Instead of using the normal left and right coordinates of this tile,
* however, we use the leading/trailing edges.
*
* Results:
* Always return 0 to keep the search alive.
*
* Side effects:
* Paints a tile of the same type as 'tile' into the
* original def, after first transforming by the inverse
* transform of that used to rotate the original def
* into the plowing yank buffer.
*
* ----------------------------------------------------------------------------
*/
int
plowUpdatePaintTile(tile, ui)
Tile *tile; /* Tile in yanked, plowed def */
PaintUndoInfo *ui; /* Identifies original cell and plane being searched */
{
Rect r, rtrans;
TileType type = TiGetTypeExact(tile);
int pNum, pMask;
r.r_ybot = BOTTOM(tile);
r.r_ytop = TOP(tile);
r.r_xbot = TRAILING(tile);
r.r_xtop = LEADING(tile);
GeoTransRect(&plowInverseTrans, &r, &rtrans);
pMask = DBTypePlaneMaskTbl[type];
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
if (PlaneMaskHasPlane(pMask, pNum))
{
ui->pu_pNum = pNum;
DBPaintPlane(ui->pu_def->cd_planes[pNum],
&rtrans, DBWriteResultTbl[type], ui);
}
}
return (0);
}