magic/database/DBtiles.c

1453 lines
40 KiB
C
Raw Normal View History

/*
* DBtiles.c --
*
* Low-level tile primitives for the database.
* This includes area searching and all other primitives that
* need to know what lives in a tile body.
*
* *********************************************************************
* * 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/database/DBtiles.c,v 1.2 2010/06/24 12:37:15 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/signals.h"
#include "utils/hash.h"
#include "utils/undo.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/malloc.h"
/* Used by DBCheckMaxHStrips() and DBCheckMaxVStrips() */
struct dbCheck
{
int (*dbc_proc)();
Rect dbc_area;
ClientData dbc_cdata;
};
int dbCheckMaxHFunc(), dbCheckMaxVFunc();
/*
* --------------------------------------------------------------------
*
* dbEvalCorner --
*
* Used by DBTestNMInteract() to determine whether two non-
* Manhattan areas have crossing diagonals by evaluating the
* corner points of the area of intersection between the two
* tiles. This routine finds the distance from a point in
* the second triangle to the diagonal of the first, in both
* x and y. If the point is below or to the left, the
* distance is negative; otherwise the distance is positive.
*
* Results:
* 1 for a positive result, -1 for a negative result, and 0
* for the same result (point touches the diagonal).
*
* Side effects:
* None.
*
* --------------------------------------------------------------------
*/
int
dbEvalCorner(Point *p, // Point to evaluate
Rect *rect, // Triangular area bounding rectangle
TileType di) // Diagonal information for split rect
{
dlong D;
/* D is the distance from a point to the diagonal of the rectangle.
* The magnitude is not important. It only matters what the sign
* is, so return 1 for positive, -1 for negative, or 0.
*/
if (di & TT_DIRECTION)
D = (p->p_y - rect->r_ybot) * (rect->r_xtop - rect->r_xbot) -
(rect->r_xtop - p->p_x) * (rect->r_ytop - rect->r_ybot);
else
D = (p->p_y - rect->r_ybot) * (rect->r_xtop - rect->r_xbot) -
(p->p_x - rect->r_xbot) * (rect->r_ytop - rect->r_ybot);
if (D > 0) return 1;
if (D < 0) return -1;
return 0;
}
/*
* --------------------------------------------------------------------
*
* DBTestNMInteract --
*
* Determine if a tile (t2) interacts with (touches or overlaps)
* a triangular area (rect1, with diagonal split information in
* di1). Tile t2 may or may not be a split tile. If t2 is
* split, then diagonal split information is in di2.
*
* There are two distinct cases: DBSrPaintNMArea() looks for
* tiles that overlap the area of rect1, but extTestNMInteract()
* looks for tiles that both overlap or touch (indicating
* electrical connectivity between the two). "overlap_only"
* distinguishes between the two use cases. Set "overlap_only"
* to TRUE for overlap searches, and FALSE for interaction
* searches.
*
* Results:
*
* If overlap_only is TRUE:
* Return TRUE if the indicated areas overlap, FALSE if not.
* If overlap_only is FALSE:
* Return TRUE if the indicated areas touch or overlap, FALSE if not.
*
* Side effects:
* None.
*
* --------------------------------------------------------------------
*/
bool
DBTestNMInteract(Rect *rect1,
TileType tt1,
Tile *t2,
TileType di2,
bool overlap_only)
{
Rect rect2, r;
Point p;
int rheight, rwidth, rmax;
dlong f1, f2, f3, f4;
TileType tt2;
int pos, neg, touch, sign;
TiToRect(t2, &rect2);
/* Assuming that rect1 is a split area, then check if any part of t2
* overlaps the split side of interest in rect1, regardless of whether
* t2 is split or not. If there is no overlap, then return FALSE.
*/
rheight = rect1->r_ytop - rect1->r_ybot;
rwidth = rect1->r_xtop - rect1->r_xbot;
rmax = MAX(rheight, rwidth);
f1 = (rect2.r_ybot > MINFINITY + 2) ?
((dlong)(rect1->r_ytop - rect2.r_ybot) * rwidth) : DLONG_MAX;
f2 = (rect2.r_ytop < INFINITY - 2) ?
((dlong)(rect2.r_ytop - rect1->r_ybot) * rwidth) : DLONG_MAX;
if (tt1 & TT_SIDE)
{
/* Outside-of-triangle check---ignore sub-integer slivers */
if (rect2.r_xtop < INFINITY - 2)
{
f3 = (dlong)(rect1->r_xtop - rect2.r_xtop) * rheight;
f3 += rmax;
}
else
f3 = DLONG_MIN;
if ((tt1 & TT_DIRECTION) ? (f2 < f3) : (f1 < f3))
return FALSE;
}
else
{
/* Outside-of-triangle check---ignore sub-integer slivers */
if (rect2.r_xbot > MINFINITY + 2)
{
f4 = (dlong)(rect2.r_xbot - rect1->r_xbot) * rheight;
f4 += rmax;
}
else
f4 = DLONG_MIN;
if ((tt1 & TT_DIRECTION) ? (f1 < f4) : (f2 < f4))
return FALSE;
}
/* If t2 is not split, or its diagonal is the opposite of t1,
* or its side is the same as that of t1, then they overlap.
*/
if (!IsSplit(t2)) return TRUE;
tt2 = TiGetTypeExact(t2) | di2;
if ((tt1 & TT_DIRECTION) != (tt2 & TT_DIRECTION)) return TRUE;
// if ((tt1 & TT_SIDE) == (tt2 & TT_SIDE)) return TRUE;
/* Hard case: Same diagonal direction, opposite sides. To determine
* overlap, count which of the three points of triangle t2 land on
* one side or the other of the rect1 split diagonal. From those
* counts, determine if the triangles are overlapping, touching,
* or disjoint.
*/
/* Evaluate the three corners of the rect2 triangle */
pos = neg = touch = 0;
if (!(tt2 & TT_DIRECTION) || !(tt2 & TT_SIDE))
{
/* Evaluate the lower left corner */
sign = dbEvalCorner(&rect2.r_ll, rect1, tt1);
if (sign == 1)
pos++;
else if (sign == -1)
neg++;
else
touch++;
}
if (!(tt2 & TT_DIRECTION) || (tt2 & TT_SIDE))
{
/* Evaluate the upper right corner */
sign = dbEvalCorner(&rect2.r_ur, rect1, tt1);
if (sign == 1)
pos++;
else if (sign == -1)
neg++;
else
touch++;
}
if ((tt2 & TT_DIRECTION) || !(tt2 & TT_SIDE))
{
/* Evaluate the upper left corner */
p.p_x = rect2.r_xbot;
p.p_y = rect2.r_ytop;
sign = dbEvalCorner(&p, rect1, tt1);
if (sign == 1)
pos++;
else if (sign == -1)
neg++;
else
touch++;
}
if ((tt2 & TT_DIRECTION) || (tt2 & TT_SIDE))
{
/* Evaluate the lower right corner */
p.p_x = rect2.r_xtop;
p.p_y = rect2.r_ybot;
sign = dbEvalCorner(&p, rect1, tt1);
if (sign == 1)
pos++;
else if (sign == -1)
neg++;
else
touch++;
}
/* If side and direction match, then pos and neg need to be swapped */
if (((tt1 & TT_SIDE) && (tt1 & TT_DIRECTION)) ||
(!(tt1 & TT_SIDE) && !(tt1 & TT_DIRECTION)))
{
int temp = neg;
neg = pos;
pos = temp;
}
/* Return TRUE or FALSE depending on the values of pos, neg, and
* touch, and depending on whether overlap_only is set or not.
*/
if (pos == 3)
return FALSE; /* Fully disjoint */
else if (neg == 3)
{
if ((tt1 & TT_SIDE) != (tt2 & TT_SIDE))
return TRUE; /* Fully enclosed */
else
{
/* This is a trickier situation. Both triangles have
* the same TT_SIDE bit, but the triangular area of t2
* could still be outside of rect1. Need to check where
* the inside corner of rect1 lands with respect to the
* t2 diagonal.
*/
if (((tt1 & TT_SIDE) == 0) && ((tt1 & TT_DIRECTION) != 0))
{
sign = dbEvalCorner(&rect1->r_ll, &rect2, tt2);
}
else if (((tt1 & TT_SIDE) == 0) && ((tt1 & TT_DIRECTION) == 0))
{
p.p_x = rect1->r_ll.p_x;
p.p_y = rect1->r_ur.p_y;
sign = dbEvalCorner(&p, &rect2, tt2);
}
else if (((tt1 & TT_SIDE) != 0) && ((tt1 & TT_DIRECTION) == 0))
{
p.p_x = rect1->r_ur.p_x;
p.p_y = rect1->r_ll.p_y;
sign = dbEvalCorner(&p, &rect2, tt2);
}
else /* if (((tt1 & TT_SIDE) != 0) && ((tt1 & TT_DIRECTION) != 0)) */
{
sign = dbEvalCorner(&rect1->r_ur, &rect2, tt2);
}
/* Again, if side and direction match, then sign is backwards
*/
if (((tt2 & TT_SIDE) && (tt2 & TT_DIRECTION)) ||
(!(tt2 & TT_SIDE) && !(tt2 & TT_DIRECTION)))
sign = -sign;
if (sign == 1)
return FALSE; /* Fully disjoint */
else if (sign == -1)
return TRUE; /* Fully overlapping */
else if (overlap_only)
return FALSE; /* Touching but not overlapping */
else
return TRUE; /* Touching but not overlapping */
}
}
else if (overlap_only)
{
if ((touch > 0) && (neg + touch == 3))
return TRUE; /* Enclosed and touching */
else if ((touch > 0) && (pos + touch == 3))
return FALSE; /* Unenclosed but touching */
else
return TRUE; /* Partially overlapping */
}
else /* overlap_only == FALSE */
{
if ((touch > 0) && (neg + touch == 3))
return TRUE; /* Enclosed and touching */
else if ((touch > 0) && (pos + touch == 3))
return TRUE; /* Unenclosed but touching */
else
return TRUE; /* Partially overlapping */
}
}
/*
* --------------------------------------------------------------------
*
* DBSrPaintNMArea --
*
* Find all tiles overlapping a given triangular area whose types are
* contained in the mask supplied. Apply the given procedure to each
* such tile. The procedure should be of the following form:
*
* int
* func(tile, cdata)
* Tile *tile;
* TileType dinfo;
* ClientData cdata;
* {
* }
*
* This is equivalent to DBSrPaintArea, but is used when only one
* diagonal half (nonmanhattan extension) of the area should be searched.
* The bitmask for the diagonal are passed in "ttype" (diagonal, side and
* direction only are used, as in DBNMPaintPlane).
*
* Results:
* 0 is returned if the search completed normally. 1 is returned
* if it aborted.
*
* Side effects:
* Whatever side effects result from application of the
* supplied procedure.
*
* Notes:
* Do not call this routine on an infinite search area (e.g., area
* == &TiPlaneRect), even if "ttype" is set appropriately.
* --------------------------------------------------------------------
*/
#define IGNORE_LEFT 1
#define IGNORE_RIGHT 2
int
DBSrPaintNMArea(hintTile, plane, ttype, rect, mask, func, arg)
Tile *hintTile; /* Tile at which to begin search, if not NULL.
* If this is NULL, use the hint tile supplied
* with plane.
*/
Plane *plane; /* Plane in which tiles lie. This is used to
* provide a hint tile in case hintTile == NULL.
* The hint tile in the plane is updated to be
* the last tile visited in the area
* enumeration, if plane is non-NULL.
*/
TileType ttype; /* Information about the non-manhattan area to
* search; zero if area is manhattan.
*/
Rect *rect; /* Area to search. This area should not be
* degenerate. Tiles must OVERLAP the area.
*/
TileTypeBitMask *mask; /* Mask of those paint tiles to be passed to
* func.
*/
int (*func)(); /* Function to apply at each tile */
ClientData arg; /* Additional argument to pass to (*func)() */
{
Point start;
Tile *tp, *tpnew;
TileType tpt;
int rheight, rwidth, rmax;
dlong f1, f2, f3, f4;
int ignore_sides;
/* If the search area is not diagonal, return the result of the */
/* standard (manhattan) search function. */
if (!(ttype & TT_DIAGONAL))
return DBSrPaintArea(hintTile, plane, rect, mask, func, arg);
start.p_x = rect->r_xbot;
start.p_y = rect->r_ytop - 1;
tp = hintTile ? hintTile : PlaneGetHint(plane);
GOTOPOINT(tp, &start);
/* Each iteration visits another tile on the LHS of the search area */
while (TOP(tp) > rect->r_ybot)
{
/* Each iteration enumerates another tile */
nm_enum:
if (plane != (Plane *)NULL) PlaneSetHint(plane, tp);
if (SigInterruptPending)
return (1);
/* Check if the tile is in the (nonmanhattan) area, and continue */
/* the tile enumeration if it is not. */
/* Watch for calculations involving (M)INFINITY in tile (tp)! */
if (IsSplit(tp))
{
TileType tpdi = TiGetTypeExact(tp);
if (TTMaskHasType(mask, SplitLeftType(tp)))
if (DBTestNMInteract(rect, ttype, tp, tpdi, TRUE))
if ((*func)(tp, (TileType)TT_DIAGONAL, arg))
return 1;
if (TTMaskHasType(mask, SplitRightType(tp)))
if (DBTestNMInteract(rect, ttype, tp, tpdi | TT_SIDE, TRUE))
if ((*func)(tp, (TileType)TT_DIAGONAL | TT_SIDE, arg))
return 1;
}
else
{
if (TTMaskHasType(mask, TiGetType(tp)))
if (DBTestNMInteract(rect, ttype, tp, (TileType)0, TRUE))
if ((*func)(tp, (TileType)0, arg))
return 1;
}
#if 0
rheight = rect->r_ytop - rect->r_ybot;
rwidth = rect->r_xtop - rect->r_xbot;
rmax = MAX(rheight, rwidth);
f1 = (BOTTOM(tp) > MINFINITY + 2) ?
((dlong)(rect->r_ytop - BOTTOM(tp)) * rwidth) : DLONG_MAX;
f2 = (TOP(tp) < INFINITY - 2) ?
((dlong)(TOP(tp) - rect->r_ybot) * rwidth) : DLONG_MAX;
if (ttype & TT_SIDE)
{
/* Outside-of-triangle check---ignore sub-integer slivers */
if (RIGHT(tp) < INFINITY - 2)
{
f3 = (dlong)(rect->r_xtop - RIGHT(tp)) * rheight;
f3 += rmax;
}
else
f3 = DLONG_MIN;
if ((ttype & TT_DIRECTION) ? (f2 < f3) : (f1 < f3))
goto enum_next;
}
else
{
/* Outside-of-triangle check---ignore sub-integer slivers */
if (LEFT(tp) > MINFINITY + 2)
{
f4 = (dlong)(LEFT(tp) - rect->r_xbot) * rheight;
f4 += rmax;
}
else
f4 = DLONG_MIN;
if ((ttype & TT_DIRECTION) ? (f1 < f4) : (f2 < f4))
goto enum_next;
}
/* Secondary checks---if tile is also non-Manhattan, is */
/* either side of it outside the area? If so, restrict it. */
/* This check is only necessary if the split directions are */
/* the same, so we have to see if either of the neighboring */
/* points is also inside the search triangle. */
ignore_sides = 0;
if (IsSplit(tp))
{
if (!TTMaskHasType(mask, SplitLeftType(tp)))
ignore_sides |= IGNORE_LEFT;
if (!TTMaskHasType(mask, SplitRightType(tp)))
ignore_sides |= IGNORE_RIGHT;
tpt = TiGetTypeExact(tp);
if ((tpt & TT_DIRECTION) == (ttype & TT_DIRECTION))
{
f3 = (LEFT(tp) > MINFINITY + 2) ?
((dlong)(rect->r_xtop - LEFT(tp)) * rheight) : DLONG_MAX;
f4 = (RIGHT(tp) < INFINITY - 2) ?
((dlong)(RIGHT(tp) - rect->r_xbot) * rheight) : DLONG_MAX;
if (ttype & TT_SIDE)
{
/* Ignore sub-integer slivers */
if (f4 != DLONG_MAX) f4 -= rmax;
if (f3 != DLONG_MAX) f3 += rmax;
if (ttype & TT_DIRECTION)
{
if ((f2 < f3) && (f1 > f4))
/* Tile bottom left is outside search area */
ignore_sides |= IGNORE_LEFT;
}
else
{
if ((f1 < f3) && (f2 > f4))
/* Tile top left is outside search area */
ignore_sides |= IGNORE_LEFT;
}
}
else
{
/* Ignore sub-integer slivers */
if (f4 != DLONG_MAX) f4 += rmax;
if (f3 != DLONG_MAX) f3 -= rmax;
if (ttype & TT_DIRECTION)
{
if ((f2 > f3) && (f1 < f4))
/* Tile top right is outside search area */
ignore_sides |= IGNORE_RIGHT;
}
else
{
if ((f1 > f3) && (f2 < f4))
/* Tile bottom right is outside search area */
ignore_sides |= IGNORE_RIGHT;
}
}
}
/* If the tile is larger than the search area or overlaps */
/* the search area, we need to check if one of the sides */
/* of the tile is disjoint from the search area. */
rheight = TOP(tp) - BOTTOM(tp);
rwidth = RIGHT(tp) - LEFT(tp);
rmax = MAX(rheight, rwidth);
f1 = (TOP(tp) < INFINITY - 2) ?
((dlong)(TOP(tp) - rect->r_ybot) * rwidth) : DLONG_MAX;
f2 = (BOTTOM(tp) > MINFINITY + 2) ?
((dlong)(rect->r_ytop - BOTTOM(tp)) * rwidth) : DLONG_MAX;
f3 = (RIGHT(tp) < INFINITY - 2) ?
((dlong)(RIGHT(tp) - rect->r_xtop) * rheight) : DLONG_MAX;
f4 = (LEFT(tp) > MINFINITY + 2) ?
((dlong)(rect->r_xbot - LEFT(tp)) * rheight) : DLONG_MAX;
/* ignore sub-integer slivers */
if (f4 < DLONG_MAX) f4 += rmax;
if (f3 < DLONG_MAX) f3 += rmax;
if (SplitDirection(tp) ? (f1 < f4) : (f2 < f4))
ignore_sides |= IGNORE_LEFT;
if (SplitDirection(tp) ? (f2 < f3) : (f1 < f3))
ignore_sides |= IGNORE_RIGHT;
/* May call function twice to paint both sides of */
/* the split tile, if necessary. */
if (!(ignore_sides & IGNORE_LEFT))
{
if ((*func)(tp, (TileType)TT_DIAGONAL, arg)) return (1);
}
if (!(ignore_sides & IGNORE_RIGHT))
{
if ((*func)(tp, (TileType)TT_DIAGONAL | TT_SIDE, arg)) return (1);
}
}
else
if (TTMaskHasType(mask, TiGetType(tp)) && (*func)(tp, (TileType)0, arg))
return (1);
#endif
enum_next:
tpnew = TR(tp);
if (LEFT(tpnew) < rect->r_xtop)
{
while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto nm_enum;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tp) > rect->r_xbot)
{
if (BOTTOM(tp) <= rect->r_ybot)
return (0);
tpnew = LB(tp);
tp = BL(tp);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto nm_enum;
}
}
/* At left edge -- walk down to next tile along the left edge */
for (tp = LB(tp); RIGHT(tp) <= rect->r_xbot; tp = TR(tp))
/* Nothing */;
}
return (0);
}
/*
* --------------------------------------------------------------------
*
* DBSrPaintArea --
*
* Find all tiles overlapping a given area whose types are contained
* in the mask supplied. Apply the given procedure to each such tile.
* The procedure should be of the following form:
*
* int
* func(tile, cdata)
* Tile *tile;
* TileType dinfo;
* ClientData cdata;
* {
* }
*
* Func normally should return 0. If it returns 1 then the search
* will be aborted. WARNING: THE CALLED PROCEDURE MAY NOT MODIFY
* THE PLANE BEING SEARCHED!!!
*
* NOTE:
*
* Results:
* 0 is returned if the search completed normally. 1 is returned
* if it aborted.
*
* Side effects:
* Whatever side effects result from application of the
* supplied procedure.
*
* --------------------------------------------------------------------
*/
int
DBSrPaintArea(hintTile, plane, rect, mask, func, arg)
Tile *hintTile; /* Tile at which to begin search, if not NULL.
* If this is NULL, use the hint tile supplied
* with plane.
*/
Plane *plane; /* Plane in which tiles lie. This is used to
* provide a hint tile in case hintTile == NULL.
* The hint tile in the plane is updated to be
* the last tile visited in the area
* enumeration.
*/
Rect *rect; /* Area to search. This area should not be
* degenerate. Tiles must OVERLAP the area.
*/
TileTypeBitMask *mask; /* Mask of those paint tiles to be passed to
* func.
*/
int (*func)(); /* Function to apply at each tile */
ClientData arg; /* Additional argument to pass to (*func)() */
{
Point start;
Tile *tp, *tpnew;
start.p_x = rect->r_xbot;
start.p_y = rect->r_ytop - 1;
tp = hintTile ? hintTile : PlaneGetHint(plane);
GOTOPOINT(tp, &start);
/* Each iteration visits another tile on the LHS of the search area */
while (TOP(tp) > rect->r_ybot)
{
/* Each iteration enumerates another tile */
enumerate:
PlaneSetHint(plane, tp);
if (SigInterruptPending)
return (1);
/* Only perform func() on diagonal tiles if the mask includes the */
/* tile type for either the left or right sides of the tile (could */
/* also use top and bottom)---by definition, opposite sides must */
/* be the two tile types defined by the diagonal split. */
if (IsSplit(tp))
{
/* May call function twice to paint both sides of */
/* the split tile, if necessary. */
/* f1 to f4 are used to find if the search box rect */
/* is completely outside the triangle. If so, do */
/* not call the function func(). */
/* Watch for calculations involving (M)INFINITY */
int theight, twidth;
dlong f1, f2, f3, f4;
theight = TOP(tp) - BOTTOM(tp);
twidth = RIGHT(tp) - LEFT(tp);
f1 = (rect->r_ybot > MINFINITY + 2) ?
(dlong)(TOP(tp) - rect->r_ybot) * twidth : DLONG_MAX;
f2 = (rect->r_ytop < INFINITY - 2) ?
(dlong)(rect->r_ytop - BOTTOM(tp)) * twidth : DLONG_MAX;
if (TTMaskHasType(mask, SplitLeftType(tp)))
{
/* !Outside-of-triangle check */
f4 = (rect->r_xbot > MINFINITY + 2) ?
(dlong)(rect->r_xbot - LEFT(tp)) * theight : DLONG_MIN;
if (SplitDirection(tp) ? (f1 > f4) : (f2 > f4))
{
if ((*func)(tp, (TileType)TT_DIAGONAL, arg)) return (1);
}
}
if (TTMaskHasType(mask, SplitRightType(tp)))
{
/* !Outside-of-triangle check */
f3 = (rect->r_xtop < INFINITY - 2) ?
(dlong)(RIGHT(tp) - rect->r_xtop) * theight : DLONG_MIN;
if (SplitDirection(tp) ? (f2 > f3) : (f1 > f3))
{
if ((*func)(tp, (TileType)TT_DIAGONAL | TT_SIDE, arg)) return (1);
}
}
}
else
if (TTMaskHasType(mask, TiGetType(tp)) && (*func)(tp, (TileType)0, arg))
return (1);
tpnew = TR(tp);
if (LEFT(tpnew) < rect->r_xtop)
{
while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tp) > rect->r_xbot)
{
if (BOTTOM(tp) <= rect->r_ybot)
return (0);
tpnew = LB(tp);
tp = BL(tp);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* At left edge -- walk down to next tile along the left edge */
for (tp = LB(tp); RIGHT(tp) <= rect->r_xbot; tp = TR(tp))
/* Nothing */;
}
return (0);
}
/*
* --------------------------------------------------------------------
*
* DBSrPaintClient --
*
* Find all tiles overlapping a given area whose types are contained
* in the mask supplied, and whose ti_client field matches 'client'.
* Apply the given procedure to each such tile. The procedure should
* be of the following form:
*
* int
* func(tile, cdata)
* Tile *tile;
* TileType dinfo;
* ClientData cdata;
* {
* }
*
* Func normally should return 0. If it returns 1 then the search
* will be aborted.
*
* Results:
* 0 is returned if the search completed normally. 1 is returned
* if it aborted.
*
* Side effects:
* Whatever side effects result from application of the
* supplied procedure.
*
* --------------------------------------------------------------------
*/
int
DBSrPaintClient(hintTile, plane, rect, mask, client, func, arg)
Tile *hintTile; /* Tile at which to begin search, if not NULL.
* If this is NULL, use the hint tile supplied
* with plane.
*/
Plane *plane; /* Plane in which tiles lie. This is used to
* provide a hint tile in case hintTile == NULL.
* The hint tile in the plane is updated to be
* the last tile visited in the area
* enumeration.
*/
Rect *rect; /* Area to search. This area should not be
* degenerate. Tiles must OVERLAP the area.
*/
TileTypeBitMask *mask; /* Mask of those paint tiles to be passed to
* func.
*/
ClientData client; /* The ti_client field of each tile must
* match this.
*/
int (*func)(); /* Function to apply at each tile */
ClientData arg; /* Additional argument to pass to (*func)() */
{
Point start;
Tile *tp, *tpnew;
start.p_x = rect->r_xbot;
start.p_y = rect->r_ytop - 1;
tp = hintTile ? hintTile : PlaneGetHint(plane);
GOTOPOINT(tp, &start);
/* Each iteration visits another tile on the LHS of the search area */
while (TOP(tp) > rect->r_ybot)
{
/* Each iteration enumerates another tile */
enumerate:
PlaneSetHint(plane, tp);
if (SigInterruptPending)
return (1);
/* Only perform func() on diagonal tiles if the mask includes the */
/* tile type for either the left or right sides of the tile (could */
/* also use top and bottom)---by definition, opposite sides must */
/* be the two tile types defined by the diagonal split. */
if (IsSplit(tp))
{
/* May call function twice to paint both sides of */
/* the split tile, if necessary. */
/* f1 to f4 are used to find if the search box rect */
/* is completely outside the triangle. If so, do */
/* not call the function func(). */
/* Watch for calculations involving (M)INFINITY */
int theight, twidth;
dlong f1, f2, f3, f4;
theight = TOP(tp) - BOTTOM(tp);
twidth = RIGHT(tp) - LEFT(tp);
f1 = (rect->r_ybot > MINFINITY + 2) ?
(dlong)(TOP(tp) - rect->r_ybot) * (dlong)twidth : DLONG_MAX;
f2 = (rect->r_ytop < INFINITY - 2) ?
(dlong)(rect->r_ytop - BOTTOM(tp)) * (dlong)twidth : DLONG_MAX;
if (TTMaskHasType(mask, SplitLeftType(tp)))
{
/* !Outside-of-triangle check */
f4 = (rect->r_xbot > MINFINITY + 2) ?
(dlong)(rect->r_xbot - LEFT(tp)) * (dlong)theight : DLONG_MIN;
if (SplitDirection(tp) ? (f1 > f4) : (f2 > f4))
{
if ((tp->ti_client == client) && (*func)(tp, (TileType)TT_DIAGONAL, arg))
return (1);
}
}
if (TTMaskHasType(mask, SplitRightType(tp)))
{
/* !Outside-of-triangle check */
f3 = (rect->r_xtop < INFINITY - 2) ?
(dlong)(RIGHT(tp) - rect->r_xtop) * (dlong)theight : DLONG_MIN;
if (SplitDirection(tp) ? (f2 > f3) : (f1 > f3))
{
if ((tp->ti_client == client) && (*func)(tp, (TileType)TT_DIAGONAL
| TT_SIDE, arg))
return (1);
}
}
}
else
if (TTMaskHasType(mask, TiGetType(tp)) && tp->ti_client == client
&& (*func)(tp, (TileType)0, arg))
return (1);
tpnew = TR(tp);
if (LEFT(tpnew) < rect->r_xtop)
{
while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tp) > rect->r_xbot)
{
if (BOTTOM(tp) <= rect->r_ybot)
return (0);
tpnew = LB(tp);
tp = BL(tp);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* At left edge -- walk down to next tile along the left edge */
for (tp = LB(tp); RIGHT(tp) <= rect->r_xbot; tp = TR(tp))
/* Nothing */;
}
return (0);
}
/*
* --------------------------------------------------------------------
*
* DBResetTilePlane --
*
* Reset the ti_client fields of all tiles in a paint tile plane to
* the value 'cdata'.
*
* Results:
* None.
*
* Side effects:
* Resets the ti_client fields of all tiles.
*
* --------------------------------------------------------------------
*/
void
DBResetTilePlane(plane, cdata)
Plane *plane; /* Plane whose tiles are to be reset */
ClientData cdata;
{
Tile *tp, *tpnew;
2025-01-31 17:28:47 +01:00
const Rect *rect = &TiPlaneRect;
/* Start with the leftmost non-infinity tile in the plane */
tp = TR(plane->pl_left);
/* Each iteration visits another tile on the LHS of the search area */
while (TOP(tp) > rect->r_ybot)
{
/* Each iteration resets another tile */
enumerate:
tp->ti_client = cdata;
/* Move along to the next tile */
tpnew = TR(tp);
if (LEFT(tpnew) < rect->r_xtop)
{
while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tp) > rect->r_xbot)
{
if (BOTTOM(tp) <= rect->r_ybot)
return;
tpnew = LB(tp);
tp = BL(tp);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* At left edge -- walk down to next tile along the left edge */
for (tp = LB(tp); RIGHT(tp) <= rect->r_xbot; tp = TR(tp))
/* Nothing */;
}
}
/*
* --------------------------------------------------------------------
*
* DBResetTilePlaneSpecial --
*
* This routine works like DBResetTilePlane(), but is designed
* specifically to be run after extFindNodes() or ExtFindRegions()
* to check for split tiles that have an allocated ExtSplitRegion
* structure in the ClientData; this needs to be freed before
* resetting the ClientData value to "cdata". It is not necessary
* to know anything about the ExtSplitRegion structure other than
* the condition under which it can be expected to be present,
* which is a split tile with neither side having type TT_SPACE.
*
* Results:
* None.
*
* Side effects:
* Resets the ti_client fields of all tiles.
*
* --------------------------------------------------------------------
*/
void
DBResetTilePlaneSpecial(plane, cdata)
Plane *plane; /* Plane whose tiles are to be reset */
ClientData cdata;
{
Tile *tp, *tpnew;
const Rect *rect = &TiPlaneRect;
/* Start with the leftmost non-infinity tile in the plane */
tp = TR(plane->pl_left);
/* Each iteration visits another tile on the LHS of the search area */
while (TOP(tp) > rect->r_ybot)
{
/* Each iteration resets another tile */
enumerate:
if (IsSplit(tp))
if ((TiGetLeftType(tp) != TT_SPACE) && (TiGetRightType(tp) != TT_SPACE))
if (tp->ti_client != cdata)
{
ASSERT(TiGetBody((Tile *)tp->ti_client) == CLIENTDEFAULT,
"DBResetTilePlaneSpecial");
freeMagic(tp->ti_client);
}
tp->ti_client = cdata;
/* Move along to the next tile */
tpnew = TR(tp);
if (LEFT(tpnew) < rect->r_xtop)
{
while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* Each iteration returns one tile further to the left */
while (LEFT(tp) > rect->r_xbot)
{
if (BOTTOM(tp) <= rect->r_ybot)
return;
tpnew = LB(tp);
tp = BL(tp);
if (BOTTOM(tpnew) >= BOTTOM(tp) || BOTTOM(tp) <= rect->r_ybot)
{
tp = tpnew;
goto enumerate;
}
}
/* At left edge -- walk down to next tile along the left edge */
for (tp = LB(tp); RIGHT(tp) <= rect->r_xbot; tp = TR(tp))
/* Nothing */;
}
}
/*
* --------------------------------------------------------------------
*
* DBFreePaintPlane --
*
* Deallocate all tiles in a paint tile plane of a given CellDef.
* Don't deallocate the four boundary tiles, or the plane itself.
*
* This is a procedure internal to the database. The only reason
* it lives in DBtiles.c rather than DBcellsubr.c is that it requires
* intimate knowledge of the contents of paint tiles and tile planes.
*
* Results:
* None.
*
* Side effects:
* Deallocates a lot of memory.
*
* *** WARNING ***
*
* This procedure uses a carfully constructed non-recursive area
* enumeration algorithm. Care is taken to not access a tile that has
* been deallocated.
*
* --------------------------------------------------------------------
*/
void
DBFreePaintPlane(plane)
Plane *plane; /* Plane whose storage is to be freed */
{
Tile *tp, *tpnew;
2025-01-31 17:28:47 +01:00
const Rect *rect = &TiPlaneRect;
/* Start with the bottom-right non-infinity tile in the plane */
tp = BL(plane->pl_right);
Tile *delayed = NULL;
/* Each iteration visits another tile on the RHS of the search area */
while (BOTTOM(tp) < rect->r_ytop)
{
enumerate:
#define CLIP_TOP(t) (MIN(TOP(t),rect->r_ytop))
/* Move along to the next tile to the left */
if (LEFT(tp) > rect->r_xbot)
{
tpnew = BL(tp);
while (TOP(tpnew) <= rect->r_ybot) tpnew = RT(tpnew);
if (CLIP_TOP(tpnew) <= CLIP_TOP(tp))
{
tp = tpnew;
goto enumerate;
}
}
/* Each iteration returns one tile further to the right */
while (RIGHT(tp) < rect->r_xtop)
{
TiFree1(&delayed, tp);
tpnew = RT(tp); /* deref of delayed */
tp = TR(tp); /* deref of delayed */
if (CLIP_TOP(tpnew) <= CLIP_TOP(tp) && BOTTOM(tpnew) < rect->r_ytop)
{
tp = tpnew;
goto enumerate;
}
}
TiFree1(&delayed, tp);
/* At right edge -- walk up to next tile along the right edge */
tp = RT(tp); /* deref of delayed */
if (BOTTOM(tp) < rect->r_ytop) {
while(LEFT(tp) >= rect->r_xtop) tp = BL(tp);
}
}
TiFreeIf(delayed);
}
/*
* --------------------------------------------------------------------
*
* DBClearCellPlane --
*
* Removes all CellUses from a def's cell plane. Does not remove the
* cell plane itself.
*
* Results:
* None.
*
* Side effects:
* Deallocates a lot of memory.
*
* --------------------------------------------------------------------
*/
void
DBClearCellPlane(def)
CellDef *def;
{
int dbDeleteCellUse(); /* Forward reference */
/* Don't let this search be interrupted. */
SigDisableInterrupts();
/* Remove everything from the BPlane */
/* Do not use BPDelete() inside a BPEnum loop. Use DBSrCellUses */
/* to get a linked list of cell instances, then remove each one. */
DBSrCellUses(def, dbDeleteCellUse, (ClientData)def);
SigEnableInterrupts();
}
/*
* --------------------------------------------------------------------
*
* dbDeleteCellUse ---
*
* Callback function from DBSrCellUses, calls BPDelete to remove a
* cell use from the cell plane
*
* --------------------------------------------------------------------
*/
int dbDeleteCellUse(CellUse *use, ClientData arg)
{
CellDef *def = (CellDef *)arg;
CellUse *defuses, *lastuse;
dbInstanceUnplace(use);
if (UndoIsEnabled())
DBUndoCellUse(use, UNDO_CELL_DELETE);
/* Remove use from cd_parents of the use's def */
lastuse = (CellUse *)NULL;
for (defuses = use->cu_def->cd_parents; defuses ; defuses = defuses->cu_nextuse)
{
if (defuses == use)
{
if (lastuse)
lastuse->cu_nextuse = defuses->cu_nextuse;
else
use->cu_def->cd_parents = defuses->cu_nextuse;
defuses->cu_nextuse = (CellUse *)NULL;
break;
}
lastuse = defuses;
}
if (use->cu_id) freeMagic(use->cu_id);
freeMagic(use);
return 0;
}
/*
* --------------------------------------------------------------------
*
* DBCheckMaxHStrips --
*
* Check the maximal horizontal strip property for the
* tile plane 'plane' over the area 'area'.
*
* Results:
* Normally returns 0; returns 1 if the procedure
* (*proc)() returned 1 or if the search were
* aborted with an interrupt.
*
* Side effects:
* Calls the procedure (*proc)() for each offending tile.
* This procedure should have the following form:
*
* int
* proc(tile, side, cdata)
* Tile *tile;
* int side;
* ClientData cdata;
* {
* }
*
* The client data is the argument 'cdata' passed to us.
* The argument 'side' is one of GEO_NORTH, GEO_SOUTH,
* GEO_EAST, or GEO_WEST, and indicates which side of
* the tile the strip property was violated on.
* If (*proc)() returns 1, we abort and return 1
* to our caller.
*
* --------------------------------------------------------------------
*/
int
DBCheckMaxHStrips(plane, area, proc, cdata)
Plane *plane; /* Search this plane */
Rect *area; /* Process all tiles in this area */
int (*proc)(); /* Filter procedure: see above */
ClientData cdata; /* Passed to (*proc)() */
{
struct dbCheck dbc;
dbc.dbc_proc = proc;
dbc.dbc_area = *area;
dbc.dbc_cdata = cdata;
return (DBSrPaintArea((Tile *) NULL, plane, area,
&DBAllTypeBits, dbCheckMaxHFunc, (ClientData) &dbc));
}
/*
* dbCheckMaxHFunc --
*
* Filter function for above.
* See the description at the top.
*/
int
dbCheckMaxHFunc(tile, dinfo, dbc)
Tile *tile;
TileType dinfo; /* (unused) */
struct dbCheck *dbc;
{
Tile *tp;
/*
* Property 1:
* No tile to the left or to the right should have the same
* type as 'tile'.
*/
if (RIGHT(tile) < dbc->dbc_area.r_xtop)
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp))
if (TiGetType(tp) == TiGetType(tile))
if ((*dbc->dbc_proc)(tile, GEO_EAST, dbc->dbc_cdata))
return (1);
if (LEFT(tile) > dbc->dbc_area.r_xbot)
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
if (TiGetType(tp) == TiGetType(tile))
if ((*dbc->dbc_proc)(tile, GEO_WEST, dbc->dbc_cdata))
return (1);
/*
* Property 2:
* No tile to the top or bottom should be of the same type and
* have the same width.
*/
if (TOP(tile) < dbc->dbc_area.r_ytop)
{
tp = RT(tile);
if (TiGetType(tp) == TiGetType(tile)
&& LEFT(tp) == LEFT(tile)
&& RIGHT(tp) == RIGHT(tile))
if ((*dbc->dbc_proc)(tile, GEO_NORTH, dbc->dbc_cdata))
return (1);
}
if (BOTTOM(tile) > dbc->dbc_area.r_ybot)
{
tp = LB(tile);
if (TiGetType(tp) == TiGetType(tile)
&& LEFT(tp) == LEFT(tile)
&& RIGHT(tp) == RIGHT(tile))
if ((*dbc->dbc_proc)(tile, GEO_SOUTH, dbc->dbc_cdata))
return (1);
}
return (0);
}
/*
* --------------------------------------------------------------------
*
* DBCheckMaxVStrips --
*
* Check the maximal vertical strip property for the
* tile plane 'plane' over the area 'area'.
*
* Results:
* Normally returns 0; returns 1 if the procedure
* (*proc)() returned 1 or if the search were
* aborted with an interrupt.
*
* Side effects:
* See DBCheckMaxHStrips() above.
*
* --------------------------------------------------------------------
*/
int
DBCheckMaxVStrips(plane, area, proc, cdata)
Plane *plane; /* Search this plane */
Rect *area; /* Process all tiles in this area */
int (*proc)(); /* Filter procedure: see above */
ClientData cdata; /* Passed to (*proc)() */
{
struct dbCheck dbc;
dbc.dbc_proc = proc;
dbc.dbc_area = *area;
dbc.dbc_cdata = cdata;
return (DBSrPaintArea((Tile *) NULL, plane, area,
&DBAllTypeBits, dbCheckMaxVFunc, (ClientData) &dbc));
}
/*
* dbCheckMaxVFunc --
*
* Filter function for above.
* See the description at the top.
*/
int
dbCheckMaxVFunc(tile, dinfo, dbc)
Tile *tile;
TileType dinfo; /* (unused) */
struct dbCheck *dbc;
{
Tile *tp;
/*
* Property 1:
* No tile to the top or to the bottom should have the same
* type as 'tile'.
*/
if (TOP(tile) < dbc->dbc_area.r_ytop)
for (tp = RT(tile); RIGHT(tp) > LEFT(tile); tp = BL(tp))
if (TiGetType(tp) == TiGetType(tile))
if ((*dbc->dbc_proc)(tile, GEO_NORTH, dbc->dbc_cdata))
return (1);
if (BOTTOM(tile) > dbc->dbc_area.r_ybot)
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
if (TiGetType(tp) == TiGetType(tile))
if ((*dbc->dbc_proc)(tile, GEO_SOUTH, dbc->dbc_cdata))
return (1);
/*
* Property 2:
* No tile to the left or right should be of the same type and
* have the same height.
*/
if (RIGHT(tile) < dbc->dbc_area.r_xtop)
{
tp = TR(tile);
if (TiGetType(tp) == TiGetType(tile)
&& BOTTOM(tp) == BOTTOM(tile)
&& TOP(tp) == TOP(tile))
if ((*dbc->dbc_proc)(tile, GEO_EAST, dbc->dbc_cdata))
return (1);
}
if (LEFT(tile) > dbc->dbc_area.r_xbot)
{
tp = BL(tile);
if (TiGetType(tp) == TiGetType(tile)
&& BOTTOM(tp) == BOTTOM(tile)
&& TOP(tp) == TOP(tile))
if ((*dbc->dbc_proc)(tile, GEO_WEST, dbc->dbc_cdata))
return (1);
}
return (0);
}