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

View File

@ -35,6 +35,11 @@
#include "function.h" #include "function.h"
#include "stats.h" #include "stats.h"
#ifdef __cplusplus
extern "C"
{
#endif //__cplusplus
/* Initialise the object system */ /* Initialise the object system */
extern BOOL objInitialise(void); extern BOOL objInitialise(void);
@ -48,4 +53,8 @@ extern void reverseObjectList(BASE_OBJECT **ppsList);
/** Output an informative string about this object. For debugging. */ /** Output an informative string about this object. For debugging. */
const char *objInfo(const BASE_OBJECT *psObj); const char *objInfo(const BASE_OBJECT *psObj);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif // __INCLUDED_SRC_OBJECTS_H__ #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. 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 // Expands bit pattern abcd efgh to 0a0b 0c0d 0e0f 0g0h
static uint64_t expand(uint32_t x) 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); 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)); std::sort(points.begin(), points.end());
}
void pointTreeClear(POINT_TREE *pointTree)
{
pointTree->points.clear();
}
void pointTreeSort(POINT_TREE *pointTree)
{
std::sort(pointTree->points.begin(), pointTree->points.end());
} }
//#define DUMP_IMAGE // All x and y coordinates must be in range -500 to 499, if dumping an image. //#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; int doDump = false;
#endif //DUMP_IMAGE #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 minXo = x - radius;
int32_t maxXo = 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 splitY1 = expandY(splitYo - 1);
uint64_t splitY2 = expandY(splitYo); uint64_t splitY2 = expandY(splitYo);
uint64_t ranges[4][2] = {{minX | minY, splitX1 | splitY1}, PointTreeRange ranges[4] = {{minX | minY, splitX1 | splitY1},
{splitX2 | minY, maxX | splitY1}, {splitX2 | minY, maxX | splitY1},
{minX | splitY2, splitX1 | maxY}, {minX | splitY2, splitX1 | maxY},
{splitX2 | splitY2, maxX | maxY} {splitX2 | splitY2, maxX | maxY}
}; };
int numRanges = 4; int numRanges = 4;
#ifdef DUMP_IMAGE #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"); f = fopen("pointtree.ppm", "wb");
fprintf(f, "P6\n1000 1000\n255\n"); fprintf(f, "P6\n1000 1000\n255\n");
int px, py; for (int py = 0; py != 1000; ++py) for (int px = 0; px != 1000; ++px)
for (py = 0; py != 1000; ++py) for (px = 0; px != 1000; ++px)
{ {
int ax = px - 500, ay = py-500; int ax = px - 500, ay = py-500;
double dx = ax - x, dy = ay - y; 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; ppm[py][px][2] /= 2;
} }
uint64_t nn = expandX(ax) | expandY(ay); 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; 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; 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; 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][0] /= 2;
ppm[py][px][1] /= 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 #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], ranges[2]);
std::swap(ranges[1][1], ranges[2][1]);
} }
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; --numRanges;
} }
if (ranges[1][1] + 1 >= ranges[2][0]) if (ranges[1].z + 1 >= ranges[2].a)
{ {
ranges[1][1] = ranges[2][1]; ranges[1].z = ranges[2].z;
ranges[2][0] = ranges[3][0]; ranges[2] = ranges[3];
ranges[2][1] = ranges[3][1];
--numRanges; --numRanges;
} }
if (ranges[0][1] + 1 >= ranges[1][0]) if (ranges[0].z + 1 >= ranges[1].a)
{ {
ranges[0][1] = ranges[1][1]; ranges[0].z = ranges[1].z;
ranges[1][0] = ranges[2][0]; ranges[1] = ranges[2];
ranges[1][1] = ranges[2][1]; ranges[2] = ranges[3];
ranges[2][0] = ranges[3][0];
ranges[2][1] = ranges[3][1];
--numRanges; --numRanges;
} }
pointTree->lastQueryResults.clear(); lastQueryResults.clear();
for (unsigned r = 0; r != numRanges; ++r) 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)); Vector::iterator i1 = std::lower_bound(points.begin(), points.end(), Point(ranges[r].a, NULL));
std::vector<POINT>::iterator i2 = std::lower_bound(i1, pointTree->points.end(), POINT(ranges[r][1] + 1, NULL)); Vector::iterator i2 = std::lower_bound(i1, points.end(), Point(ranges[r].z + 1, NULL));
for (std::vector<POINT>::const_iterator i = i1; i != i2; ++i) for (Vector::const_iterator i = i1; i != i2; ++i)
{ {
uint64_t px = i->first & 0xAAAAAAAAAAAAAAAA; uint64_t px = i->first & 0xAAAAAAAAAAAAAAAA;
uint64_t py = i->first & 0x5555555555555555; 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. 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 #ifdef DUMP_IMAGE
if (doDump) 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 #ifdef DUMP_IMAGE
if (doDump) if (doDump)
{ {
@ -235,5 +215,41 @@ void **pointTreeQuery(POINT_TREE *pointTree, int32_t x, int32_t y, int32_t radiu
} }
#endif //DUMP_IMAGE #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]; return &pointTree->lastQueryResults[0];
} }

View File

@ -3,13 +3,37 @@
#include "lib/framework/types.h" #include "lib/framework/types.h"
struct _pointTree;
typedef struct _pointTree POINT_TREE;
#ifdef __cplusplus #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" extern "C"
{ {
#endif #endif //_cplusplus
struct PointTree;
typedef struct PointTree POINT_TREE;
POINT_TREE *pointTreeCreate(void); POINT_TREE *pointTreeCreate(void);
void pointTreeDestroy(POINT_TREE *pointTree); void pointTreeDestroy(POINT_TREE *pointTree);
@ -21,10 +45,10 @@ void pointTreeClear();
void pointTreeSort(POINT_TREE *pointTree); void pointTreeSort(POINT_TREE *pointTree);
// Returns all points less than or equal to radius from (x, y), possibly plus some extra nearby points. // 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 #ifdef __cplusplus
} }
#endif #endif //_cplusplus
#endif //_point_tree_h #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 */ /* Decrement any reference counts the projectile may have increased */
setProjectileDamaged(psObj, NULL); setProjectileDamaged(psObj, NULL);
setProjectileSource(psObj, NULL); setProjectileSource(psObj, NULL);
setProjectileDestination(psObj, NULL);
free(psObj); free(psObj);
} }