Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

659 lines
17 KiB

5 years ago
//========= Copyright <EFBFBD> 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