/* * 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 #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; 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; 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); }