magic/tiles/tile.c

830 lines
20 KiB
C

/*
* tile.c --
*
* Basic tile manipulation
*
* *********************************************************************
* * 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/tiles/tile.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "utils/magic.h"
#include "utils/malloc.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
/*
* Debugging version of TiSetBody() macro in tile.h
* Includes sanity check that a tile at "infinity"
* is not being set to a type other than space.
*/
/*
void
TiSetBody(tp, b)
Tile *tp;
ClientData b;
{
if (b != (ClientData)0 && b != (ClientData)(-1))
if (RIGHT(tp) == INFINITY || TOP(tp) == INFINITY ||
LEFT(tp) == MINFINITY || BOTTOM(tp) == MINFINITY)
TxError("Error: Tile at infinity set to non-space value %d\n", (int)b);
tp->ti_body = b;
}
*/
/*
* Rectangle that defines the maximum extent of any plane.
* No tile created by the user should ever extend outside of
* this area.
*/
global Rect TiPlaneRect = { MINFINITY+2, MINFINITY+2, INFINITY-2, INFINITY-2 };
#ifdef HAVE_SYS_MMAN_H
global Tile *TileStoreFreeList = NULL;
global Tile *TileStoreFreeList_end = NULL;
/* The new Tile Allocation scheme (Magic 8.0) */
static void *_block_begin = NULL;
static void *_current_ptr = NULL;
static void *_block_end = NULL;
#endif /* HAVE_SYS_MMAN_H */
/*
* --------------------------------------------------------------------
*
* TiNewPlane --
*
* Allocate and initialize a new tile plane.
*
* Results:
* A newly allocated Plane with all corner stitches set
* appropriately.
*
* Side effects:
* Adjusts the corner stitches of the Tile supplied to
* point to the appropriate bounding tile in the newly
* created Plane.
*
* --------------------------------------------------------------------
*/
Plane *
TiNewPlane(tile)
Tile *tile;/* Tile to become initial tile of plane.
* May be NULL.
*/
{
Plane *newplane;
static Tile *infinityTile = (Tile *) NULL;
newplane = (Plane *) mallocMagic((unsigned) (sizeof (Plane)));
newplane->pl_policy = MAX_HORIZ_STRIPS;
newplane->pl_top = TiAlloc();
newplane->pl_right = TiAlloc();
newplane->pl_bottom = TiAlloc();
newplane->pl_left = TiAlloc();
/*
* Since the lower left coordinates of the TR and RT
* stitches of a tile are used to determine its upper right,
* we must give the boundary tiles a meaningful TR and RT.
* To make certain that these tiles don't have zero width
* or height, we use a dummy tile at (INFINITY+1,INFINITY+1).
*/
if (infinityTile == (Tile *) NULL)
{
infinityTile = TiAlloc();
LEFT(infinityTile) = INFINITY+1;
BOTTOM(infinityTile) = INFINITY+1;
}
if (tile)
{
RT(tile) = newplane->pl_top;
TR(tile) = newplane->pl_right;
LB(tile) = newplane->pl_bottom;
BL(tile) = newplane->pl_left;
}
LEFT(newplane->pl_bottom) = MINFINITY;
BOTTOM(newplane->pl_bottom) = MINFINITY;
RT(newplane->pl_bottom) = tile;
TR(newplane->pl_bottom) = newplane->pl_right;
LB(newplane->pl_bottom) = BADTILE;
BL(newplane->pl_bottom) = newplane->pl_left;
TiSetBody(newplane->pl_bottom, -1);
LEFT(newplane->pl_top) = MINFINITY;
BOTTOM(newplane->pl_top) = INFINITY;
RT(newplane->pl_top) = infinityTile;
TR(newplane->pl_top) = newplane->pl_right;
LB(newplane->pl_top) = tile;
BL(newplane->pl_top) = newplane->pl_left;
TiSetBody(newplane->pl_top, -1);
LEFT(newplane->pl_left) = MINFINITY;
BOTTOM(newplane->pl_left) = MINFINITY;
RT(newplane->pl_left) = newplane->pl_top;
TR(newplane->pl_left) = tile;
LB(newplane->pl_left) = newplane->pl_bottom;
BL(newplane->pl_left) = BADTILE;
TiSetBody(newplane->pl_left, -1);
LEFT(newplane->pl_right) = INFINITY;
BOTTOM(newplane->pl_right) = MINFINITY;
RT(newplane->pl_right) = newplane->pl_top;
TR(newplane->pl_right) = infinityTile;
LB(newplane->pl_right) = newplane->pl_bottom;
BL(newplane->pl_right) = tile;
TiSetBody(newplane->pl_right, -1);
newplane->pl_hint = tile;
return (newplane);
}
/*
* --------------------------------------------------------------------
*
* TiFreePlane --
*
* Free the storage associated with a tile plane.
* Only the plane itself and its four border tiles are deallocated.
*
* Results:
* None.
*
* Side effects:
* Frees memory.
*
* --------------------------------------------------------------------
*/
void
TiFreePlane(plane)
Plane *plane; /* Plane to be freed */
{
TiFree(plane->pl_left);
TiFree(plane->pl_right);
TiFree(plane->pl_top);
TiFree(plane->pl_bottom);
freeMagic((char *) plane);
}
/*
* --------------------------------------------------------------------
*
* TiToRect --
*
* Convert a tile to a rectangle.
*
* Results:
* None.
*
* Side effects:
* Sets *rect to the bounding box for the supplied tile.
*
* --------------------------------------------------------------------
*/
void
TiToRect(tile, rect)
Tile *tile; /* Tile whose bounding box is to be stored in *rect */
Rect *rect; /* Pointer to rect to be set to bounding box */
{
rect->r_xbot = LEFT(tile);
rect->r_xtop = RIGHT(tile);
rect->r_ybot = BOTTOM(tile);
rect->r_ytop = TOP(tile);
}
/*
* --------------------------------------------------------------------
*
* TiSplitX --
*
* Given a tile and an X coordinate, split the tile into two
* along a line running vertically through the given coordinate.
*
* Results:
* Returns the new tile resulting from the splitting, which
* is the tile occupying the right-hand half of the original
* tile.
*
* Side effects:
* Modifies the corner stitches in the database to reflect
* the presence of two tiles in place of the original one.
*
* --------------------------------------------------------------------
*/
Tile *
TiSplitX(tile, x)
Tile *tile; /* Tile to be split */
int x; /* X coordinate of split */
{
Tile *newtile;
Tile *tp;
ASSERT(x > LEFT(tile) && x < RIGHT(tile), "TiSplitX");
newtile = TiAlloc();
TiSetClient(newtile, CLIENTDEFAULT);
TiSetBody(newtile, 0);
LEFT(newtile) = x;
BOTTOM(newtile) = BOTTOM(tile);
BL(newtile) = tile;
TR(newtile) = TR(tile);
RT(newtile) = RT(tile);
/*
* Adjust corner stitches along the right edge
*/
for (tp = TR(tile); BL(tp) == tile; tp = LB(tp))
BL(tp) = newtile;
TR(tile) = newtile;
/*
* Adjust corner stitches along the top edge
*/
for (tp = RT(tile); LEFT(tp) >= x; tp = BL(tp))
LB(tp) = newtile;
RT(tile) = tp;
/*
* Adjust corner stitches along the bottom edge
*/
for (tp = LB(tile); RIGHT(tp) <= x; tp = TR(tp))
/* nothing */;
LB(newtile) = tp;
while (RT(tp) == tile)
{
RT(tp) = newtile;
tp = TR(tp);
}
return (newtile);
}
/*
* --------------------------------------------------------------------
*
* TiSplitY --
*
* Given a tile and a Y coordinate, split the tile into two
* along a horizontal line running through the given coordinate.
*
* Results:
* Returns the new tile resulting from the splitting, which
* is the tile occupying the top half of the original
* tile.
*
* Side effects:
* Modifies the corner stitches in the database to reflect
* the presence of two tiles in place of the original one.
*
* --------------------------------------------------------------------
*/
Tile *
TiSplitY(tile, y)
Tile *tile; /* Tile to be split */
int y; /* Y coordinate of split */
{
Tile *newtile;
Tile *tp;
ASSERT(y > BOTTOM(tile) && y < TOP(tile), "TiSplitY");
newtile = TiAlloc();
TiSetClient(newtile, CLIENTDEFAULT);
TiSetBody(newtile, 0);
LEFT(newtile) = LEFT(tile);
BOTTOM(newtile) = y;
LB(newtile) = tile;
RT(newtile) = RT(tile);
TR(newtile) = TR(tile);
/*
* Adjust corner stitches along top edge
*/
for (tp = RT(tile); LB(tp) == tile; tp = BL(tp))
LB(tp) = newtile;
RT(tile) = newtile;
/*
* Adjust corner stitches along right edge
*/
for (tp = TR(tile); BOTTOM(tp) >= y; tp = LB(tp))
BL(tp) = newtile;
TR(tile) = tp;
/*
* Adjust corner stitches along left edge
*/
for (tp = BL(tile); TOP(tp) <= y; tp = RT(tp))
/* nothing */;
BL(newtile) = tp;
while (TR(tp) == tile)
{
TR(tp) = newtile;
tp = RT(tp);
}
return (newtile);
}
/*
* --------------------------------------------------------------------
*
* TiSplitX_Left --
*
* Given a tile and an X coordinate, split the tile into two
* along a line running vertically through the given coordinate.
* Intended for use when plowing to the left.
*
* Results:
* Returns the new tile resulting from the splitting, which
* is the tile occupying the left-hand half of the original
* tile.
*
* Side effects:
* Modifies the corner stitches in the database to reflect
* the presence of two tiles in place of the original one.
*
* --------------------------------------------------------------------
*/
Tile *
TiSplitX_Left(tile, x)
Tile *tile; /* Tile to be split */
int x; /* X coordinate of split */
{
Tile *newtile;
Tile *tp;
ASSERT(x > LEFT(tile) && x < RIGHT(tile), "TiSplitX");
newtile = TiAlloc();
TiSetClient(newtile, CLIENTDEFAULT);
TiSetBody(newtile, 0);
LEFT(newtile) = LEFT(tile);
LEFT(tile) = x;
BOTTOM(newtile) = BOTTOM(tile);
BL(newtile) = BL(tile);
LB(newtile) = LB(tile);
TR(newtile) = tile;
BL(tile) = newtile;
/* Adjust corner stitches along the left edge */
for (tp = BL(newtile); TR(tp) == tile; tp = RT(tp))
TR(tp) = newtile;
/* Adjust corner stitches along the top edge */
for (tp = RT(tile); LEFT(tp) >= x; tp = BL(tp))
/* nothing */;
RT(newtile) = tp;
for ( ; LB(tp) == tile; tp = BL(tp))
LB(tp) = newtile;
/* Adjust corner stitches along the bottom edge */
for (tp = LB(tile); RIGHT(tp) <= x; tp = TR(tp))
RT(tp) = newtile;
LB(tile) = tp;
return (newtile);
}
/*
* --------------------------------------------------------------------
*
* TiSplitY_Bottom --
*
* Given a tile and a Y coordinate, split the tile into two
* along a horizontal line running through the given coordinate.
* Used when plowing down.
*
* Results:
* Returns the new tile resulting from the splitting, which
* is the tile occupying the bottom half of the original
* tile.
*
* Side effects:
* Modifies the corner stitches in the database to reflect
* the presence of two tiles in place of the original one.
*
* --------------------------------------------------------------------
*/
Tile *
TiSplitY_Bottom(tile, y)
Tile *tile; /* Tile to be split */
int y; /* Y coordinate of split */
{
Tile *newtile;
Tile *tp;
ASSERT(y > BOTTOM(tile) && y < TOP(tile), "TiSplitY");
newtile = TiAlloc();
TiSetClient(newtile, CLIENTDEFAULT);
TiSetBody(newtile, 0);
LEFT(newtile) = LEFT(tile);
BOTTOM(newtile) = BOTTOM(tile);
BOTTOM(tile) = y;
RT(newtile) = tile;
LB(newtile) = LB(tile);
BL(newtile) = BL(tile);
LB(tile) = newtile;
/* Adjust corner stitches along bottom edge */
for (tp = LB(newtile); RT(tp) == tile; tp = TR(tp))
RT(tp) = newtile;
/* Adjust corner stitches along right edge */
for (tp = TR(tile); BOTTOM(tp) >= y; tp = LB(tp))
/* nothing */;
TR(newtile) = tp;
for ( ; BL(tp) == tile; tp = LB(tp))
BL(tp) = newtile;
/* Adjust corner stitches along left edge */
for (tp = BL(tile); TOP(tp) <= y; tp = RT(tp))
TR(tp) = newtile;
BL(tile) = tp;
return (newtile);
}
/*
* --------------------------------------------------------------------
*
* TiJoinX --
*
* Given two tiles sharing an entire common vertical edge, replace
* them with a single tile occupying the union of their areas.
*
* Results:
* None.
*
* Side effects:
* The first tile is simply relinked to reflect its new size.
* The second tile is deallocated. Corner stitches in the
* neighboring tiles are updated to reflect the new structure.
* If the hint tile pointer in the supplied plane pointed to
* the second tile, it is adjusted to point instead to the
* first.
*
* --------------------------------------------------------------------
*/
void
TiJoinX(tile1, tile2, plane)
Tile *tile1; /* First tile, remains allocated after call */
Tile *tile2; /* Second tile, deallocated by call */
Plane *plane; /* Plane in which hint tile is updated */
{
Tile *tp;
/*
* Basic algorithm:
*
* Update all the corner stitches in the neighbors of tile2
* to point to tile1.
* Update the corner stitches of tile1 along the shared edge
* to be those of tile2.
* Change the bottom or left coordinate of tile1 if appropriate.
* Deallocate tile2.
*/
ASSERT(BOTTOM(tile1)==BOTTOM(tile2) && TOP(tile1)==TOP(tile2), "TiJoinX");
ASSERT(LEFT(tile1)==RIGHT(tile2) || RIGHT(tile1)==LEFT(tile2), "TiJoinX");
/*
* Update stitches along top of tile
*/
for (tp = RT(tile2); LB(tp) == tile2; tp = BL(tp))
LB(tp) = tile1;
/*
* Update stitches along bottom of tile
*/
for (tp = LB(tile2); RT(tp) == tile2; tp = TR(tp))
RT(tp) = tile1;
/*
* Update stitches along either left or right, depending
* on relative position of the two tiles.
*/
ASSERT(LEFT(tile1) != LEFT(tile2), "TiJoinX");
if (LEFT(tile1) < LEFT(tile2))
{
for (tp = TR(tile2); BL(tp) == tile2; tp = LB(tp))
BL(tp) = tile1;
TR(tile1) = TR(tile2);
RT(tile1) = RT(tile2);
}
else
{
for (tp = BL(tile2); TR(tp) == tile2; tp = RT(tp))
TR(tp) = tile1;
BL(tile1) = BL(tile2);
LB(tile1) = LB(tile2);
LEFT(tile1) = LEFT(tile2);
}
if (plane->pl_hint == tile2)
plane->pl_hint = tile1;
TiFree(tile2);
}
/*
* --------------------------------------------------------------------
*
* TiJoinY --
*
* Given two tiles sharing an entire common horizontal edge, replace
* them with a single tile occupying the union of their areas.
*
* Results:
* None.
*
* Side effects:
* The first tile is simply relinked to reflect its new size.
* The second tile is deallocated. Corner stitches in the
* neighboring tiles are updated to reflect the new structure.
* If the hint tile pointer in the supplied plane pointed to
* the second tile, it is adjusted to point instead to the
* first.
*
* --------------------------------------------------------------------
*/
void
TiJoinY(tile1, tile2, plane)
Tile *tile1; /* First tile, remains allocated after call */
Tile *tile2; /* Second tile, deallocated by call */
Plane *plane; /* Plane in which hint tile is updated */
{
Tile *tp;
/*
* Basic algorithm:
*
* Update all the corner stitches in the neighbors of tile2
* to point to tile1.
* Update the corner stitches of tile1 along the shared edge
* to be those of tile2.
* Change the bottom or left coordinate of tile1 if appropriate.
* Deallocate tile2.
*/
ASSERT(LEFT(tile1)==LEFT(tile2) && RIGHT(tile1)==RIGHT(tile2), "TiJoinY");
ASSERT(TOP(tile1)==BOTTOM(tile2) || BOTTOM(tile1)==TOP(tile2), "TiJoinY");
/*
* Update stitches along right of tile.
*/
for (tp = TR(tile2); BL(tp) == tile2; tp = LB(tp))
BL(tp) = tile1;
/*
* Update stitches along left of tile.
*/
for (tp = BL(tile2); TR(tp) == tile2; tp = RT(tp))
TR(tp) = tile1;
/*
* Update stitches along either top or bottom, depending
* on relative position of the two tiles.
*/
ASSERT(BOTTOM(tile1) != BOTTOM(tile2), "TiJoinY");
if (BOTTOM(tile1) < BOTTOM(tile2))
{
for (tp = RT(tile2); LB(tp) == tile2; tp = BL(tp))
LB(tp) = tile1;
RT(tile1) = RT(tile2);
TR(tile1) = TR(tile2);
}
else
{
for (tp = LB(tile2); RT(tp) == tile2; tp = TR(tp))
RT(tp) = tile1;
LB(tile1) = LB(tile2);
BL(tile1) = BL(tile2);
BOTTOM(tile1) = BOTTOM(tile2);
}
if (plane->pl_hint == tile2)
plane->pl_hint = tile1;
TiFree(tile2);
}
#ifdef HAVE_SYS_MMAN_H
/* MMAP the tile store */
static signed char
mmapTileStore()
{
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_ANON | MAP_PRIVATE;
unsigned long map_len = TILE_STORE_BLOCK_SIZE;
_block_begin = mmap(NULL, map_len, prot, flags, -1, 0);
if (_block_begin == MAP_FAILED)
{
TxError("TileStore: Unable to mmap ANON SEGMENT\n");
_exit(1);
}
_block_end = (void *) ((unsigned long) _block_begin + map_len);
_current_ptr = _block_begin;
return 0;
}
Tile *
getTileFromTileStore()
{
Tile *_return_tile = NULL;
if (!_block_begin && !_block_end)
{
mmapTileStore();
}
/* Check if we can get the tile from the
* Free list
*/
if (TileStoreFreeList)
{
_return_tile = TileStoreFreeList;
TileStoreFreeList = (Tile *)TileStoreFreeList->ti_client;
return _return_tile;
}
/* Get it from the mmap */
if (((unsigned long)_current_ptr + sizeof(Tile))
> (unsigned long)_block_end)
{
mmapTileStore();
}
_current_ptr = (void *)((unsigned long)_current_ptr + sizeof(Tile));
if ((unsigned long)_current_ptr > (unsigned long) _block_end)
{
fprintf(stderr,"TileStore: internal assertion failure...");
_exit(1);
}
return (Tile *)((unsigned long)_current_ptr - sizeof(Tile));
}
static void
TileStoreFree(ptr)
Tile *ptr;
{
if (!TileStoreFreeList_end || !TileStoreFreeList)
{
TileStoreFreeList_end = ptr;
ptr->ti_client = (unsigned long)0;
TileStoreFreeList = TileStoreFreeList_end;
}
else
{
TileStoreFreeList_end->ti_client = (unsigned long)ptr;
TileStoreFreeList_end = ptr;
TileStoreFreeList_end->ti_client = (unsigned long) 0;
}
}
Tile *
TiAlloc()
{
Tile *newtile;
newtile = getTileFromTileStore();
TiSetClient(newtile, CLIENTDEFAULT);
TiSetBody(newtile, 0);
return (newtile);
}
void
TiFree(tp)
Tile *tp;
{
TileStoreFree(tp);
}
#else
/*
* --------------------------------------------------------------------
*
* TiAlloc ---
*
* Memory allocation for tiles
*
* Results:
* Pointer to an initialized memory location for a tile.
*
* --------------------------------------------------------------------
*/
Tile *
TiAlloc()
{
Tile *newtile;
newtile = (Tile *) mallocMagic((unsigned) (sizeof (Tile)));
TiSetClient(newtile, CLIENTDEFAULT);
TiSetBody(newtile, 0);
return (newtile);
}
/*
* --------------------------------------------------------------------
*
* TiFree ---
*
* Release memory allocation for tiles
*
* Results:
* None.
*
* --------------------------------------------------------------------
*/
void
TiFree(tp)
Tile *tp;
{
freeMagic((char *)tp);
}
#endif /* !HAVE_SYS_MMAN_H */
/* ==================================================================== */
/* */
/* DEBUGGING PROCEDURES */
/* */
/* ==================================================================== */
void
tiPrint(tp)
Tile *tp;
{
printf("tp=%p LL=(%d,%d) body=0x%"DLONG_PREFIX"x\n",
tp, LEFT(tp), BOTTOM(tp), (dlong) tp->ti_body);
printf("BL=%p LB=%p RT=%p TR=%p\n", BL(tp), LB(tp), RT(tp), TR(tp));
}
void
tiPrintAll(tp)
Tile *tp;
{
tiPrint(tp);
printf("UR=(%d,%d)\n", RIGHT(tp), TOP(tp));
/* The following is for plowing debugging */
printf("LEAD=%d\n", (int) tp->ti_client);
}