487 lines
11 KiB
C
487 lines
11 KiB
C
/*
|
|
* ResFract.c
|
|
*
|
|
* routines to convert a maximum horizontal rectangles database
|
|
* into one fractured in the manner of Horowitz's '83 Transactions
|
|
* on CAD paper.
|
|
*
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResFract.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "textio/txcommands.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/signals.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "database/databaseInt.h"
|
|
#include "utils/malloc.h"
|
|
#include "windows/windows.h"
|
|
#include "utils/main.h"
|
|
|
|
extern Tile *ResSplitX();
|
|
|
|
|
|
Tile *resSrTile;
|
|
Tile *resTopTile;
|
|
Plane *resFracPlane;
|
|
|
|
/* Forward declarations */
|
|
|
|
extern void ResCheckConcavity();
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------------------
|
|
*
|
|
* ResFracture -- Convert a maxiumum horizontal strips cell def into
|
|
* one where the split at each concave corner is in the direction
|
|
* with the least material of the same tiletype. This is done
|
|
* using TiSplitX and TiJoinY. Joins are only done on tiles with
|
|
* the same time; this implies that contacts should first be erased
|
|
* using ResDissolve contacts.
|
|
*
|
|
* We can't use DBSrPaintArea because the fracturing
|
|
* routines modify the database. This is essentially the same routine
|
|
* except that has to be careful that it doesn't merge away the
|
|
* current tile in the search.
|
|
*
|
|
*
|
|
* --------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ResFracture(plane, rect)
|
|
Plane *plane;
|
|
Rect *rect;
|
|
{
|
|
Point start;
|
|
Tile *tpnew;
|
|
TileType tt;
|
|
|
|
resFracPlane = plane;
|
|
|
|
start.p_x = rect->r_xbot;
|
|
start.p_y = rect->r_ytop - 1;
|
|
resSrTile = plane->pl_hint;
|
|
GOTOPOINT(resSrTile, &start);
|
|
|
|
/* Each iteration visits another tile on the LHS of the search area */
|
|
while (TOP(resSrTile) > rect->r_ybot)
|
|
{
|
|
/* Each iteration enumerates another tile */
|
|
enumerate:
|
|
plane->pl_hint = resSrTile;
|
|
if (SigInterruptPending)
|
|
return (1);
|
|
|
|
if ((tt = TiGetType(resSrTile)) != TT_SPACE)
|
|
{
|
|
resTopTile = RT(resSrTile);
|
|
while (RIGHT(resTopTile) > LEFT(resSrTile))
|
|
{
|
|
TileType ntt = TiGetType(resTopTile);
|
|
|
|
if (ntt != tt)
|
|
{
|
|
resTopTile = BL(resTopTile);
|
|
continue;
|
|
}
|
|
/* ok, we may have found a concave corner */
|
|
ResCheckConcavity(resSrTile, resTopTile, tt);
|
|
if (resTopTile == NULL) break;
|
|
if (BOTTOM(resTopTile) != TOP(resSrTile))
|
|
{
|
|
resTopTile = RT(resSrTile);
|
|
}
|
|
else
|
|
{
|
|
resTopTile=BL(resTopTile);
|
|
}
|
|
}
|
|
}
|
|
|
|
tpnew = TR(resSrTile);
|
|
if (LEFT(tpnew) < rect->r_xtop)
|
|
{
|
|
while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
|
|
if (BOTTOM(tpnew) >= BOTTOM(resSrTile) || BOTTOM(resSrTile) <= rect->r_ybot)
|
|
{
|
|
resSrTile = tpnew;
|
|
goto enumerate;
|
|
}
|
|
}
|
|
|
|
/* Each iteration returns one tile further to the left */
|
|
while (LEFT(resSrTile) > rect->r_xbot)
|
|
{
|
|
if (BOTTOM(resSrTile) <= rect->r_ybot)
|
|
return (0);
|
|
tpnew = LB(resSrTile);
|
|
resSrTile = BL(resSrTile);
|
|
if (BOTTOM(tpnew) >= BOTTOM(resSrTile) || BOTTOM(resSrTile) <= rect->r_ybot)
|
|
{
|
|
resSrTile = tpnew;
|
|
goto enumerate;
|
|
}
|
|
}
|
|
|
|
/* At left edge -- walk down to next tile along the left edge */
|
|
for (resSrTile = LB(resSrTile); RIGHT(resSrTile) <= rect->r_xbot;
|
|
resSrTile = TR(resSrTile))
|
|
/* Nothing */;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResCheckConcavity -- Called when two tiles of the same type are found.
|
|
* These tiles can form concave edges 4 different ways; check for
|
|
* each such case. When one is found, call the resWalk routines to
|
|
* decide whether any tiles need to be split.
|
|
*
|
|
* Results: none.
|
|
*
|
|
* Side Effects: may change the plane on which it acts. Can also modify
|
|
* the global variable resTopTile.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ResCheckConcavity(bot, top, tt)
|
|
Tile *bot, *top;
|
|
TileType tt;
|
|
|
|
{
|
|
Tile *tp;
|
|
int xlen, ylen;
|
|
|
|
/* corner #1:
|
|
* XXXXXXX
|
|
* YYYYYYY
|
|
* ^--here
|
|
*/
|
|
|
|
if (RIGHT(top) > RIGHT(bot) && TiGetType(TR(bot)) != tt)
|
|
{
|
|
int xpos = RIGHT(bot);
|
|
int ypos = BOTTOM(top);
|
|
xlen = xpos - resWalkleft(top, tt, xpos, ypos, NULL);
|
|
ylen = resWalkup(top, tt, xpos, ypos, NULL) - ypos;
|
|
if (xlen > ylen)
|
|
{
|
|
(void) resWalkup(top, tt, xpos, ypos, ResSplitX);
|
|
}
|
|
}
|
|
if (resTopTile == NULL) return;
|
|
|
|
/* corner #2:
|
|
* v--here
|
|
* XXXXXXX
|
|
* YYYYYYY
|
|
*/
|
|
|
|
if (RIGHT(top) < RIGHT(bot))
|
|
{
|
|
for (tp = TR(top); BOTTOM(tp) > BOTTOM(top); tp = LB(tp));
|
|
if (TiGetType(tp) != tt)
|
|
{
|
|
int xpos = RIGHT(top);
|
|
int ypos = BOTTOM(top);
|
|
xlen = xpos - resWalkleft(top, tt, xpos, ypos, NULL);
|
|
ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL);
|
|
if (xlen > ylen)
|
|
{
|
|
(void) resWalkdown(bot,tt,xpos,ypos,ResSplitX);
|
|
}
|
|
}
|
|
}
|
|
if (resTopTile == NULL) return;
|
|
|
|
/* corner #3:
|
|
* XXXXXXX
|
|
* YYYYYYY
|
|
* ^--here
|
|
*/
|
|
|
|
if (LEFT(top) < LEFT(bot))
|
|
{
|
|
for (tp = BL(bot); TOP(tp) < TOP(bot); tp = RT(tp));
|
|
if (TiGetType(tp) != tt)
|
|
{
|
|
int xpos = LEFT(bot);
|
|
int ypos = BOTTOM(top);
|
|
xlen = resWalkright(top, tt, xpos, ypos, NULL) - xpos;
|
|
ylen = resWalkup(top, tt, xpos, ypos, NULL) - ypos;
|
|
if (xlen > ylen)
|
|
{
|
|
(void) resWalkup(top, tt, xpos, ypos, ResSplitX);
|
|
}
|
|
}
|
|
}
|
|
if (resTopTile == NULL) return;
|
|
|
|
/* corner #4:
|
|
* v--here
|
|
* XXXXXXX
|
|
* YYYYYYY
|
|
*/
|
|
|
|
if (LEFT(top) > LEFT(bot) && TiGetType(BL(top)) != tt)
|
|
{
|
|
int xpos = LEFT(top);
|
|
int ypos = BOTTOM(top);
|
|
xlen = resWalkright(top, tt, xpos, ypos, NULL) - xpos;
|
|
ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL);
|
|
if (xlen > ylen)
|
|
{
|
|
(void) resWalkdown(bot, tt, xpos, ypos, ResSplitX);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* resWalk{up,down,left,right} -- move in the specified direction looking
|
|
* for tiles of a given type.
|
|
*
|
|
* Results: returns the coordinate that is the farthest point in the specified
|
|
* direction that one can walk and still be surrounded by material of
|
|
* a given type.
|
|
*
|
|
* Side Effects: if func is non-NULL, it is called on each tile intersected
|
|
* by the path. (Note that if the path moves along the edge of a tile,
|
|
* func is not called.)
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
resWalkup(tile, tt, xpos, ypos, func)
|
|
Tile *tile;
|
|
TileType tt;
|
|
int xpos,ypos;
|
|
Tile * (*func)();
|
|
|
|
{
|
|
Point pt;
|
|
Tile *tp;
|
|
|
|
pt.p_x = xpos;
|
|
while (TiGetType(tile) == tt)
|
|
{
|
|
if (xpos == LEFT(tile))
|
|
{
|
|
/* walk up left edge */
|
|
for (tp = BL(tile); TOP(tp) <= ypos; tp = RT(tp));
|
|
for (; BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
{
|
|
if (TiGetType(tp) != tt) return(BOTTOM(tp));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (func) tile = (*func)(tile,xpos);
|
|
}
|
|
pt.p_y = TOP(tile);
|
|
GOTOPOINT(tile, &pt);
|
|
}
|
|
return(BOTTOM(tile));
|
|
}
|
|
|
|
int
|
|
resWalkdown(tile, tt, xpos, ypos, func)
|
|
Tile *tile;
|
|
TileType tt;
|
|
int xpos, ypos;
|
|
Tile * (*func)();
|
|
|
|
{
|
|
Point pt;
|
|
Tile *tp;
|
|
Tile *endt;
|
|
|
|
pt.p_x = xpos;
|
|
while (TiGetType(tile) == tt)
|
|
{
|
|
if (xpos == LEFT(tile))
|
|
{
|
|
/* walk up left edge */
|
|
endt = NULL;
|
|
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
|
|
{
|
|
if (TiGetType(tp) != tt)
|
|
{
|
|
if (BOTTOM(tp) < ypos) endt = tp;
|
|
}
|
|
}
|
|
if (endt)
|
|
{
|
|
return TOP(endt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (func) tile = (*func)(tile, xpos);
|
|
}
|
|
pt.p_y = BOTTOM(tile) - 1;
|
|
GOTOPOINT(tile, &pt);
|
|
}
|
|
return(TOP(tile));
|
|
}
|
|
|
|
int
|
|
resWalkright(tile, tt, xpos, ypos, func)
|
|
Tile *tile;
|
|
TileType tt;
|
|
int xpos, ypos;
|
|
Tile * (*func)();
|
|
|
|
{
|
|
Point pt;
|
|
Tile *tp;
|
|
|
|
pt.p_y = ypos;
|
|
while (TiGetType(tile) == tt)
|
|
{
|
|
if (ypos == BOTTOM(tile))
|
|
{
|
|
/* walk along bottom edge */
|
|
for (tp = LB(tile); LEFT(tp) < xpos; tp = TR(tp));
|
|
for (; LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
|
{
|
|
if (TiGetType(tp) != tt) return(LEFT(tp));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (func) tile = (*func)(tile, ypos);
|
|
}
|
|
pt.p_x = RIGHT(tile);
|
|
GOTOPOINT(tile, &pt);
|
|
}
|
|
return(LEFT(tile));
|
|
}
|
|
|
|
int
|
|
resWalkleft(tile, tt, xpos, ypos, func)
|
|
Tile *tile;
|
|
TileType tt;
|
|
int xpos, ypos;
|
|
Tile * (*func)();
|
|
|
|
{
|
|
Point pt;
|
|
Tile *tp;
|
|
Tile *endt;
|
|
|
|
pt.p_y = ypos;
|
|
while (TiGetType(tile) == tt)
|
|
{
|
|
if (ypos == BOTTOM(tile))
|
|
{
|
|
/* walk along bottom edge */
|
|
endt = NULL;
|
|
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
|
|
{
|
|
if (TiGetType(tp) != tt)
|
|
{
|
|
if (LEFT(tp) < xpos) endt = tp;
|
|
}
|
|
}
|
|
if (endt)
|
|
{
|
|
return RIGHT(endt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (func) tile = (*func)(tile, ypos);
|
|
}
|
|
pt.p_x = LEFT(tile) - 1;
|
|
GOTOPOINT(tile, &pt);
|
|
}
|
|
return(RIGHT(tile));
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* ResSplitX -- calls TiSplitX, sets the tiletype,
|
|
* then tries to join tiles that share a common long edge.
|
|
*
|
|
* Results: returns new tile if tile was destroyed by a Join function.
|
|
*
|
|
* Side Effects: modifies the tile plane and the global variables
|
|
* resSrTile and resTopTile.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
Tile *
|
|
ResSplitX(tile, x)
|
|
Tile *tile;
|
|
int x;
|
|
|
|
{
|
|
TileType tt = TiGetType(tile);
|
|
Tile *tp = TiSplitX(tile, x);
|
|
Tile *tp2;
|
|
|
|
TiSetBody(tp,tt);
|
|
/* check to see if we can combine with the tiles above or below us */
|
|
tp2 = RT(tile);
|
|
if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile))
|
|
{
|
|
if (tp2 == resSrTile)
|
|
{
|
|
if (resTopTile == tile) resTopTile = NULL;
|
|
TiJoinY(tp2, tile, resFracPlane);
|
|
tile = tp2;
|
|
}
|
|
else
|
|
{
|
|
if (resTopTile == tp2) resTopTile = NULL;
|
|
TiJoinY(tile, tp2, resFracPlane);
|
|
}
|
|
}
|
|
tp2 = LB(tile);
|
|
if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile))
|
|
{
|
|
if (tp2 == resSrTile)
|
|
{
|
|
if (resTopTile == tile) resTopTile = NULL;
|
|
TiJoinY(tp2, tile, resFracPlane);
|
|
tile = tp2;
|
|
}
|
|
else
|
|
{
|
|
if (resTopTile == tp2) resTopTile = NULL;
|
|
TiJoinY(tile, tp2, resFracPlane);
|
|
}
|
|
}
|
|
/* do the same checks with the newly created tile */
|
|
tp2 = RT(tp);
|
|
if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tp) && RIGHT(tp2) == RIGHT(tp))
|
|
{
|
|
TiJoinY(tp2, tp, resFracPlane);
|
|
tp = tp2;
|
|
}
|
|
tp2 = LB(tp);
|
|
if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tp) && RIGHT(tp2) == RIGHT(tp))
|
|
{
|
|
TiJoinY(tp2, tp, resFracPlane);
|
|
}
|
|
return tile;
|
|
}
|