pointtree: Kill mapgrid.c. Reincarnate as mapgrid.cpp, using the new PointTree.

git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@9261 4a71c877-e1ca-e34f-864e-861f7616d084
master
Cyp 2010-01-16 22:49:14 +00:00 committed by Git SVN Gateway
parent 5c9585e8cb
commit d6d0070530
8 changed files with 268 additions and 696 deletions

View File

@ -230,7 +230,7 @@ warzone2100_SOURCES = \
main.c \
map.c \
mapdisplay.c \
mapgrid.c \
mapgrid.cpp \
mechanics.c \
message.c \
message_lexer.lex.c \

View File

@ -1,599 +0,0 @@
/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2009 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* MapGrid.c
*
* Functions for storing objects in a grid over the map.
* The objects are stored in every grid over which they might
* have some influence.
*
*/
#include "lib/framework/frame.h"
#include "objects.h"
#include "map.h"
#include "mapgrid.h"
// The number of world units per grid
#define GRID_UNITS (GRID_SIZE * TILE_UNITS)
static UDWORD gridWidth, gridHeight;
// The map grid
static GRID_ARRAY *apsMapGrid[GRID_MAXAREA];
#define GridIndex(a,b) (((b)*gridWidth) + (a))
// which grid to garbage collect on next
static SDWORD garbageX, garbageY;
// the current state of the iterator
static GRID_ARRAY *psIterateGrid;
static SDWORD iterateEntry;
// what to do when calculating the coverage of an object
typedef enum _coverage_mode
{
GRID_ADDOBJECT,
GRID_REMOVEOBJECT,
} COVERAGE_MODE;
// Function prototypes
static BOOL gridIntersect(const int x1, const int y1, const int x2, const int y2,
const int cx, const int cy, const int Rad);
static void gridAddArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj);
static void gridRemoveArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj);
static void gridCompactArray(SDWORD x, SDWORD y);
static int gridObjRange(const BASE_OBJECT* psObj);
// initialise the grid system
BOOL gridInitialise(void)
{
memset(apsMapGrid, 0, sizeof(GRID_ARRAY *) * GRID_MAXAREA);
garbageX = 0;
garbageY = 0;
psIterateGrid = NULL;
iterateEntry = 0;
return true;
}
//clear the grid of everything on it
void gridClear(void)
{
GRID_ARRAY *psCurr, *psNext;
unsigned int x, y;
debug( LOG_NEVER, "gridClear %d %d\n", gridWidth, gridHeight );
for(x = 0; x < gridWidth; ++x)
{
for(y = 0; y < gridHeight; ++y)
{
for(psCurr = apsMapGrid[GridIndex(x,y)]; psCurr != NULL; psCurr = psNext)
{
psNext = psCurr->psNext;
free(psCurr);
}
apsMapGrid[GridIndex(x,y)] = NULL;
}
}
}
// reset the grid system
void gridReset(void)
{
STRUCTURE *psStruct;
DROID *psDroid;
FEATURE *psFeature;
UBYTE inc;
// Setup the grid dimensions.
gridWidth = (mapWidth+GRID_SIZE-1) / GRID_SIZE;
gridHeight = (mapHeight+GRID_SIZE-1) / GRID_SIZE;
gridClear();
//put all the existing objects into the grid
for (inc = 0; inc < MAX_PLAYERS; inc++)
{
for (psDroid = apsDroidLists[inc]; psDroid != NULL; psDroid =
psDroid->psNext)
{
gridAddObject((BASE_OBJECT *)psDroid);
}
for (psStruct = apsStructLists[inc]; psStruct != NULL; psStruct =
psStruct->psNext)
{
gridAddObject((BASE_OBJECT *)psStruct);
}
for (psFeature = apsFeatureLists[inc]; psFeature != NULL; psFeature =
psFeature->psNext)
{
gridAddObject((BASE_OBJECT *)psFeature);
}
}
}
// shutdown the grid system
void gridShutDown(void)
{
gridClear();
}
// find the grid's that are covered by the object and either
// add or remove the object
static void gridCalcCoverage(BASE_OBJECT *psObj, SDWORD objx, SDWORD objy, COVERAGE_MODE mode)
{
SDWORD range, x,y, minx,maxx, miny,maxy;
range = gridObjRange(psObj);
// calculate the range of grids that could be covered by the object
objx = (objx & ~TILE_MASK) + TILE_UNITS/2;
objy = (objy & ~TILE_MASK) + TILE_UNITS/2;
minx = objx - range;
maxx = objx + range;
miny = objy - range;
maxy = objy + range;
minx = map_coord(minx) / GRID_SIZE;
maxx = map_coord(maxx) / GRID_SIZE;
miny = map_coord(miny) / GRID_SIZE;
maxy = map_coord(maxy) / GRID_SIZE;
// see which ones are covered by the object
for (x=minx; x<=maxx; x++)
{
if ( (x >= 0) && (x < gridWidth) )
{
for(y=miny; y<=maxy; y++)
{
if ( (y >= 0) && (y < gridHeight) &&
gridIntersect( x * GRID_UNITS, y * GRID_UNITS,
(x+1) * GRID_UNITS, (y+1) * GRID_UNITS,
objx, objy, range ) )
{
switch (mode)
{
case GRID_ADDOBJECT:
gridAddArrayObject(x,y, psObj);
break;
case GRID_REMOVEOBJECT:
gridRemoveArrayObject(x,y, psObj);
break;
}
}
}
}
}
}
// add an object to the grid system
void gridAddObject(BASE_OBJECT *psObj)
{
ASSERT_OR_RETURN(, psObj != NULL, "Attempted to add a NULL pointer");
ASSERT_OR_RETURN(, !isDead(psObj), "Attempted to add dead object %s(%d) to the map grid!", objInfo(psObj), (int)psObj->id);
gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_ADDOBJECT);
}
// move an object within the grid
// oldX,oldY are the old position of the object in world coords
void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY)
{
if (map_coord(psDroid->pos.x) == map_coord(oldX)
&& map_coord(psDroid->pos.y) == map_coord(oldY))
{
// havn't changed the tile the object is on, don't bother updating
return;
}
gridCalcCoverage((BASE_OBJECT*)psDroid, oldX,oldY, GRID_REMOVEOBJECT);
gridCalcCoverage((BASE_OBJECT*)psDroid, psDroid->pos.x, psDroid->pos.y, GRID_ADDOBJECT);
}
// remove an object from the grid system
void gridRemoveObject(BASE_OBJECT *psObj)
{
gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_REMOVEOBJECT);
#if defined(DEBUG)
{
GRID_ARRAY *psCurr;
unsigned int i,x,y;
for (x = 0; x < gridWidth; x++)
{
for(y = 0; y < gridHeight; y++)
{
for (psCurr = apsMapGrid[GridIndex(x,y)]; psCurr; psCurr = psCurr->psNext)
{
for (i = 0; i < MAX_GRID_ARRAY_CHUNK; i++)
{
if (psCurr->apsObjects[i] == psObj)
{
ASSERT(false, "Grid out of sync at (%u,%u):%u removing %s(%d)", x, y, i, objInfo(psObj), (int)psObj->id);
psCurr->apsObjects[i] = NULL;
}
}
}
}
}
}
#endif
}
// initialise the grid system to start iterating through units that
// could affect a location (x,y in world coords)
void gridStartIterate(SDWORD x, SDWORD y)
{
const int nx = x / GRID_UNITS;
const int ny = y / GRID_UNITS;
ASSERT_OR_RETURN(, nx >= 0 && nx < gridWidth && ny >= 0 && ny < gridHeight, "Coordinates(%d, %d) off grid(%u, %u)", nx, ny, gridWidth, gridHeight);
psIterateGrid = apsMapGrid[GridIndex(nx, ny)];
iterateEntry = 0;
}
// get the next object that could affect a location,
// should only be called after gridStartIterate
BASE_OBJECT *gridIterate(void)
{
BASE_OBJECT *psRet;
while (psIterateGrid != NULL)
{
if (psIterateGrid->apsObjects[iterateEntry] != NULL)
{
break;
}
iterateEntry += 1;
if (iterateEntry >= MAX_GRID_ARRAY_CHUNK)
{
psIterateGrid = psIterateGrid->psNext;
iterateEntry = 0;
}
}
if (psIterateGrid)
{
psRet = psIterateGrid->apsObjects[iterateEntry];
iterateEntry += 1;
if (iterateEntry >= MAX_GRID_ARRAY_CHUNK)
{
psIterateGrid = psIterateGrid->psNext;
iterateEntry = 0;
}
}
else
{
psRet = NULL;
}
return psRet;
}
// compact some of the grid arrays
void gridGarbageCollect(void)
{
gridCompactArray(garbageX,garbageY);
++garbageX;
if (garbageX >= gridWidth)
{
garbageX = 0;
++garbageY;
if (garbageY >= gridHeight)
{
garbageX = 0;
garbageY = 0;
}
}
//#ifdef DEBUG
#if 0
// integrity check the array
{
GRID_ARRAY *psCurr, *psCheck;
SDWORD curr, check;
BASE_OBJECT *psObj;
check = 0;
psCheck = apsMapGrid[GridIndex(garbageX,garbageY)];
while (psCheck != NULL)
{
psObj = psCheck->apsObjects[check];
if (psObj != NULL)
{
// see if there is a duplicate element in the array
curr = 0;
psCurr = apsMapGrid[GridIndex(garbageX,garbageY)];
while ( psCurr != NULL )
{
if ( !((psCurr == psCheck) && (curr == check)) &&
(psCurr->apsObjects[curr] == psObj) )
{
ASSERT( false, "mapGrid integrity check failed" );
psCurr->apsObjects[curr] = NULL;
}
curr += 1;
if (curr >= MAX_GRID_ARRAY_CHUNK)
{
psCurr=psCurr->psNext;
curr = 0;
}
}
}
check += 1;
if (check >= MAX_GRID_ARRAY_CHUNK)
{
psCheck = psCheck->psNext;
check = 0;
}
}
}
#endif
}
// add an object to a grid array
static void gridAddArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj)
{
GRID_ARRAY *psPrev, *psCurr, *psNew;
SDWORD i;
// see if there is an empty slot in the currently allocated array
psPrev = NULL;
for (psCurr = apsMapGrid[GridIndex(x,y)]; psCurr; psCurr=psCurr->psNext)
{
for(i=0; i<MAX_GRID_ARRAY_CHUNK; i++)
{
if (psCurr->apsObjects[i] == NULL)
{
// found an empty slot
psCurr->apsObjects[i] = psObj;
return;
}
}
psPrev = psCurr;
}
// allocate a new array chunk
psNew = malloc(sizeof(GRID_ARRAY));
if (psNew == NULL)
{
debug(LOG_ERROR, "gridAddArrayObject: Out of memory");
return;
}
// store the object
memset(psNew, 0, sizeof(GRID_ARRAY));
psNew->apsObjects[0] = psObj;
// add the chunk to the end of the list
if (psPrev == NULL)
{
apsMapGrid[GridIndex(x,y)] = psNew;
}
else
{
psPrev->psNext = psNew;
}
}
// remove an object from a grid array
static void gridRemoveArrayObject(SDWORD x, SDWORD y, BASE_OBJECT *psObj)
{
GRID_ARRAY *psCurr;
SDWORD i;
for (psCurr = apsMapGrid[GridIndex(x,y)]; psCurr; psCurr = psCurr->psNext)
{
for (i=0; i<MAX_GRID_ARRAY_CHUNK; i++)
{
if (psCurr->apsObjects[i] == psObj)
{
psCurr->apsObjects[i] = NULL;
return;
}
}
}
}
// Compact a grid array to remove any NULL objects
static void gridCompactArray(SDWORD x, SDWORD y)
{
GRID_ARRAY *psDone, *psMove, *psPrev, *psNext;
SDWORD done, move;
psDone = psMove = apsMapGrid[GridIndex(x,y)];
done = move = 0;
// move every entry down in the array
psPrev = NULL;
while (psMove != NULL)
{
if (psMove->apsObjects[move] != NULL)
{
psDone->apsObjects[done] = psMove->apsObjects[move];
done += 1;
if (done >= MAX_GRID_ARRAY_CHUNK)
{
psPrev = psDone;
psDone = psDone->psNext;
done = 0;
}
}
move += 1;
if (move >= MAX_GRID_ARRAY_CHUNK)
{
psMove = psMove->psNext;
move = 0;
}
}
// if the compacted array finishes half way through, NULL the rest of it
if ( (psDone != NULL) && (done != 0) )
{
memset(&psDone->apsObjects[done], 0, sizeof(BASE_OBJECT *) * (MAX_GRID_ARRAY_CHUNK - done) );
psPrev = psDone;
psDone = psDone->psNext;
}
// now release any unused chunks
if (psPrev == NULL)
{
apsMapGrid[GridIndex(x,y)] = NULL;
}
else
{
psPrev->psNext = NULL;
}
while (psDone)
{
psNext = psDone->psNext;
free(psDone);
psDone = psNext;
}
}
// Display all the grid's an object is a member of
void gridDisplayCoverage(BASE_OBJECT *psObj)
{
#ifdef DEBUG
{
unsigned int x, y, i;
GRID_ARRAY *psCurr;
debug(LOG_ERROR, "Grid coverage for object %d (%d,%d) - range %d", psObj->id, psObj->pos.x, psObj->pos.y, gridObjRange(psObj) );
for (x = 0; x < gridWidth; x++)
{
for(y = 0; y < gridHeight; y++)
{
psCurr = apsMapGrid[GridIndex(x, y)];
i = 0;
while (psCurr != NULL)
{
if (psCurr->apsObjects[i] == psObj)
{
debug(LOG_ERROR, " %d,%d [ %d,%d -> %d,%d ]", x, y, x*GRID_UNITS, y*GRID_UNITS, (x+1)*GRID_UNITS, (y+1)*GRID_UNITS );
}
++i;
if (i >= MAX_GRID_ARRAY_CHUNK)
{
psCurr = psCurr->psNext;
i = 0;
}
}
}
}
}
#endif
}
// Fast circle rectangle intersection, taken from Graphics Gems I, p51
// by Clifford A Shaffer
/* Return true iff rectangle R intersects circle with centerpoint C and
radius Rad. */
static BOOL gridIntersect(const int x1, const int y1, const int x2, const int y2,
const int cx, const int cy, const int Rad)
{
// Translate coordinates, placing C at the origin
const struct
{
int x1, y1,
x2, y2;
} rect = { x1 - cx, y1 - cy,
x2 - cx, y2 - cy };
const int Rad2 = Rad * Rad;
if (rect.x2 < 0) /* R to left of circle center */
{
if (rect.y2 < 0) /* R in lower left corner */
{
return ((rect.x2 * rect.x2 + rect.y2 * rect.y2) < Rad2);
}
else if (rect.y1 > 0) /* R in upper left corner */
{
return ((rect.x2 * rect.x2 + rect.y1 * rect.y1) < Rad2);
}
else /* R due West of circle */
{
return(abs(rect.x2) < Rad);
}
}
else if (rect.x1 > 0) /* R to right of circle center */
{
if (rect.y2 < 0) /* R in lower right corner */
{
return ((rect.x1 * rect.x1 + rect.y2 * rect.y2) < Rad2);
}
else if (rect.y1 > 0) /* R in upper right corner */
{
return ((rect.x1 * rect.x1 + rect.y1 * rect.y1) < Rad2);
}
else /* R due East of circle */
{
return (rect.x1 < Rad);
}
}
else /* R on circle vertical centerline */
{
if (rect.y2 < 0) /* R due South of circle */
{
return (abs(rect.y2) < Rad);
}
else if (rect.y1 > 0) /* R due North of circle */
{
return (rect.y1 < Rad);
}
else /* R contains circle centerpoint */
{
return(true);
}
}
}
// Get the range of effect of an object
static int gridObjRange(WZ_DECL_UNUSED const BASE_OBJECT* psObj)
{
return world_coord(20);
}

