jedi-academy/code/Ragl/graph_region.h
2013-04-23 15:21:39 +10:00

419 lines
No EOL
13 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
// RAVEN STANDARD TEMPLATE LIBRARY
// (c) 2002 Activision
//
//
// Graph Region
// ------------
//
//
////////////////////////////////////////////////////////////////////////////////////////
#if !defined(RATL_GRAPH_REGION_INC)
#define RATL_GRAPH_REGION_INC
////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////
#if defined(RA_DEBUG_LINKING)
#pragma message("...including graph_region.h")
#endif
#if !defined(RAGL_COMMON_INC)
#include "ragl_common.h"
#endif
#if !defined(RAGL_GRAPH_VS_INC)
#include "graph_vs.h"
#endif
namespace ragl
{
////////////////////////////////////////////////////////////////////////////////////////
// The Graph Region Class
////////////////////////////////////////////////////////////////////////////////////////
template <class TNODE, int MAXNODES, class TEDGE, int MAXEDGES, int NUM_EDGES_PER_NODE, int MAXREGIONS, int MAXREGIONEDGES>
class graph_region : public ratl::ratl_base
{
public:
////////////////////////////////////////////////////////////////////////////////////
// Capacity Enum
////////////////////////////////////////////////////////////////////////////////////
enum
{
NULL_REGION = -1,
NULL_EDGE = -1,
CAPACITY = MAXREGIONS
};
////////////////////////////////////////////////////////////////////////////////////
// Some Public Type Defines
////////////////////////////////////////////////////////////////////////////////////
typedef ragl::graph_vs<TNODE, MAXNODES, TEDGE, MAXEDGES, NUM_EDGES_PER_NODE> TGraph;
typedef ratl::vector_vs<int, MAXNODES> TRegions;
typedef ratl::vector_vs<short, MAXREGIONS> TRegionEdge; // List Of All Edges Which Connect RegionA<->RegionB
typedef ratl::pool_vs<TRegionEdge, MAXREGIONEDGES> TEdges; // Pool Of All RegionEdges
typedef ratl::grid2_vs<short, MAXREGIONS, MAXREGIONS> TLinks; // Graph Of Links From Region To Region, Each Points To A RegionEdge
typedef ratl::bits_vs<MAXREGIONS> TClosed;
////////////////////////////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////////////////////////////
graph_region(TGraph& Graph) : mGraph(Graph)
{
clear();
}
~graph_region()
{
}
////////////////////////////////////////////////////////////////////////////////////
// Clear Out All Temp Data So We Can Recalculate Regions
////////////////////////////////////////////////////////////////////////////////////
void clear()
{
mRegions.resize(0, (int)NULL_REGION);
mRegions.resize(MAXNODES, (int)NULL_REGION);
mRegionCount = 0;
mReservedRegionCount = 0;
mLinks.init(NULL_EDGE);
for (int i=0; i<MAXREGIONEDGES; i++)
{
if (mEdges.is_used(i))
{
mEdges[i].resize(0, NULL_EDGE);
}
}
mEdges.clear();
}
////////////////////////////////////////////////////////////////////////////////////
// How Many Regions Have Been Created
////////////////////////////////////////////////////////////////////////////////////
int size()
{
return mRegionCount;
}
////////////////////////////////////////////////////////////////////////////////////
// Get The Region For A Given Node
////////////////////////////////////////////////////////////////////////////////////
int get_node_region(int Node)
{
return mRegions[mGraph.node_index(Node)];
}
////////////////////////////////////////////////////////////////////////////////////
// Call this function to find out if it is at all possible to get from nodeA to
// nodeB. If there is no possible connection, or there is one, but the connection
// is not valid at the current time, this routine will return false. Use it as
// a quick cull routine before a search.
//
// In order to use this function, you must have an EdgeQuery class (use the default
// above, or derive your own for more specialized behavior).
////////////////////////////////////////////////////////////////////////////////////
bool has_valid_edge(int NodeA, int NodeB, const typename TGraph::user& user)
{
int RegionA = mRegions[NodeA];
int RegionB = mRegions[NodeB];
if (RegionA==RegionB)
{
return true;
}
mClosed.clear();
return has_valid_region_edge(RegionA, RegionB, user);
}
////////////////////////////////////////////////////////////////////////////////////
// Reserve Region
//
// Allows a user to pre-allocate a special region for a group of points
////////////////////////////////////////////////////////////////////////////////////
int reserve()
{
assert(mRegionCount < (MAXREGIONS-1));
if (mRegionCount >= (MAXREGIONS-1) )
{//stop adding points, we're full, you MUST increase MAXREGIONS for this to work
return NULL_REGION;
}
mReservedRegionCount ++;
mRegionCount ++;
return (mRegionCount);
}
////////////////////////////////////////////////////////////////////////////////////
// assign_region
//
// Allows a user to pre-allocate a special region for a group of points
////////////////////////////////////////////////////////////////////////////////////
void assign_region(int NodeIndex, int RegionIndex)
{
mRegions[NodeIndex] = RegionIndex;
}
////////////////////////////////////////////////////////////////////////////////////
// Define Regions
//
// Scan through all the nodes (calling the depth first recursive traversal below),
// and mark regions of nodes which can traverse to one another without needing to check
// for valid edges.
//
////////////////////////////////////////////////////////////////////////////////////
bool find_regions(const typename TGraph::user& user)
{
int CurNodeIndex;
for (TGraph::TNodes::iterator i=mGraph.nodes_begin(); i!=mGraph.nodes_end(); i++)
{
CurNodeIndex = i.index();
if (mRegions[CurNodeIndex] == NULL_REGION)
{
assert(mRegionCount < (MAXREGIONS-1));
if (mRegionCount >= (MAXREGIONS-1) )
{//stop adding points, we're full, you MUST increase MAXREGIONS for this to work
return false;
}
mRegionCount ++; // Allocate The New Region
assign(CurNodeIndex, user); // Assign All Points To It
}
}
mRegionCount ++; // Size is actually 1 greater than the number of regions
return true;
}
////////////////////////////////////////////////////////////////////////////////////
// Search For All Possible Edges Which Connect Regions
//
// Once called, this class will have reference data for how to get from one region
// to another.
////////////////////////////////////////////////////////////////////////////////////
bool find_region_edges()
{
int RegionA;
int RegionB;
int RegionLink;
bool Success = true;
bool ReservedRegionLink;
for (int indexA=0; indexA<MAXNODES; indexA++)
{
RegionA = mRegions[indexA];
if (RegionA!=NULL_REGION)
{
for (int indexB=0; indexB<MAXNODES; indexB++)
{
RegionB = mRegions[indexB];
ReservedRegionLink = (RegionA<=mReservedRegionCount || RegionB<=mReservedRegionCount);
if (RegionB!=NULL_REGION && RegionB!=RegionA && mGraph.get_edge_across(indexA, indexB))
{
RegionLink = mLinks.get(RegionA, RegionB);
// Do We Need To Allocate A New Region Link Vector?
//--------------------------------------------------
if (RegionLink==-1)
{
if (ReservedRegionLink)
{
mLinks.get(RegionA, RegionB) = -2; // Special Flag For Reserved Regions - they have no edges
mLinks.get(RegionB, RegionA) = -2;
}
else
{
if (mEdges.full())
{
assert("graph_region: Too Many Region Edges"==0);
Success = false;
}
else
{
RegionLink = mEdges.alloc();
mEdges[RegionLink].resize(0, NULL_EDGE);
mEdges[RegionLink].push_back(mGraph.get_edge_across(indexA, indexB));
mLinks.get(RegionA, RegionB) = RegionLink;
mLinks.get(RegionB, RegionA) = RegionLink;
}
}
}
// Add This Edge To The Other Region Links
//-----------------------------------------
else if (!ReservedRegionLink)
{
mEdges[RegionLink].push_back(mGraph.get_edge_across(indexA, indexB));
}
}
}
}
}
return Success;
}
private:
////////////////////////////////////////////////////////////////////////////////////
// This Routine Is A Depth First Recursive Traversal
//
// It will visit all neighbors for each node which have not already been visited
// and assigned to a region. Neighbors must always be valid.
////////////////////////////////////////////////////////////////////////////////////
void assign(int Node, const typename TGraph::user& user)
{
mRegions[Node] = mRegionCount;
for (int i=0; i<MAXNODES; i++)
{
if (mRegions[i]==-1)
{
int edgeNum = mGraph.get_edge_across(Node, i);
if (edgeNum && !user.can_be_invalid(mGraph.get_edge(edgeNum)))
{
assign(i, user);
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////
// This Routine Is A Depth First Recursive Search For Target Region
//
// Visited regions are makred on the "closed" bit field.
////////////////////////////////////////////////////////////////////////////////////
bool has_valid_region_edge(int CurRegion, int TargetRegion, const typename TGraph::user& user)
{
// Mark The Cur Region As Visited, So We Don't Try To Return To It
//-----------------------------------------------------------------
mClosed.set_bit(CurRegion);
// If The Two Nodes Are In The Same Region, Then This Is Valid
//-------------------------------------------------------------
if (CurRegion==TargetRegion)
{
return true;
}
// Scan Through The Cur Region's Neighbors With Currently Valid Region Edges
//---------------------------------------------------------------------------
int CurRegionEdge;
for (int NextRegion=0; NextRegion<mRegionCount; NextRegion++)
{
// Check If The Link Exists And We Have Not Already Visited The Next Region
//--------------------------------------------------------------------------
CurRegionEdge = mLinks.get(CurRegion, NextRegion);
if (CurRegionEdge!=NULL_EDGE && !mClosed.get_bit(NextRegion))
{
if (CurRegion<=mReservedRegionCount)
{
// Great, So We Have Found A Valid Neighboring Region, Search There
//------------------------------------------------------------------
if (has_valid_region_edge(NextRegion, TargetRegion, user))
{
return true; // HEY! Somehow, Going To Next Region Got Us To The Target Region!
}
}
else
{
// Scan Through This Region Edge List Of Graph Edges For Any Valid One
//---------------------------------------------------------------------
assert(mEdges[CurRegionEdge].size()>0);
for (int j=0; j<mEdges[CurRegionEdge].size(); j++)
{
if (user.is_valid(
mGraph.get_edge(mEdges[CurRegionEdge][j]),
(NextRegion==TargetRegion)?(-1):(0)
)
)
{
// Great, So We Have Found A Valid Neighboring Region, Search There
//------------------------------------------------------------------
if (has_valid_region_edge(NextRegion, TargetRegion, user))
{
return true; // HEY! Somehow, Going To Next Region Got Us To The Target Region!
}
// Ok, The Target Region Turned Out To Be A Dead End, We Can Stop Trying To Get There
//------------------------------------------------------------------------------------
break;
}
}
}
}
}
// Nope, We Failed To Find Any Valid Region Edges Which Lead To The Target Region
//--------------------------------------------------------------------------------
return false;
}
private:
////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////
TGraph& mGraph;
TRegions mRegions;
int mRegionCount;
int mReservedRegionCount;
TLinks mLinks;
TEdges mEdges;
TClosed mClosed;
public:
#if !defined(FINAL_BUILD)
void ProfileSpew()
{
ProfilePrint("");
ProfilePrint("");
ProfilePrint("--------------------------------------------------------");
ProfilePrint("RAVEN STANDARD LIBRARY - COMPUTATIONAL GEOMETRY MODULE");
ProfilePrint(" Region Profile Results ");
ProfilePrint("--------------------------------------------------------");
ProfilePrint("");
ProfilePrint("REGION SIZE (Bytes): (%d) (KiloBytes): (%5.3f)", sizeof(*this), ((float)(sizeof(*this))/1024.0f));
ProfilePrint("REGION COUNT: (%d) Regions (%d) Edges", mRegionCount, mEdges.size());
if (mRegionCount)
{
int RegionEdges = 0;
for (TEdges::iterator it=mEdges.begin(); it!=mEdges.end(); it++)
{
RegionEdges += (*it).size();
}
ProfilePrint("REGION COUNT: (%f) Ave Edges Size", (float)RegionEdges / (float)mRegionCount);
}
ProfilePrint("");
};
#endif
};
}
#endif