|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Header: $
// $NoKeywords: $
//=============================================================================//
#ifndef UTLGRAPH_H
#define UTLGRAPH_H
#include "tier1/utlmap.h"
#include "tier1/utlvector.h"
#include <limits.h>
//-------------------------------------
//-----------------------------------------------------------------------------
// A Graph class
//
// Nodes must have a unique Node ID.
//
// Edges are unidirectional, specified from the beginning node.
//
//-----------------------------------------------------------------------------
template <class T, class C > class CUtlGraphVisitor;
template <class T, class C = int > class CUtlGraph { public: typedef int I; typedef I IndexType_t; typedef T NodeID_t; typedef C CostType_t;
typedef CUtlGraphVisitor<T,C> Visitor_t;
struct Edge_t { IndexType_t m_DestinationNode; CostType_t m_EdgeCost;
Edge_t( IndexType_t i = 0 ) { m_DestinationNode = i; m_EdgeCost = 0; }
bool operator==(const Edge_t &that ) const { return m_DestinationNode == that.m_DestinationNode; }
static int SortFn( const Edge_t *plhs, const Edge_t *prhs ) { if ( plhs->m_EdgeCost < prhs->m_EdgeCost ) return -1; else if ( plhs->m_EdgeCost > prhs->m_EdgeCost ) return 1; else return 0; } };
typedef CUtlVector<Edge_t> vecEdges_t;
// constructor, destructor
CUtlGraph( ); ~CUtlGraph();
// Add an edge
bool AddEdge( T SourceNode, T DestNode, C nCost );
// Remove an edge
bool RemoveEdge( T SourceNode, T DestNode ); // gets particular elements
T& Element( I i ); T const &Element( I i ) const; T& operator[]( I i ); T const &operator[]( I i ) const;
// Find a node
I Find( T Node ) { return m_Nodes.Find( Node ); } I Find( T Node ) const { return m_Nodes.Find( Node ); }
// Num elements
unsigned int Count() const { return m_Nodes.Count() ; } // Max "size" of the vector
I MaxElement() const { return m_Nodes.MaxElement(); } // Checks if a node is valid and in the graph
bool IsValidIndex( I i ) const { return m_Nodes.IsValidIndex( i ); } // Checks if the graph as a whole is valid
bool IsValid() const { return m_Nodes.IsValid(); } // Invalid index
static I InvalidIndex() { return CUtlMap< NodeID_t, vecEdges_t*>::InvalidIndex(); } // Remove methods
void RemoveAt( I i ); void RemoveAll();
// Makes sure we have enough memory allocated to store a requested # of elements
void EnsureCapacity( int num );
// Create Path Matrix once you've added all nodes and edges
void CreatePathMatrix();
// For Visitor classes
vecEdges_t *GetEdges( I i );
// shortest path costs
vecEdges_t *GetPathCosts( I i );
#ifdef DBGFLAG_VALIDATE
void Validate( CValidator &validator, const char *pchName ); #endif // DBGFLAG_VALIDATE
protected:
struct Node_t { vecEdges_t *m_pvecEdges; vecEdges_t *m_pvecPaths;
Node_t() { m_pvecEdges = NULL; m_pvecPaths = NULL; } };
CUtlMap< NodeID_t, Node_t > m_Nodes; };
//-----------------------------------------------------------------------------
// A Graph "visitor" class
//
// From the specified beginning point, visits each node in an expanding radius
//
//-----------------------------------------------------------------------------
template <class T, class C = int > class CUtlGraphVisitor { public: CUtlGraphVisitor( CUtlGraph<T, C> &graph );
bool Begin( T StartNode ); bool Advance();
T CurrentNode(); C AccumulatedCost(); int CurrentRadius(); private:
typedef typename CUtlGraph<T,C>::IndexType_t IndexType_t; typedef typename CUtlGraph<T,C>::Edge_t Edge_t; typedef CUtlVector<Edge_t> vecEdges_t; CUtlGraph<T, C> &m_Graph;
vecEdges_t m_vecVisitQueue; int m_iVisiting; int m_nCurrentRadius;
vecEdges_t m_vecFringeQueue;
CUtlVector<T> m_vecNodesVisited; };
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
template <class T, class C > inline CUtlGraph<T, C>::CUtlGraph() { SetDefLessFunc( m_Nodes ); }
template <class T, class C > inline CUtlGraph<T, C>::~CUtlGraph() { RemoveAll(); }
//-----------------------------------------------------------------------------
// gets particular elements
//-----------------------------------------------------------------------------
template <class T, class C > inline T &CUtlGraph<T, C>::Element( I i ) { return m_Nodes.Key( i ); }
template <class T, class C > inline T const &CUtlGraph<T, C>::Element( I i ) const { return m_Nodes.Key( i ); }
template <class T, class C > inline T &CUtlGraph<T, C>::operator[]( I i ) { return Element(i); }
template <class T, class C > inline T const &CUtlGraph<T, C>::operator[]( I i ) const { return Element(i); }
//-----------------------------------------------------------------------------
//
// various accessors
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Removes all nodes from the tree
//-----------------------------------------------------------------------------
template <class T, class C > void CUtlGraph<T, C>::RemoveAll() { FOR_EACH_MAP_FAST( m_Nodes, iNode ) { delete m_Nodes[iNode].m_pvecEdges; delete m_Nodes[iNode].m_pvecPaths; }
m_Nodes.RemoveAll(); }
//-----------------------------------------------------------------------------
// Makes sure we have enough memory allocated to store a requested # of elements
//-----------------------------------------------------------------------------
template <class T, class C > void CUtlGraph<T, C>::EnsureCapacity( int num ) { m_Nodes.EnsureCapacity(num); }
//-----------------------------------------------------------------------------
// Add an edge
//-----------------------------------------------------------------------------
template <class T, class C > bool CUtlGraph<T, C>::AddEdge( T SourceNode, T DestNode, C nCost ) { I iSrcNode = m_Nodes.Find( SourceNode ); if ( !m_Nodes.IsValidIndex( iSrcNode ) ) { Node_t Node; Node.m_pvecEdges = new vecEdges_t(); Node.m_pvecPaths = new vecEdges_t(); iSrcNode = m_Nodes.Insert( SourceNode, Node ); }
I iDstNode = m_Nodes.Find( DestNode ); if ( !m_Nodes.IsValidIndex( iDstNode ) ) { Node_t Node; Node.m_pvecEdges = new vecEdges_t(); Node.m_pvecPaths = new vecEdges_t(); iDstNode = m_Nodes.Insert( DestNode, Node ); }
vecEdges_t &vecEdges = *m_Nodes[iSrcNode].m_pvecEdges;
#ifdef _DEBUG
FOR_EACH_VEC( vecEdges, iEdge ) { if ( vecEdges[iEdge].m_DestinationNode == iDstNode ) return false; } #endif
Edge_t newEdge; newEdge.m_DestinationNode = iDstNode; newEdge.m_EdgeCost = nCost;
vecEdges[ vecEdges.AddToTail() ] = newEdge;
return true; }
//-----------------------------------------------------------------------------
// Remove an edge
//-----------------------------------------------------------------------------
template <class T, class C > bool CUtlGraph<T, C>::RemoveEdge( T SourceNode, T DestNode ) { I iSrcNode = m_Nodes.Find( SourceNode ); if ( !m_Nodes.IsValidIndex( iSrcNode ) ) return false;
I iDstNode = m_Nodes.Find( DestNode ); if ( !m_Nodes.IsValidIndex( iDstNode ) ) return false;
vecEdges_t &vecEdges = *m_Nodes[iSrcNode].m_pvecEdges;
FOR_EACH_VEC( vecEdges, iEdge ) { if ( vecEdges[iEdge].m_DestinationNode == iDstNode ) { // could use FastRemove, but nodes won't have that
// many edges, and the elements are small, and
// preserving the original ordering is nice
vecEdges.Remove( iEdge ); return true; } }
return false; }
//-----------------------------------------------------------------------------
// Get all of a Node's edges
//-----------------------------------------------------------------------------
template <class T, class C > typename CUtlGraph<T, C>::vecEdges_t *CUtlGraph<T, C>::GetEdges( I i ) { return m_Nodes[i].m_pvecEdges; }
//-----------------------------------------------------------------------------
// Get all of a Node's edges
//-----------------------------------------------------------------------------
template <class T, class C > typename CUtlGraph<T, C>::vecEdges_t *CUtlGraph<T, C>::GetPathCosts( I i ) { return m_Nodes[i].m_pvecPaths; }
//-----------------------------------------------------------------------------
// Data and memory validation
//-----------------------------------------------------------------------------
#ifdef DBGFLAG_VALIDATE
template <class T, class C > void CUtlGraph<T, C>::Validate( CValidator &validator, const char *pchName ) { #ifdef _WIN32
validator.Push( typeid(*this).raw_name(), this, pchName ); #else
validator.Push( typeid(*this).name(), this, pchName ); #endif
ValidateObj( m_Nodes );
FOR_EACH_MAP_FAST( m_Nodes, iNode ) { validator.ClaimMemory( m_Nodes[iNode].m_pvecEdges ); ValidateObj( *m_Nodes[iNode].m_pvecEdges ); validator.ClaimMemory( m_Nodes[iNode].m_pvecPaths ); ValidateObj( *m_Nodes[iNode].m_pvecPaths ); }
validator.Pop(); } #endif // DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Get all of a Node's edges
//-----------------------------------------------------------------------------
template <class T, class C > void CUtlGraph<T, C>::CreatePathMatrix() { int n = MaxElement();
// Notes
// Because CUtlMap stores its nodes in essentially a vector,
// we know that we can use its indices in our own path matrix
// vectors safely (they will be numbers in the range (0,N) where
// N is largest number of nodes ever present in the graph).
//
// This lets us very quickly access previous best-path estimates
// by indexing into a node's vecPaths directly.
//
// When we are all done, we can then compact the vector, removing
// "null" paths, and then sorting by cost.
// Initialize matrix with all edges
FOR_EACH_MAP_FAST( m_Nodes, iNode ) { vecEdges_t &vecEdges = *m_Nodes.Element( iNode ).m_pvecEdges; vecEdges_t &vecPaths = *m_Nodes.Element( iNode ).m_pvecPaths; vecPaths.RemoveAll(); vecPaths.AddMultipleToTail( n ); FOR_EACH_VEC( vecPaths, iPath ) { vecPaths[iPath].m_DestinationNode = InvalidIndex(); }
// Path to self
vecPaths[iNode].m_DestinationNode = iNode; // zero cost to self
vecPaths[iNode].m_EdgeCost = 0;
FOR_EACH_VEC( vecEdges, iEdge ) { // Path to a neighbor node - we know exactly what the cost is
Edge_t &edge = vecEdges[iEdge]; vecPaths[ edge.m_DestinationNode ].m_DestinationNode = edge.m_DestinationNode; vecPaths[ edge.m_DestinationNode ].m_EdgeCost = edge.m_EdgeCost; } }
// Floyd-Warshall
// for k:= 0 to n-1
// for each (i,j) in (0..n-1)
// path[i][j] = min( path[i][j], path[i][k]+path[k][j] );
for ( int k = 0; k < n; ++ k ) { if ( !m_Nodes.IsValidIndex( k ) ) continue;
// All current known paths from K
vecEdges_t &destMapFromK = *m_Nodes[k].m_pvecPaths;
for ( int i = 0; i < n; ++i ) { if ( !m_Nodes.IsValidIndex( i ) ) continue;
// All current known paths from J
vecEdges_t &destMapFromI = *m_Nodes[i].m_pvecPaths;
// Path from I to K?
int iFromIToK = k; bool bFromIToK = destMapFromI[iFromIToK].m_DestinationNode != InvalidIndex(); CostType_t cIToK = ( bFromIToK ) ? destMapFromI[iFromIToK].m_EdgeCost : INT_MAX;
for ( int j = 0; j < n; ++ j ) { if ( !m_Nodes.IsValidIndex( j ) ) continue;
// Path from I to J already?
int iFromIToJ = j; bool bFromIToJ = destMapFromI[iFromIToJ].m_DestinationNode != InvalidIndex(); CostType_t cIToJ = ( bFromIToJ ) ? destMapFromI[iFromIToJ].m_EdgeCost : INT_MAX;
// Path from K to J?
int iFromKToJ = j; bool bFromKToJ = destMapFromK[iFromKToJ].m_DestinationNode != InvalidIndex(); CostType_t cKToJ = ( bFromKToJ ) ? destMapFromK[iFromKToJ].m_EdgeCost : INT_MAX; // Is the new path valid?
bool bNewPathFound = ( bFromIToK && bFromKToJ );
if ( bNewPathFound ) { if ( bFromIToJ ) { // Pick min of previous best and current path
destMapFromI[iFromIToJ].m_EdgeCost = min( cIToJ, cIToK + cKToJ ); } else { // Current path is the first, hence the best so far
destMapFromI[iFromIToJ].m_DestinationNode = iFromIToJ; destMapFromI[iFromIToJ].m_EdgeCost = cIToK + cKToJ; } } } } }
// Clean up and sort the paths
FOR_EACH_MAP_FAST( m_Nodes, iNode ) { vecEdges_t &vecPaths = *m_Nodes.Element( iNode ).m_pvecPaths; FOR_EACH_VEC( vecPaths, iPath ) { Edge_t &edge = vecPaths[iPath]; if ( edge.m_DestinationNode == InvalidIndex() ) { // No path to this destination was found.
// Remove this entry from the vector.
vecPaths.FastRemove( iPath ); --iPath; // adjust for the removal
} }
// Sort the vector by cost, given that it
// is likely consumers will want to
// iterate destinations in that order.
vecPaths.Sort( Edge_t::SortFn ); } }
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
template <class T, class C > CUtlGraphVisitor<T, C>::CUtlGraphVisitor( CUtlGraph<T,C> &graph ) : m_Graph( graph ) { m_iVisiting = 0; m_nCurrentRadius = 0; }
//-----------------------------------------------------------------------------
// Begin visiting the nodes in the graph. Returns false if the start node
// does not exist
//-----------------------------------------------------------------------------
template <class T, class C > bool CUtlGraphVisitor<T, C>::Begin( T StartNode ) { m_vecVisitQueue.RemoveAll(); m_vecFringeQueue.RemoveAll(); m_vecNodesVisited.RemoveAll(); m_iVisiting = 0; m_nCurrentRadius = 0;
IndexType_t iStartNode = m_Graph.Find( StartNode );
if ( !m_Graph.IsValidIndex( iStartNode ) ) return false;
vecEdges_t *pvecEdges = m_Graph.GetEdges( iStartNode );
Edge_t edge; edge.m_DestinationNode = iStartNode; edge.m_EdgeCost = 0;
m_vecVisitQueue[ m_vecVisitQueue.AddToTail() ] = edge;
m_vecNodesVisited[ m_vecNodesVisited.AddToTail() ] = iStartNode;
m_vecFringeQueue = *pvecEdges;
// cells actually get marked as "visited" as soon as we put
// them in the fringe queue, so we don't put them in the *next*
// fringe queue (we build the fringe queue before we actually visit
// the nodes in the new visit queue).
FOR_EACH_VEC( m_vecFringeQueue, iFringe ) { m_vecNodesVisited[ m_vecNodesVisited.AddToTail() ] = m_vecFringeQueue[iFringe].m_DestinationNode; }
return true; }
//-----------------------------------------------------------------------------
// Advance to the next node. Returns false when all nodes have been visited
//-----------------------------------------------------------------------------
template <class T, class C> bool CUtlGraphVisitor<T, C>::Advance() { m_iVisiting++;
// Is the VisitQueue empty? move outward one radius if so
if ( m_iVisiting >= m_vecVisitQueue.Count() ) { m_nCurrentRadius++; m_iVisiting = 0; m_vecVisitQueue = m_vecFringeQueue; m_vecFringeQueue.RemoveAll();
if ( !m_vecVisitQueue.Count() ) return false;
// create new fringe queue
FOR_EACH_VEC( m_vecVisitQueue, iNode ) { Edge_t &node = m_vecVisitQueue[iNode]; vecEdges_t &vecEdges = *m_Graph.GetEdges( node.m_DestinationNode ); FOR_EACH_VEC( vecEdges, iEdge ) { Edge_t &edge = vecEdges[iEdge]; if ( m_vecNodesVisited.InvalidIndex() == m_vecNodesVisited.Find( edge.m_DestinationNode ) ) { m_vecNodesVisited[ m_vecNodesVisited.AddToTail() ] = edge.m_DestinationNode;
int iNewFringeNode = m_vecFringeQueue.AddToTail(); m_vecFringeQueue[ iNewFringeNode ] = edge; // Accumulate the cost to get to the current point
m_vecFringeQueue[ iNewFringeNode ].m_EdgeCost += node.m_EdgeCost; } } } }
return true; }
//-----------------------------------------------------------------------------
// Get the current node in the visit sequence
//-----------------------------------------------------------------------------
template <class T, class C> T CUtlGraphVisitor<T, C>::CurrentNode() { if ( m_iVisiting >= m_vecVisitQueue.Count() ) { AssertMsg( false, "Visitor invalid" ); return T(); }
return m_Graph[ m_vecVisitQueue[ m_iVisiting ].m_DestinationNode ]; }
//-----------------------------------------------------------------------------
// Get the accumulated cost to traverse the graph to the current node
//-----------------------------------------------------------------------------
template <class T, class C> C CUtlGraphVisitor<T, C>::AccumulatedCost() { if ( m_iVisiting >= m_vecVisitQueue.Count() ) { AssertMsg( false, "Visitor invalid" ); return C(); }
return m_vecVisitQueue[ m_iVisiting ].m_EdgeCost; }
//-----------------------------------------------------------------------------
// Get the current radius from the start point to this node
//-----------------------------------------------------------------------------
template <class T, class C> int CUtlGraphVisitor<T, C>::CurrentRadius() { if ( m_iVisiting >= m_vecVisitQueue.Count() ) { AssertMsg( false, "Visitor invalid" ); return 0; }
return m_nCurrentRadius; }
#endif // UTLGRAPH_H
|