130
src/mapgrid.cpp Normal file
View File

@ -0,0 +1,130 @@
/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2010 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* mapgrid.cpp
*
* Functions for storing objects in a quad-tree like object over the map.
* The objects are stored in the quad-tree.
*
*/
#include "lib/framework/frame.h"
#include "objects.h"
#include "map.h"
#include "mapgrid.h"
#include "pointtree.h"
// the current state of the iterator
void **gridIterator;
PointTree *gridPointTree = NULL; // A quad-tree-like object.
// initialise the grid system
BOOL gridInitialise(void)
{
ASSERT(gridPointTree == NULL, "gridInitialise already called, without calling gridShutDown.");
gridPointTree = new PointTree;
return true; // Yay, nothing failed!
}
// reset the grid system
void gridReset(void)
{
gridPointTree->clear();
// Put all existing objects into the point tree.
for (unsigned player = 0; player < MAX_PLAYERS; player++)
{
BASE_OBJECT *start[3] = {(BASE_OBJECT *)apsDroidLists[player], (BASE_OBJECT *)apsStructLists[player], (BASE_OBJECT *)apsFeatureLists[player]};
for (unsigned type = 0; type != sizeof(start)/sizeof(*start); ++type)
{
for (BASE_OBJECT *psObj = start[type]; psObj != NULL; psObj = psObj->psNext)
{
gridPointTree->insert(psObj, psObj->pos.x, psObj->pos.y);
}
}
}
gridPointTree->sort();
}
// shutdown the grid system
void gridShutDown(void)
{
delete gridPointTree;
gridPointTree = NULL;
}
// add an object to the grid system
void gridAddObject(BASE_OBJECT *psObj)
{
ASSERT_OR_RETURN(, psObj != NULL, "Attempted to add a NULL pointer");
ASSERT_OR_RETURN(, !isDead(psObj), "Attempted to add dead object %s(%d) to the map grid!", objInfo(psObj), (int)psObj->id);
//gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_ADDOBJECT);
}
// move an object within the grid
// oldX,oldY are the old position of the object in world coords
void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY)
{
if (map_coord(psDroid->pos.x) == map_coord(oldX)
&& map_coord(psDroid->pos.y) == map_coord(oldY))
{
// havn't changed the tile the object is on, don't bother updating
return;
}
//gridCalcCoverage((BASE_OBJECT*)psDroid, oldX,oldY, GRID_REMOVEOBJECT);
//gridCalcCoverage((BASE_OBJECT*)psDroid, psDroid->pos.x, psDroid->pos.y, GRID_ADDOBJECT);
}
// remove an object from the grid system
void gridRemoveObject(BASE_OBJECT *psObj)
{
//gridCalcCoverage(psObj, (SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y, GRID_REMOVEOBJECT);
}
// initialise the grid system to start iterating through units that
// could affect a location (x,y in world coords)
void gridStartIterate(SDWORD x, SDWORD y/*, uint32_t radius*/)
{
uint32_t radius = 20*TILE_UNITS;
gridIterator = pointTreeQuery(gridPointTree, x, y, radius); // Use the C interface, for NULL termination.
/*
// In case you are curious.
int len = 0;
for(void **x = gridIterator; *x != NULL; ++x)
++len;
debug(LOG_WARNING, "gridStartIterate found %d objects", len);
*/
}
// compact some of the grid arrays
void gridGarbageCollect(void)
{
gridReset();
}
// Display all the grid's an object is a member of
void gridDisplayCoverage(BASE_OBJECT *psObj)
{
}

