/*
 * 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-7.4/resis/ResFract.c,v 1.1.1.1 2006/04/10 22:02:12 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;
}
