/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program 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 this program; if not, see .
===========================================================================
*/
////////////////////////////////////////////////////////////////////////////////////////
// RAVEN STANDARD TEMPLATE LIBRARY
// (c) 2002 Activision
//
//
// Graph
// -----
// A Graph object is one of the most generic data structures. Theoretically, a graph
// could be used to make a tree, list, or any other standard structure. A graph has
// two data types: NODE and EDGE, and maintains connection information how edges can
// link two nodes.
//
// One of the most intuitive uses of a graph class is a navigation bouy system. In such
// a use, the nodes would probably be vector based objects with positional information
// and the edges might contain portal information (door is open / closed / locked).
//
// Another example might be a web page, or decision tree, or any other problem space
// which requires object connection data.
//
//
//
//
// Implimentation
// --------------
// This template allocates a pool for NODES, a pool for EDGES, and a grid2_vs to serve
// as an Adjacency Matrix (called Links). The Adj. Matrix stores indicies to EDGE objects
// in the EDGE pool.
//
//
//
//
// What If You Do Not Need Any Edge Objects?
// -----------------------------------------
// It's fairly common to have a graph with no connection information other than the
// existance of the link. For this case, you should be able to create a graph with a
// MAXEDGES of 1. You will want to call the version of connect_node() which does not
// take an edge object, and uses 1 as the "index" in the Adj. Matrix.
//
//
//
//
// How Do You Search?
// -------------------
// This graph supports 3 search methods:
// Breadth First - Exausts as many links close to start as possible
// Depth First - Gets as far from start as quickly as possible
// A* - Uses a distance heuristic toward end point
//
// First, create a (graph_vs::search) object with the start and end points that you want
// to search for. Then, call either bfs(), dfs(), or astar(). When you get the
// object back, it will have a vector of all the nodes that were visited and methods
// for iterating over that vector to get the path.
//
// for (TestSearch.path_begin(); !TestSearch.path_end(); TestSearch.path_inc())
// {
// sprintf(Buf, "(%d)", TestSearch.path_at());
// OutputDebugString(Buf);
// }
//
//
//
//
// Complexity Analisis
// -------------------
// All data operations except remove_node() are O(1) constant time.
// remove_node() can be O(n) where n is the number of NODES in the graph.
//
// Search routines:
// BFS -
// DFS -
// A* -
//
//
//
//
////////////////////////////////////////////////////////////////////////////////////////
#if !defined(RAGL_GRAPH_VS_INC)
#define RAGL_GRAPH_VS_INC
////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////
#if defined(RA_DEBUG_LINKING)
#pragma message("...including graph_vs.h")
#endif
#if !defined(RAGL_COMMON_INC)
#include "ragl_common.h"
#endif
#if !defined(RATL_ARRAY_VS_INC)
#include "../Ratl/array_vs.h"
#endif
#if !defined(RATL_VECTOR_VS_INC)
#include "../Ratl/vector_vs.h"
#endif
#if !defined(RATL_BITS_INC)
#include "../Ratl/bits_vs.h"
#endif
#if !defined(RATL_QUEUE_VS_INC)
#include "../Ratl/queue_vs.h"
#endif
#if !defined(RATL_STACK_VS_INC)
#include "../Ratl/stack_vs.h"
#endif
#if !defined(RBTREE_MAP_VS_INC)
#include "../Ratl/map_vs.h"
#endif
#if !defined(RATL_POOL_VS_INC)
#include "../Ratl/pool_vs.h"
#endif
#if !defined(RATL_GRID_VS_INC)
#include "../Ratl/grid_vs.h"
#endif
namespace ragl
{
////////////////////////////////////////////////////////////////////////////////////////
// The Graph Class
////////////////////////////////////////////////////////////////////////////////////////
template
class graph_vs : public ratl::ratl_base
{
public:
////////////////////////////////////////////////////////////////////////////////////
// The Graph User Class
//
// When executing a search (in particular an A* search), you may want to derive your
// own user class so that you can provide specific functionality to the search. For
// example, you might want characters on one team to bias the cost of going to nodes
// which are occupied by the other team. Or you might want to allow specific
// characters to access some edges where others cannot. Perhaps one can fly or has
// a special key to a door...
//
////////////////////////////////////////////////////////////////////////////////////
class user
{
public:
////////////////////////////////////////////////////////////////////////////////////
// can be invalid edge (For Region)
////////////////////////////////////////////////////////////////////////////////////
virtual bool can_be_invalid(const TEDGE& Edge) const = 0;
////////////////////////////////////////////////////////////////////////////////////
// valid edge (For A* and Region)
////////////////////////////////////////////////////////////////////////////////////
virtual bool is_valid(TEDGE& Edge, int EndPoint=0) const = 0;
////////////////////////////////////////////////////////////////////////////////////
// cost estimate from A to B (For A*)
////////////////////////////////////////////////////////////////////////////////////
virtual float cost(const TNODE& A, const TNODE& B) const = 0;
////////////////////////////////////////////////////////////////////////////////////
// cost estimate of Edge (For A*)
////////////////////////////////////////////////////////////////////////////////////
virtual float cost(const TEDGE& Edge, const TNODE& B) const = 0;
////////////////////////////////////////////////////////////////////////////////////
// same floor check (For Triangulation)
////////////////////////////////////////////////////////////////////////////////////
virtual bool on_same_floor(const TNODE& A, const TNODE& B) const = 0;
////////////////////////////////////////////////////////////////////////////////////
// setup the edge (For Triangulation)
////////////////////////////////////////////////////////////////////////////////////
virtual void setup_edge(TEDGE& Edge, int A, int B, bool OnHull, const TNODE& NodeA, const TNODE& NodeB, bool CanBeInvalid=false) = 0;
};
////////////////////////////////////////////////////////////////////////////////////
// Capacity Enum
////////////////////////////////////////////////////////////////////////////////////
enum
{
CAPACITY = MAXNODES,
NULLEDGE = -1,
};
typedef typename ratl::pool_vs TNodes;
typedef typename ratl::pool_vs TEdges;
struct SNodeNeighbor
{
short mEdge;
short mNode;
};
typedef typename ratl::vector_vs TNodeNeighbors;
typedef typename ratl::array_vs< TNodeNeighbors, MAXNODES> TLinks;
typedef typename ragl::graph_vs TGraph;
////////////////////////////////////////////////////////////////////////////////////
// cells class
////////////////////////////////////////////////////////////////////////////////////
template
class cells : public ratl::ratl_base
{
public:
enum
{
SIZEX = CELLSX,
SIZEY = CELLSY,
SIZENODES = NODESPERCELL
};
struct SSortNode
{
float mCost;
short mHandle;
bool operator<(const SSortNode& t) const
{
return (mCost TSortNodes;
typedef typename ratl::vector_vs TCellNodes;
struct SCell
{
TCellNodes mNodes;
TCellNodes mEdges;
};
typedef typename ratl::grid2_vs TCells;
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
cells(TGraph& g) : mGraph(g)
{
}
void clear()
{
mCells.clear();
SCell EmptyCell;
mCells.init(EmptyCell);
}
void get_cell_upperleft(int x, int y, float& xReal, float& yReal)
{
mCells.get_cell_upperleft(x,y,xReal,yReal);
}
void get_cell_lowerright(int x, int y, float& xReal, float& yReal)
{
mCells.get_cell_lowerright(x,y,xReal,yReal);
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void expand_bounds(int nodeHandle)
{
TNODE& node = mGraph.get_node(nodeHandle);
mCells.expand_bounds(node[0], node[1]);
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
SCell& get_cell(float x, float y)
{
return mCells.get(x,y);
}
SCell& get_cell(int x, int y)
{
return mCells.get(x,y);
}
void convert_to_cell_coords(float x, float y, int& xint, int& yint)
{
mCells.get_cell_coords(x, y, xint, yint);
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void fill_cells_nodes(float range)
{
// I. Fill All The Cells With The Points Contained By Those Cells
//----------------------------------------------------------------
bool full = false;
for (typename TNodes::iterator it=mGraph.nodes_begin(); it!=mGraph.nodes_end() && !full; it++)
{
TNODE& node = (*it);
SCell& cell = mCells.get(node[0], node[1]);
cell.mNodes.push_back(it.index());
full = cell.mNodes.full();
assert(!full || "Cell Filled On Inital Containment"==0);
}
mCells.scale_by_largest_axis(range);
// II. Go To All Neighboring Cells And Get Them
//==============================================
int iRange = (int)(range) + 1;
typename TCells::riterator rcell;
typename TCells::riterator rcellend;
CVec3 cellCenter(0,0,0);
CVec3 nodeCenter(0,0,0);
TSortNodes *sortnodes = new TSortNodes;
SSortNode sortnode;
TCells *sortcells = new TCells;
sortcells->copy_bounds(mCells);
// For Every Cell
//----------------
for (int x=0; xclear();
mCells.get_cell_position(x,y, cellCenter.v[0], cellCenter.v[1]);
for (rcell = mCells.rangeBegin(iRange,x,y); !rcell.at_end(); rcell++)
{
SCell& cell = (*rcell);
// Add The Nodes To The Sort List
//--------------------------------
for (int i=0; ifull(); i++)
{
int nodeHandle = cell.mNodes[i];
TNODE& node = mGraph.get_node(nodeHandle);
nodeCenter[0] = node[0];
nodeCenter[1] = node[1];
sortnode.mHandle = nodeHandle;
sortnode.mCost = cellCenter.Dist2(nodeCenter);
sortnodes->push_back(sortnode);
}
}
// Sort The Results
//------------------
sortnodes->sort();
// Copy The Sorted Nodes Vector Into The Sorted Cell (Of The Sorted Cell Grid)
//----------------------------------------------------------------------------
SCell& cell = sortcells->get(x,y);
cell.mNodes.clear();
for (int i=0; isize() && iget(xb,yb);
SCell& mcell = mCells.get(xb,yb);
mcell.mNodes = scell.mNodes;
}
}
delete sortnodes;
delete sortcells;
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void fill_cells_edges(float range)
{
// I. Fill All The Cells With The Points Contained By Those Cells
//----------------------------------------------------------------
bool full = false;
for (typename TEdges::iterator eit=mGraph.edges_begin(); eit!=mGraph.edges_end() && !full; eit++)
{
TEDGE& edge = (*eit);
SCell& cell = mCells.get(edge[0], edge[1]);
cell.mEdges.push_back(eit.index());
full = cell.mEdges.full();
assert(!full || "Cell Filled On Inital Containment"==0);
}
mCells.scale_by_largest_axis(range);
// II. Go To All Neighboring Cells And Get Them
//==============================================
int iRange = (int)(range) + 1;
typename TCells::riterator rcell;
typename TCells::riterator rcellend;
CVec3 cellCenter(0,0,0);
CVec3 nodeCenter(0,0,0);
TSortNodes *sortedges = new TSortNodes;
SSortNode sortnode;
TCells *sortcells = new TCells;
sortcells->copy_bounds(mCells);
// For Every Cell
//----------------
for (int x=0; xclear();
mCells.get_cell_position(x,y, cellCenter.v[0], cellCenter.v[1]);
for (rcell = mCells.rangeBegin(iRange,x,y); !rcell.at_end(); rcell++)
{
SCell& cell = (*rcell);
// Add The Edges To The Sort List
//--------------------------------
for (int e=0; efull(); e++)
{
int edgeHandle = cell.mEdges[e];
TEDGE& edge = mGraph.get_edge(edgeHandle);
nodeCenter[0] = edge[0];
nodeCenter[1] = edge[1];
sortnode.mHandle = edgeHandle;
sortnode.mCost = cellCenter.Dist2(nodeCenter);
sortedges->push_back(sortnode);
}
}
// Sort The Results
//------------------
sortedges->sort();
// Copy The Sorted Nodes Vector Into The Sorted Cell (Of The Sorted Cell Grid)
//----------------------------------------------------------------------------
SCell& cell = sortcells->get(x,y);
cell.mEdges.clear();
for (int j=0; jsize() && jget(xb,yb);
SCell& mcell = mCells.get(xb,yb);
mcell.mEdges = scell.mEdges;
}
}
delete sortedges;
delete sortcells;
}
private:
TGraph& mGraph;
TCells mCells;
};
////////////////////////////////////////////////////////////////////////////////////
// Remove All Edges
////////////////////////////////////////////////////////////////////////////////////
void clear_edges()
{
mEdges.clear();
mEdges.alloc(); // Alloc a dummy edge at location 0
for (int i=0; iB) if reflexive, also (B->A)
////////////////////////////////////////////////////////////////////////////////////
int connect_node(const TEDGE& t, int nodeA, int nodeB, bool reflexive=true)
{
if (nodeA==nodeB || !nodeA || !nodeB || !mNodes.is_used(nodeA) || !mNodes.is_used(nodeB))
{
assert("ERROR: Cannot Connect A and B!"==0);
return 0;
}
if (mLinks[nodeA].full() || (reflexive && mLinks[nodeB].full()))
{
assert("ERROR: Max edges per node exceeded!"==0);
return 0;
}
if (mEdges.full())
{
assert("ERROR: Max edges exceeded!"==0);
return 0;
}
SNodeNeighbor nNbr;
nNbr.mNode = nodeB;
nNbr.mEdge = mEdges.alloc();
mEdges[nNbr.mEdge] = t;
mLinks[nodeA].push_back(nNbr);
if (reflexive)
{
nNbr.mNode = nodeA;
mLinks[nodeB].push_back(nNbr);
}
return nNbr.mEdge;
}
////////////////////////////////////////////////////////////////////////////////////
// Connect Node Without Allocating An Edge Object (A->B) if reflexive, also (B->A)
////////////////////////////////////////////////////////////////////////////////////
void connect_node(int nodeA, int nodeB, bool reflexive=true)
{
if (nodeA==nodeB || !nodeA || !nodeB || !mNodes.is_used(nodeA) || !mNodes.is_used(nodeB))
{
assert("ERROR: Cannot Connect A and B!"==0);
return;
}
if (mLinks[nodeA].full() || (reflexive && mLinks[nodeB].full()))
{
assert("ERROR: Max edges per node exceeded!"==0);
return;
}
SNodeNeighbor nNbr;
nNbr.mNode = nodeB;
nNbr.mEdge = 0;
mLinks[nodeA].push_back(nNbr);
if (reflexive)
{
nNbr.mNode = nodeA;
mLinks[nodeB].push_back(nNbr);
}
return;
}
////////////////////////////////////////////////////////////////////////////////////
// Remove Edge
////////////////////////////////////////////////////////////////////////////////////
void remove_edge(int nodeA, int nodeB, bool reflexive=true)
{
if (!mNodes.is_used(nodeA) || (!mNodes.is_used(nodeB) && nodeA==nodeB))
{
assert("Unable To Remove Edge"==0);
return;
}
int linkNum=0;
for (linkNum=0; linkNumindex.
//
// The handle heap is used by A* to sort the open list by cost.
//
////////////////////////////////////////////////////////////////////////////////////////
template
class handle_heap
{
public:
////////////////////////////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////////////////////////////
handle_heap(TNodes& Nodes) : mNodes(Nodes)
{
clear();
}
////////////////////////////////////////////////////////////////////////////////////
// Get The Size (The Difference Between The Push And Pop "Pointers")
////////////////////////////////////////////////////////////////////////////////////
int size() const
{
return (mPush);
}
////////////////////////////////////////////////////////////////////////////////////
// Check To See If The Size Is Zero
////////////////////////////////////////////////////////////////////////////////////
bool empty() const
{
return (!size());
}
////////////////////////////////////////////////////////////////////////////////////
// Check To See If The Size Is Full
////////////////////////////////////////////////////////////////////////////////////
bool full() const
{
return (size()==MAXNODES);
}
////////////////////////////////////////////////////////////////////////////////////
// Empty Out The Entire Heap
////////////////////////////////////////////////////////////////////////////////////
void clear()
{
mPush = 0;
for (int i=0; i0); // Don't Try To Look At This If There Is Nothing In Here
return (mData[0]);
}
////////////////////////////////////////////////////////////////////////////////////
// Accessor
////////////////////////////////////////////////////////////////////////////////////
T& operator[](int handle)
{
// If You Hit This Assert, Then You Are Asking For Unallocated Data
//------------------------------------------------------------------
assert(used(handle));
return mData[mHandleToPos[handle]];
}
////////////////////////////////////////////////////////////////////////////////////
// Add A Value To The Queue
////////////////////////////////////////////////////////////////////////////////////
void push(const T& nValue)
{
assert(size()0);
mPush--;
assert(mHandleToPos[mData[0].handle()]==0);
// Swap The Lowest Element Up To The Spot We Just "Erased"
//---------------------------------------------------------
swap(0, mPush);
mHandleToPos[mData[mPush].handle()] = -1; // Erase This Handles Marker
// Fix Possible Heap Inconsistancies
//-----------------------------------
reheapify_downward(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Call This Func If The Value At The Given Handle Has Changed & Needs Adjustment
////////////////////////////////////////////////////////////////////////////////////
void reheapify(int handle)
{
assert(used(handle));
int Pos = mHandleToPos[handle];
reheapify_upward(Pos);
reheapify_downward(Pos);
}
private:
////////////////////////////////////////////////////////////////////////////////////
// Returns The Location Of Node (i)'s Parent Node (The Parent Node Of Zero Is Zero)
////////////////////////////////////////////////////////////////////////////////////
int parent(int i)
{
return ((i-1)/2);
}
////////////////////////////////////////////////////////////////////////////////////
// Returns The Location Of Node (i)'s Left Child (The Child Of A Leaf Is The Leaf)
////////////////////////////////////////////////////////////////////////////////////
int left(int i)
{
return (2*i)+1;
}
////////////////////////////////////////////////////////////////////////////////////
// Returns The Location Of Node (i)'s Right Child (The Child Of A Leaf Is The Leaf)
////////////////////////////////////////////////////////////////////////////////////
int right(int i)
{
return (2*i)+2;
}
////////////////////////////////////////////////////////////////////////////////////
// Returns The Location Of Largest Child Of Node (i)
////////////////////////////////////////////////////////////////////////////////////
int largest_child(int i)
{
if (left(i)=0 && b>=0 && aTEMP
mData[a] = mData[b]; // b->a
mData[b] = mData[MAXNODES]; // TEMP->B
assert(mHandleToPos[mData[a].handle()]==a);
assert(mHandleToPos[mData[b].handle()]==b);
}
////////////////////////////////////////////////////////////////////////////////////
// Swaps The Data Up The Heap Until It Reaches A Valid Location
////////////////////////////////////////////////////////////////////////////////////
void reheapify_upward(int Pos)
{
while (Pos && mData[parent(Pos)] mData; // The Memory (Plus One For Temp Sort)
ratl::array_vs mHandleToPos; //
int mPush; // Address Of Next Add Location
TNodes& mNodes;
};
////////////////////////////////////////////////////////////////////////////////////
// A Search Node
////////////////////////////////////////////////////////////////////////////////////
class search_node
{
public:
////////////////////////////////////////////////////////////////////////////////
// Constructors
////////////////////////////////////////////////////////////////////////////////
search_node(int Node=-1, int Parent=-1) :
mNode(Node),
mParentVisit(Parent),
mCostToGoal(-1),
mCostFromStart(0)
{}
search_node(const search_node& t) :
mNode(t.mNode),
mParentVisit(t.mParentVisit),
mCostToGoal(t.mCostToGoal),
mCostFromStart(t.mCostFromStart)
{}
////////////////////////////////////////////////////////////////////////////////
// Assignment Operator
////////////////////////////////////////////////////////////////////////////////
void operator=(const search_node& t)
{
mNode = (t.mNode);
mParentVisit = (t.mParentVisit);
mCostToGoal = (t.mCostToGoal);
mCostFromStart = (t.mCostFromStart);
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
int handle() const
{
return mNode;
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
float cost_estimate() const
{
return mCostFromStart+mCostToGoal;
}
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
bool operator< (const search_node& t) const
{
return (cost_estimate() > t.cost_estimate());
}
////////////////////////////////////////////////////////////////////////////////
// Data
////////////////////////////////////////////////////////////////////////////////
int mNode; // Which Node Is This (Index To Pool mNodes)
int mParentVisit; // Which Search Node (In Visited)
float mCostToGoal; // How Far From The Start Of The Search Are We?
float mCostFromStart; // How Far From The End Of The Search Are We?
};
public:
typedef ratl::vector_vs TVisited;
typedef ratl::array_vs TVisitedHandles;
typedef ratl::bits_vs TNodeState;
////////////////////////////////////////////////////////////////////////////////////
// The Search Data Object
////////////////////////////////////////////////////////////////////////////////////
class search
{
public:
////////////////////////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////////////////////////
search(int nodeStart=0, int nodeEnd=0) :
mStart(nodeStart),
mEnd(nodeEnd)
{
clear(true, false);
}
enum
{
NULL_NODE = 0,
NULL_NODE_INDEX = -1,
NULL_VISIT_INDEX = -1,
NULL_COST = -1,
};
////////////////////////////////////////////////////////////////////////////////
// Reset All The Search Parameters
////////////////////////////////////////////////////////////////////////////////
void clear(bool clearNodesPtr=true, bool clearStartAndEnd=true)
{
// Reset All Data
//----------------
mClosed.clear();
mVisited.clear();
mNodeIndexToVisited.fill(NULL_VISIT_INDEX);
mNext.mNode = NULL_NODE;
mNext.mParentVisit = NULL_VISIT_INDEX;
mNext.mCostToGoal = NULL_COST;
mNext.mCostFromStart= NULL_COST;
mPrevIndex = NULL_NODE_INDEX;
mNextIndex = NULL_NODE_INDEX;
mPathVisit = NULL_VISIT_INDEX;
if (clearNodesPtr)
{
mNodesPtr = 0;
}
// Clear Out The Start And End Handles
//-------------------------------------
if (clearStartAndEnd)
{
mStart = NULL_NODE;
mEnd = NULL_NODE;
}
// Otherwise, We Can Start The Next Index
//----------------------------------------
else if (mNodesPtr && mStart!=NULL_NODE && mEnd!=NULL_NODE)
{
mNextIndex = mStart;
}
}
////////////////////////////////////////////////////////////////////////////////
// Call This Function To Clear Out Everything EXCEPT Start And End Points
////////////////////////////////////////////////////////////////////////////////
void reset()
{
clear(false, false);
}
////////////////////////////////////////////////////////////////////////////////
// Mark This Node As Closed
////////////////////////////////////////////////////////////////////////////////
void close(int node)
{
mClosed.set_bit(node);
}
////////////////////////////////////////////////////////////////////////////////
// Mark This Node As Closed
////////////////////////////////////////////////////////////////////////////////
void close(const TNodeState& AllNodesToClose)
{
mClosed |= AllNodesToClose;
}
////////////////////////////////////////////////////////////////////////////////
// Return True If We Have Found The Node We Were Searching For
////////////////////////////////////////////////////////////////////////////////
bool success()
{
if (mEnd && mPrevIndex!=NULL_NODE_INDEX)
{
return (mPrevIndex==mEnd);
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Get An Index To The Beginning Of The Path
////////////////////////////////////////////////////////////////////////////////
void path_begin()
{
if (success())
{
mPathVisit = (mVisited.size()-1);
}
else
{
mPathVisit = NULL_VISIT_INDEX;
}
}
////////////////////////////////////////////////////////////////////////////////
// Check ForThe End Of The Path (Use In A for() Loop)
////////////////////////////////////////////////////////////////////////////////
bool path_end()
{
return (mPathVisit==NULL_VISIT_INDEX);
}
////////////////////////////////////////////////////////////////////////////////
// Index Path Inc (Get The Old Path Node's Parent)
////////////////////////////////////////////////////////////////////////////////
void path_inc()
{
assert(mPathVisit!=NULL_VISIT_INDEX);
mPathVisit = mVisited[mPathVisit].mParentVisit;
}
////////////////////////////////////////////////////////////////////////////////
// Get the node handle of the current node
////////////////////////////////////////////////////////////////////////////////
int path_at()
{
assert(mPathVisit!=NULL_VISIT_INDEX);
return mVisited[mPathVisit].mNode;
}
////////////////////////////////////////////////////////////////////////////////
// The Total Cost Of The Path
////////////////////////////////////////////////////////////////////////////////
float path_cost()
{
if (success())
{
return (mVisited[(mVisited.size()-1)].mCostFromStart);
}
return NULL_COST;
}
////////////////////////////////////////////////////////////////////////////////
// How Many Nodes Were Looked At In This Search
////////////////////////////////////////////////////////////////////////////////
int num_visited()
{
return mVisited.size();
}
////////////////////////////////////////////////////////////////////////////////
// Get An Iterator To The Beginning Of The Visited Pool
////////////////////////////////////////////////////////////////////////////////
typename TVisited::iterator visited_begin()
{
return mVisited.begin();
}
////////////////////////////////////////////////////////////////////////////////
// Get An Iterator To The End Of The Visited Pool
////////////////////////////////////////////////////////////////////////////////
typename TVisited::iterator visited_end()
{
return mVisited.end();
}
private:
////////////////////////////////////////////////////////////////////////////////
// Start At The First NON Closed Node
////////////////////////////////////////////////////////////////////////////////
void setup(TNodes* nodesPtr)
{
mNodesPtr = nodesPtr;
clear(false, false);
}
////////////////////////////////////////////////////////////////////////////////
// Pretend the next index is open, probably because we found a shorter route
// than the first time it was closed, and want it back on the open list
////////////////////////////////////////////////////////////////////////////////
void reopen_next_index()
{
assert(mNextIndex!=NULL_NODE_INDEX);
mNodeIndexToVisited[mNextIndex] = NULL_VISIT_INDEX;
mClosed.clear_bit(mNextIndex);
}
////////////////////////////////////////////////////////////////////////////////
// The current estimated cost of reaching a give node, if the node was never
// visited, but IS closed, it returns NULL cost
////////////////////////////////////////////////////////////////////////////////
float visited_cost(int NodeIndex)
{
int VisitedIndex = mNodeIndexToVisited[NodeIndex];
if (VisitedIndex!=NULL_VISIT_INDEX)
{
return mVisited[VisitedIndex].cost_estimate();
}
return NULL_COST;
}
////////////////////////////////////////////////////////////////////////////////
// Add This Search Node To The Visited List And Keep Track Of It For Later
////////////////////////////////////////////////////////////////////////////////
void visit(search_node& t)
{
assert(mNodesPtr!=0);
mPrevIndex = t.mNode;
// Add It To The Visited List, And Mark The Location In The Node Index Array
//---------------------------------------------------------------------------
mVisited.push_back(t);
mNodeIndexToVisited[mPrevIndex] = (mVisited.size()-1);
mClosed.set_bit(mPrevIndex);
// Setup Our Next Node To Know It Came From The Last Location In The Visited Vector
//----------------------------------------------------------------------------------
mNext.mParentVisit = (mVisited.size()-1);
}
////////////////////////////////////////////////////////////////////////////////
// Check To See If The Next Index Has Already Been Closed
////////////////////////////////////////////////////////////////////////////////
bool next_index_closed()
{
assert(mNextIndex!=NULL_NODE_INDEX);
return (mClosed.get_bit(mNextIndex));
}
////////////////////////////////////////////////////////////////////////////////
// The Simple "Get Next" Just Converts The Next Index To A Handle In mNext
////////////////////////////////////////////////////////////////////////////////
search_node& get_next()
{
assert(mNodesPtr);
assert(mNextIndex!=NULL_NODE_INDEX);
mClosed.set_bit(mNextIndex);
mNext.mNode = mNextIndex;
mNext.mCostToGoal = 0;
mNext.mCostFromStart = 0;
return mNext;
}
////////////////////////////////////////////////////////////////////////////////
// This "Get Next" Function Is For A* and Sets Up THe Costs Of The Search Node
////////////////////////////////////////////////////////////////////////////////
search_node& get_next(const user& suser, TEDGE& edge_parent_to_next)
{
assert(mNodesPtr);
assert(mNextIndex!=NULL_NODE_INDEX);
//NOTE: we do not do a "mClosed.set_bit(mNextIndex);" here because A* only closes nodes that are visited
mNext.mNode = mNextIndex;
mNext.mCostToGoal = suser.cost((*mNodesPtr)[mNext.mNode], (*mNodesPtr)[mEnd]);
mNext.mCostFromStart = suser.cost(edge_parent_to_next, (*mNodesPtr)[mNext.mNode]);
if (mNext.mParentVisit!=NULL_VISIT_INDEX)
{
mNext.mCostFromStart += mVisited[mNext.mParentVisit].mCostFromStart;
}
return mNext;
}
////////////////////////////////////////////////////////////////////////////////
// Parameters (Start And End Are External Handles)
////////////////////////////////////////////////////////////////////////////////
public:
int mStart;
int mEnd;
////////////////////////////////////////////////////////////////////////////////
// Data
////////////////////////////////////////////////////////////////////////////////
private:
TNodes* mNodesPtr;
int mPathVisit;
int mPrevIndex;
int mNextIndex;
search_node mNext;
ratl::bits_vs mClosed;
TVisited mVisited;
TVisitedHandles mNodeIndexToVisited;
friend class graph_vs;
};
////////////////////////////////////////////////////////////////////////////////////
// Setup The Search Data
////////////////////////////////////////////////////////////////////////////////////
void setup_search(search& sdata)
{
sdata.setup(&mNodes);
}
////////////////////////////////////////////////////////////////////////////////////
// A* Search
////////////////////////////////////////////////////////////////////////////////////
void astar(search& sdata, const user& suser)
{
// Make Sure The Nodes We Are Searching For Exist
//------------------------------------------------
assert(MAXEDGES>1);
sdata.setup(&mNodes);
// Allocate Our Data Structures
//------------------------------
handle_heap open(mNodes);
int curNeighbor;
int curEdge;
float curCost;
// Run Through The Open List
//---------------------------
open.push(sdata.get_next());
while (!open.empty() && !sdata.success())
{
sdata.visit(open.top());
open.pop();
// Search Through The Non Closed Nodes Edges
//-------------------------------------------
TNodeNeighbors& curNeighbors = get_node_neighbors(sdata.mPrevIndex);
for (curNeighbor=0; curNeighbor open;
// Run Through The Open List
//---------------------------
open.push(sdata.get_next());
while (open.size()>0 && !sdata.success())
{
sdata.visit(open.top());
open.pop();
// Search Through The Non Closed Nodes Edges
//-------------------------------------------
for (sdata.mNextIndex=0; sdata.mNextIndex open;
// Run Through The Open List
//---------------------------
open.push(sdata.get_next());
while (open.size()>0 && !sdata.success())
{
sdata.visit(open.top());
open.pop();
// Search Through The Non Closed Nodes Edges
//-------------------------------------------
for (sdata.mNextIndex=0; sdata.mNextIndex