View File

@ -18,64 +18,57 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/** @file
* Definitions for storing objects in a grid over the map.
* The objects are stored in every grid over which they might have some influence.
* Allows querying which objects are within a given radius of a given location.
*/
#ifndef __INCLUDED_SRC_MAPGRID_H__
#define __INCLUDED_SRC_MAPGRID_H__
/** Number of objects in each chunk of the grid array. */
#define MAX_GRID_ARRAY_CHUNK 32
// Objects are stored in an extensible array for each grid
typedef struct _grid_array
#ifdef __cplusplus
extern "C"
{
BASE_OBJECT *apsObjects[MAX_GRID_ARRAY_CHUNK];
#endif //__cplusplus
struct _grid_array *psNext;
} GRID_ARRAY;
extern void **gridIterator; ///< The iterator.
/** The number of tiles per grid. */
#define GRID_SIZE 8
#define GRID_MAXAREA (MAP_MAXAREA/(GRID_SIZE*GRID_SIZE))
// initialise the grid system
extern BOOL gridInitialise(void);
// shutdown the grid system
extern void gridShutDown(void);
//clear the grid of everything on it
extern void gridClear(void);
// reset the grid system
extern void gridReset(void);
// add an object to the grid system
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
extern void gridAddObject(BASE_OBJECT *psObj);
// move a DROID within the grid
// oldX,oldY are the old position of the object in world coords
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
extern void gridMoveDroid(DROID* psDroid, SDWORD oldX, SDWORD oldY);
// remove an object from the grid system
// TODO THIS FUNCTION DOES NOT DO ANYTHING ANYMORE. Delete this function
extern void gridRemoveObject(BASE_OBJECT *psObj);
// compact some of the grid arrays
// Calls gridReset().
// TODO Remove this function, call gridReset() directly.
extern void gridGarbageCollect(void);
// Display all the grid's an object is a member of
// TODO This function doesn't do anything, should probably be deleted.
extern void gridDisplayCoverage(BASE_OBJECT *psObj);
// initialise the grid system to start iterating through units that
// could affect a location (x,y in world coords)
extern void gridStartIterate(SDWORD x, SDWORD y);
/// Find all objects within radius r = 20*TILE_UNITS. Call gridIterate() to get the search results.
extern void gridStartIterate(int32_t x, int32_t y/*, uint32_t radius*/);
// get the next object that could affect a location,
// should only be called after gridStartIterate
extern BASE_OBJECT *gridIterate(void);
/// Get the next search result from gridStartIterate, or NULL if finished.
static inline BASE_OBJECT *gridIterate(void)
{
return (BASE_OBJECT *)*gridIterator++;
}
#ifdef __cplusplus
}
#endif //__cplusplus
#endif // __INCLUDED_SRC_MAPGRID_H__

