/*
 * DBbound.c --
 *
 * Computation of boundaries of a database tile plane.
 *
 *     *********************************************************************
 *     * 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/DBbound.c,v 1.2 2008/12/11 04:20:04 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "database/database.h"
#include "tiles/tile.h"


typedef struct dbcellboundstruct
{
    Rect *area;
    Rect *extended;
    bool found;
} DBCellBoundStruct;

/*
 * --------------------------------------------------------------------
 * DBBoundCellPlane ---
 *
 *	Find the extents of all subcells of the cell "def", both the
 *	extent of geometry (rect) and the extent of geometry plus any
 *	labels extending outside the extent of geometry (extended).
 *
 * Results:
 *	TRUE if subcells were found and measured;  FALSE if no subcells
 *	were found (in which case "extended" and "rect" may not be
 *	valid).
 *
 * Side effects:
 *	Values may be recorded in "extended" and "rect".
 * --------------------------------------------------------------------
 */

int
DBBoundCellPlane(
    CellDef *def,
    Rect *extended,
    Rect *rect)
{
    TreeFilter filter;
    DBCellBoundStruct cbs;
    int dbCellBoundFunc();
    Plane *plane;

    filter.tf_func = NULL;
    filter.tf_arg = (ClientData)&cbs;

    cbs.area = rect;
    cbs.extended = extended;
    cbs.found = FALSE;

    *rect = GeoNullRect;
    if (DBSrCellPlaneArea(def->cd_cellPlane, &TiPlaneRect,
		dbCellBoundFunc, (ClientData) &filter) == 0)
	return cbs.found;
    else
	return -1;
}

int
dbCellBoundFunc(
    CellUse *use,
    TreeFilter *fp)
{
    DBCellBoundStruct *cbs;

    cbs = (DBCellBoundStruct *)fp->tf_arg;

    if (cbs->found)
    {
	GeoInclude(&use->cu_extended, cbs->extended);
	GeoInclude(&use->cu_bbox, cbs->area);
    }
    else
    {
	*cbs->extended = use->cu_extended;
	*cbs->area = use->cu_bbox;
	cbs->found = TRUE;
    }
    return 0;
}

/*
 * --------------------------------------------------------------------
 *
 * DBBoundPlane --
 *
 * Determine the bounding rectangle for the supplied tile plane.
 * The bounding rectangle is the smallest rectangle that completely
 * encloses all non-space tiles.
 *
 * If the tile plane is completely empty, we return a 0x0 bounding
 * box at the origin.
 *
 * Results:
 *	TRUE if the tile plane contains any geometry, FALSE
 *	if it is completely empty.
 *
 * Side effects:
 *	Sets *rect to the bounding rectangle.
 *
 * --------------------------------------------------------------------
 */

bool
DBBoundPlane(
    Plane *plane,
    Rect *rect)
{
    Tile *left, *right, *top, *bottom, *tp;

    left = plane->pl_left;
    right = plane->pl_right;
    top = plane->pl_top;
    bottom = plane->pl_bottom;

    rect->r_ur = TiPlaneRect.r_ll;
    rect->r_ll = TiPlaneRect.r_ur;

    /*
     * To find the rightmost and leftmost solid edges, we
     * scan along the respective edges.  Our assumption is
     * that the only tiles along the edges are space tiles,
     * which, by the maximum horizontal strip property, must
     * have either solid tiles or the edge of the plane on
     * their other sides.
     */

    for (tp = TR(left); tp != bottom; tp = LB(tp))
	if (RIGHT(tp) < rect->r_xbot)
	    rect->r_xbot = RIGHT(tp);

    for (tp = BL(right); tp != top; tp = RT(tp))
	if (LEFT(tp) > rect->r_xtop)
	    rect->r_xtop = LEFT(tp);

    /*
     * We assume that only space tiles extend all the way
     * from the left edge of the plane to the right.  We
     * also assume that the topmost and bottommost tiles
     * are space tiles.
     */

    rect->r_ytop = BOTTOM(LB(top));
    rect->r_ybot = TOP(RT(bottom));

    /*
     * If the bounding rectangle is degenerate (indicating no solid
     * tiles in the plane), we make it the 1x1 rectangle: (0,0)::(1,1).
     */
    if (rect->r_xtop < rect->r_xbot || rect->r_ytop < rect->r_ybot)
    {
	rect->r_xbot = rect->r_xtop = 0;
	rect->r_ybot = rect->r_ytop = 0;
	return (FALSE);
    }

    return (TRUE);
}

/*
 * --------------------------------------------------------------------
 *
 * DBBoundPlaneVert --
 *
 * Determine the bounding rectangle for the supplied tile plane,
 * which is organized into maximal vertical strips instead of
 * maximal horizontal ones.
 *
 * The bounding rectangle is the smallest rectangle that completely
 * encloses all non-space tiles.
 *
 * If the tile plane is completely empty, we return a 0x0 bounding
 * box at the origin.
 *
 * Results:
 *	TRUE if the tile plane contains any geometry, FALSE
 *	if it is completely empty.
 *
 * Side effects:
 *	Sets *rect to the bounding rectangle.
 *
 * --------------------------------------------------------------------
 */

bool
DBBoundPlaneVert(
    Plane *plane,
    Rect *rect)
{
    Tile *left, *right, *top, *bottom, *tp;

    left = plane->pl_left;
    right = plane->pl_right;
    top = plane->pl_top;
    bottom = plane->pl_bottom;

    rect->r_ur = TiPlaneRect.r_ll;
    rect->r_ll = TiPlaneRect.r_ur;

    /*
     * To find the topmost and bottommost solid edges, we
     * scan along the respective edges.  Our assumption is
     * that the only tiles along the edges are space tiles,
     * which, by the maximum vertical strip property, must
     * have either solid tiles or the edge of the plane on
     * their other sides.
     */
    for (tp = RT(bottom); tp != left; tp = BL(tp))
	if (TOP(tp) < rect->r_ybot)
	    rect->r_ybot = TOP(tp);
    for (tp = LB(top); tp != right; tp = TR(tp))
	if (BOTTOM(tp) > rect->r_ytop)
	    rect->r_ytop = BOTTOM(tp);

    /*
     * We assume that only space tiles extend all the way
     * from the top edge of the plane to the bottom.  We
     * also assume that the leftmost and rightmost tiles
     * are space tiles.
     */
    rect->r_xtop = LEFT(BL(right));
    rect->r_xbot = RIGHT(TR(left));

    /*
     * If the bounding rectangle is degenerate (indicating no solid
     * tiles in the plane), we make it the 1x1 rectangle: (0,0)::(1,1).
     */
    if (rect->r_xtop < rect->r_xbot || rect->r_ytop < rect->r_ybot)
    {
	rect->r_xbot = rect->r_xtop = 0;
	rect->r_ybot = rect->r_ytop = 0;
	return (FALSE);
    }

    return (TRUE);
}