View File

@ -35,6 +35,11 @@
#include "function.h"
#include "stats.h"
#ifdef __cplusplus
extern "C"
{
#endif //__cplusplus
/* Initialise the object system */
extern BOOL objInitialise(void);
@ -48,4 +53,8 @@ extern void reverseObjectList(BASE_OBJECT **ppsList);
/** Output an informative string about this object. For debugging. */
const char *objInfo(const BASE_OBJECT *psObj);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif // __INCLUDED_SRC_OBJECTS_H__

View File

@ -17,16 +17,6 @@ The coloured areas are the points in the ranges (note that each range also conta
outside the rectangles). The orange points are the search results.
*/
typedef std::pair<uint64_t, void *> POINT;
struct _pointTree
{
std::vector<POINT> points;
std::vector<void *> lastQueryResults;
};
// Expands bit pattern abcd efgh to 0a0b 0c0d 0e0f 0g0h
static uint64_t expand(uint32_t x)
{
@ -66,29 +56,19 @@ static uint64_t interleave(int32_t x, int32_t y)
return expandX(x) | expandY(y);
}
POINT_TREE *pointTreeCreate(void)
void PointTree::insert(void *pointData, int32_t x, int32_t y)
{
return new POINT_TREE;
points.push_back(Point(interleave(x, y), pointData));
}
void pointTreeDestroy(POINT_TREE *pointTree)
void PointTree::clear()
{
delete pointTree;
points.clear();
}
void pointTreeInsert(POINT_TREE *pointTree, void *point, int32_t x, int32_t y)
void PointTree::sort()
{
pointTree->points.push_back(POINT(interleave(x, y), point));
}
void pointTreeClear(POINT_TREE *pointTree)
{
pointTree->points.clear();
}
void pointTreeSort(POINT_TREE *pointTree)
{
std::sort(pointTree->points.begin(), pointTree->points.end());
std::sort(points.begin(), points.end());
}
//#define DUMP_IMAGE // All x and y coordinates must be in range -500 to 499, if dumping an image.
@ -98,7 +78,12 @@ uint8_t ppm[1000][1000][3];
int doDump = false;
#endif //DUMP_IMAGE
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radius)
struct PointTreeRange
{
uint64_t a, z;
};
PointTree::ResultVector &PointTree::query(int32_t x, int32_t y, uint32_t radius)
{
int32_t minXo = x - radius;
int32_t maxXo = x + radius;
@ -117,11 +102,11 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
uint64_t splitY1 = expandY(splitYo - 1);
uint64_t splitY2 = expandY(splitYo);
uint64_t ranges[4][2] = {{minX | minY, splitX1 | splitY1},
{splitX2 | minY, maxX | splitY1},
{minX | splitY2, splitX1 | maxY},
{splitX2 | splitY2, maxX | maxY}
};
PointTreeRange ranges[4] = {{minX | minY, splitX1 | splitY1},
{splitX2 | minY, maxX | splitY1},
{minX | splitY2, splitX1 | maxY},
{splitX2 | splitY2, maxX | maxY}
};
int numRanges = 4;
#ifdef DUMP_IMAGE
@ -130,8 +115,7 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
{
f = fopen("pointtree.ppm", "wb");
fprintf(f, "P6\n1000 1000\n255\n");
int px, py;
for (py = 0; py != 1000; ++py) for (px = 0; px != 1000; ++px)
for (int py = 0; py != 1000; ++py) for (int px = 0; px != 1000; ++px)
{
int ax = px - 500, ay = py-500;
double dx = ax - x, dy = ay - y;
@ -152,19 +136,19 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
ppm[py][px][2] /= 2;
}
uint64_t nn = expandX(ax) | expandY(ay);
if (ranges[0][0] <= nn && nn <= ranges[0][1])
if (ranges[0].a <= nn && nn <= ranges[0].z)
{
ppm[py][px][0] /= 2;
}
if (ranges[1][0] <= nn && nn <= ranges[1][1])
if (ranges[1].a <= nn && nn <= ranges[1].z)
{
ppm[py][px][1] /= 2;
}
if (ranges[2][0] <= nn && nn <= ranges[2][1])
if (ranges[2].a <= nn && nn <= ranges[2].z)
{
ppm[py][px][2] /= 2;
}
if (ranges[3][0] <= nn && nn <= ranges[3][1] && ((ax ^ ay) & 2))
if (ranges[3].a <= nn && nn <= ranges[3].z && ((ax ^ ay) & 2))
{
ppm[py][px][0] /= 2;
ppm[py][px][1] /= 2;
@ -174,45 +158,43 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
}
#endif //DUMP_IMAGE
if (ranges[1][0] > ranges[2][0])
// Sort ranges ready to be merged.
if (ranges[1].a > ranges[2].a)
{
std::swap(ranges[1][0], ranges[2][0]);
std::swap(ranges[1][1], ranges[2][1]);
std::swap(ranges[1], ranges[2]);
}
if (ranges[2][1] + 1 >= ranges[3][0])
// Merge ranges if needed.
if (ranges[2].z + 1 >= ranges[3].a)
{
ranges[2][1] = ranges[3][1];
ranges[2].z = ranges[3].z;
--numRanges;
}
if (ranges[1][1] + 1 >= ranges[2][0])
if (ranges[1].z + 1 >= ranges[2].a)
{
ranges[1][1] = ranges[2][1];
ranges[2][0] = ranges[3][0];
ranges[2][1] = ranges[3][1];
ranges[1].z = ranges[2].z;
ranges[2] = ranges[3];
--numRanges;
}
if (ranges[0][1] + 1 >= ranges[1][0])
if (ranges[0].z + 1 >= ranges[1].a)
{
ranges[0][1] = ranges[1][1];
ranges[1][0] = ranges[2][0];
ranges[1][1] = ranges[2][1];
ranges[2][0] = ranges[3][0];
ranges[2][1] = ranges[3][1];
ranges[0].z = ranges[1].z;
ranges[1] = ranges[2];
ranges[2] = ranges[3];
--numRanges;
}
pointTree->lastQueryResults.clear();
lastQueryResults.clear();
for (unsigned r = 0; r != numRanges; ++r)
{
std::vector<POINT>::iterator i1 = std::lower_bound(pointTree->points.begin(), pointTree->points.end(), POINT(ranges[r][0], NULL));
std::vector<POINT>::iterator i2 = std::lower_bound(i1, pointTree->points.end(), POINT(ranges[r][1] + 1, NULL));
for (std::vector<POINT>::const_iterator i = i1; i != i2; ++i)
Vector::iterator i1 = std::lower_bound(points.begin(), points.end(), Point(ranges[r].a, NULL));
Vector::iterator i2 = std::lower_bound(i1, points.end(), Point(ranges[r].z + 1, NULL));
for (Vector::const_iterator i = i1; i != i2; ++i)
{
uint64_t px = i->first & 0xAAAAAAAAAAAAAAAA;
uint64_t py = i->first & 0x5555555555555555;
if (px >= minX && px <= maxX && py >= minY && py <= maxY) // Only add point if it's at least in the desired square.
{
pointTree->lastQueryResults.push_back(i->second);
lastQueryResults.push_back(i->second);
#ifdef DUMP_IMAGE
if (doDump)
{
@ -225,8 +207,6 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
}
}
pointTree->lastQueryResults.push_back(NULL); // So it's possible to know when the list ends.
#ifdef DUMP_IMAGE
if (doDump)
{
@ -235,5 +215,41 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
}
#endif //DUMP_IMAGE
return lastQueryResults;
}
/////////////////
// C interface //
/////////////////
PointTree *pointTreeCreate(void)
{
return new PointTree;
}
void pointTreeDestroy(PointTree *pointTree)
{
delete pointTree;
}
void pointTreeInsert(PointTree *pointTree, void *pointData, int32_t x, int32_t y)
{
pointTree->insert(pointData, x, y);
}
void pointTreeClear(POINT_TREE *pointTree)
{
pointTree->clear();
}
void pointTreeSort(POINT_TREE *pointTree)
{
pointTree->sort();
}
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, uint32_t radius)
{
pointTree->query(x, y, radius);
pointTree->lastQueryResults.push_back(NULL); // So it's possible to know when the list ends.
return &pointTree->lastQueryResults[0];
}

View File

@ -3,13 +3,37 @@
#include "lib/framework/types.h"
struct _pointTree;
typedef struct _pointTree POINT_TREE;
#ifdef __cplusplus
#include <vector>
class PointTree
{
public:
typedef std::vector<void *> ResultVector;
void insert(void *pointData, int32_t x, int32_t y); ///< Inserts a point into the point tree.
void clear(); ///< Clears the PointTree.
void sort(); ///< Must be done between inserting and querying, to get meaningful results.
/// Returns all points less than or equal to radius from (x, y), possibly plus some extra nearby points.
/// (More specifically, returns all objects in a square with edge length 2*radius.)
ResultVector &query(int32_t x, int32_t y, uint32_t radius);
ResultVector lastQueryResults;
private:
typedef std::pair<uint64_t, void *> Point;
typedef std::vector<Point> Vector;
Vector points;
};
extern "C"
{
#endif
#endif //_cplusplus
struct PointTree;
typedef struct PointTree POINT_TREE;
POINT_TREE *pointTreeCreate(void);
void pointTreeDestroy(POINT_TREE *pointTree);
@ -21,10 +45,10 @@ void pointTreeClear();
void pointTreeSort(POINT_TREE *pointTree);
// Returns all points less than or equal to radius from (x, y), possibly plus some extra nearby points.
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radius);
void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, uint32_t radius);
#ifdef __cplusplus
}
#endif
#endif //_cplusplus
#endif //_point_tree_h

View File

@ -223,7 +223,6 @@ static void proj_Free(PROJECTILE *psObj)
/* Decrement any reference counts the projectile may have increased */
setProjectileDamaged(psObj, NULL);
setProjectileSource(psObj, NULL);
setProjectileDestination(psObj, NULL);
free(psObj);
}