Initial commit.
This commit is contained in:
419
code/ragl/graph_region.h
Normal file
419
code/ragl/graph_region.h
Normal file
@@ -0,0 +1,419 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
833
code/ragl/graph_triangulate.h
Normal file
833
code/ragl/graph_triangulate.h
Normal file
@@ -0,0 +1,833 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RAVEN STANDARD TEMPLATE LIBRARY
|
||||
// (c) 2002 Activision
|
||||
//
|
||||
//
|
||||
// Graph Triangulate
|
||||
// -----------------
|
||||
// Triangulation is the process of generating graph edges between "nearby" points.
|
||||
//
|
||||
// This class is designed to work with the ragl_graph template class, and requires that
|
||||
// the same template parameters for that class be used here. The memory requirements
|
||||
// of this class are not inconsequential, so it is best to allocate this class during
|
||||
// a preprocess step and then throw it away.
|
||||
//
|
||||
// NOTE: This is a 2D triangulation! All Z Coordinates are ignored!
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// How Do I Triangulate A Raw Set Of Points?
|
||||
// -----------------------------------------
|
||||
// First of all, in order to construct a triangulation, you need to have your graph and
|
||||
// pass it in to the constructor:
|
||||
//
|
||||
// typedef ragl::graph_triangulate<TNODE, MAXNODES, TEDGE, MAXEDGES> TTriangulation
|
||||
// TTriangulation MyTriangulation(mMyGraph);
|
||||
//
|
||||
// Next, you are free to call any of the public functions in any order, but the best use
|
||||
// is to call them in this order:
|
||||
//
|
||||
// MyTriangulation.insertion_hull();
|
||||
// MyTriangulation.delaunay_edge_flip();
|
||||
// MyTriangulation.alpha_shape(MyGraphUser, <MIN>, <MAX>);
|
||||
//
|
||||
// For documentation on the above functions, look at their def below. Also, the doc on
|
||||
// the Graph User class is in graph_vs.h
|
||||
//
|
||||
//
|
||||
// Finally, when you are ready, call the finish() function. That will populate your
|
||||
// graph (which has not been altered in any way up until now). After calling finish()
|
||||
// you can dump the triangulation class, as it has done it's job and all the data is
|
||||
// now stored in the class.
|
||||
//
|
||||
// MyTriangulation.finish();
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// How Does It Work? (Overview)
|
||||
// -----------------------------
|
||||
// The details of how each step works are outlined below, however, here is the general
|
||||
// idea:
|
||||
//
|
||||
// - Call insertion hull to generate a "rough and dirty" triangulation of the point set.
|
||||
// The algorithm is relativly fast, and as a handy bi-product, generates the convex
|
||||
// hull of the points. The resulting mesh is ugly though. You probably won't want
|
||||
// to use it in the rough state. The basic idea of this algorithm is to iterativly
|
||||
// add points which have been presorted along the x-axis into the triangulation. It
|
||||
// is easy to do so, because you always know it will be on the right side of any edge
|
||||
// it needs to connect with.
|
||||
//
|
||||
// - Now that you have a functional triangulation with edges and faces, there is fairly
|
||||
// simple and fast algorithm to "clean it up" called EdgeFlipping. The idea is simple.
|
||||
// Just scan through the edges, if you find one that is "bad", flip it! Continue until
|
||||
// you find no "bad" edges. NOTE: This algorithm can lock up if any four points are
|
||||
// colinear!
|
||||
//
|
||||
// - Finally, Alpha Shape is just a simple prune scan of the edges for anything that is
|
||||
// too big or too small. This step is totally optional.
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(RATL_GRAPH_TRIANGULATE_INC)
|
||||
#define RATL_GRAPH_TRIANGULATE_INC
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(RA_DEBUG_LINKING)
|
||||
#pragma message("...including graph_triangulate.h")
|
||||
#endif
|
||||
#if !defined(RAGL_COMMON_INC)
|
||||
#include "ragl_common.h"
|
||||
#endif
|
||||
#if !defined(RAGL_GRAPH_VS_INC)
|
||||
#include "graph_vs.h"
|
||||
#endif
|
||||
#if !defined(RATL_LIST_VS_INC)
|
||||
#include "..\Ratl\list_vs.h"
|
||||
#endif
|
||||
namespace ragl
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Graph Class
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <class TNODE, int MAXNODES, class TEDGE, int MAXEDGES, int MAXNODENEIGHBORS>
|
||||
class graph_triangulate : public ratl::ratl_base
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Capacity Enum
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
enum
|
||||
{
|
||||
CAPACITY = MAXNODES,
|
||||
MAXFACES = MAXEDGES*2,
|
||||
NULLEDGE = -1
|
||||
};
|
||||
|
||||
typedef graph_vs<TNODE, MAXNODES, TEDGE, MAXEDGES, MAXNODENEIGHBORS> TGraph;
|
||||
|
||||
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
graph_triangulate(TGraph& Graph) : mGraph(Graph), mHull(), mHullIter(mHull.begin())
|
||||
{
|
||||
mLinks.init(0);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Clear Out All Temp Data So We Can Triangulate Again
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void clear()
|
||||
{
|
||||
mLinks.init(0);
|
||||
mEdges.clear();
|
||||
mFaces.clear();
|
||||
|
||||
mHull.clear();
|
||||
mHullIter = mHull.begin();
|
||||
|
||||
mSortNodes.clear();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Insertion Hull
|
||||
//
|
||||
// This is a "quick and dirty" triangulation technique. It does not give you a very
|
||||
// nice looking or terribly useful mesh, but it is a good place to start. Once
|
||||
// you have an insertion hull triangulation, you can call delauny_edge_flip() to
|
||||
// clean it up some.
|
||||
//
|
||||
// This algorithm's complexity isbounded in the worst case where all the points in
|
||||
// the mesh are on the "hull", in which case it is O(n^2). However the number of
|
||||
// points on the hull for most common point clouds is more likely to be log n.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void insertion_hull()
|
||||
{
|
||||
assert(mGraph.size_nodes()>3); // We Need More Than 3 Points To Triangulate
|
||||
|
||||
// STEP ONE: Sort all points along the x axis in increasing order
|
||||
//----------------------------------------------------------------
|
||||
// COMPLEXITY: O(n log n) Heapsort
|
||||
|
||||
sort_points();
|
||||
|
||||
|
||||
|
||||
// STEP TWO: Manually constructe the first face of the triangulation out of the 3 rightmost points
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// COMPLEXITY: O(1)
|
||||
|
||||
add_face(mSortNodes[0].mNodeHandle, mSortNodes[1].mNodeHandle, mSortNodes[2].mNodeHandle);
|
||||
|
||||
|
||||
|
||||
// STEP THREE: Add each remaining point to the hull, constructing new faces as we go
|
||||
//-----------------------------------------------------------------------------------
|
||||
// COMPLEXITY: O(n*c) (n = num nodes, c = num nodes on hull)
|
||||
|
||||
for (int i=3; i<mSortNodes.size(); i++)
|
||||
{
|
||||
insert_point(mSortNodes[i].mNodeHandle);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Delaunay Edge Flipping
|
||||
//
|
||||
// This algorithm iterativly rotates edges which do not fit the "delaunay" criterion
|
||||
// of all points on two adjacent faces containment within the circumscribed circles
|
||||
// of the two faces. It solves the all pairs nearest neighbors problem.
|
||||
//
|
||||
// The routine is sadly bounded by n^2 complexity, but in practice perfromes very
|
||||
// well - much better than n^2 (closer to n log n).
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void delaunay_edge_flip()
|
||||
{
|
||||
int CurFlipped;
|
||||
int TotalFlipped = 0;
|
||||
|
||||
do
|
||||
{
|
||||
CurFlipped = flip();
|
||||
TotalFlipped += CurFlipped;
|
||||
}
|
||||
while (CurFlipped!=0 && TotalFlipped<10000 /*Sanity Condition*/);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This function attempts to prune out edges which connect across "floors" and
|
||||
//
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void floor_shape(typename TGraph::user& user, float maxzdelta)
|
||||
{
|
||||
ratl::vector_vs<int, MAXEDGES> CullEdges;
|
||||
int nEdge;
|
||||
TEdges::iterator stop=mEdges.end();
|
||||
for (TEdges::iterator it=mEdges.begin(); it!=mEdges.end(); it++)
|
||||
{
|
||||
if (!(*it).mOnHull)
|
||||
{
|
||||
edge& EdgeAt = *it;
|
||||
face& FaceR = mFaces[EdgeAt.mRight];
|
||||
face& FaceL = mFaces[EdgeAt.mLeft];
|
||||
|
||||
// int Edge = mEdges.index_to_handle(it.index());
|
||||
int R = FaceR.opposing_node(EdgeAt.mA, EdgeAt.mB);
|
||||
int L = FaceL.opposing_node(EdgeAt.mA, EdgeAt.mB);
|
||||
int RInd = mGraph.node_index(R);
|
||||
int LInd = mGraph.node_index(L);
|
||||
|
||||
TNODE& PtA = mGraph.get_node(EdgeAt.mA);
|
||||
TNODE& PtB = mGraph.get_node(EdgeAt.mB);
|
||||
TNODE& PtR = mGraph.get_node(R);
|
||||
TNODE& PtL = mGraph.get_node(L);
|
||||
|
||||
|
||||
if (
|
||||
(user.on_same_floor(PtR, PtL)) &&
|
||||
(mLinks.get(RInd, LInd)==0) &&
|
||||
(mLinks.get(LInd, RInd)==0) &&
|
||||
(!user.on_same_floor(PtL, PtA) || !user.on_same_floor(PtL, PtB))
|
||||
)
|
||||
{
|
||||
nEdge= mEdges.alloc();
|
||||
|
||||
mEdges[nEdge].mA = R;
|
||||
mEdges[nEdge].mB = L;
|
||||
mEdges[nEdge].mHullLoc = mHullIter;
|
||||
mEdges[nEdge].mOnHull = true;
|
||||
mEdges[nEdge].mFlips = 0;
|
||||
mEdges[nEdge].mLeft = 0;
|
||||
mEdges[nEdge].mRight = 0;
|
||||
|
||||
|
||||
mLinks.get(RInd, LInd) = nEdge;
|
||||
mLinks.get(LInd, RInd) = nEdge;
|
||||
}
|
||||
|
||||
if (!user.on_same_floor(PtA, PtB))
|
||||
{
|
||||
mLinks.get(mGraph.node_index(EdgeAt.mA), mGraph.node_index(EdgeAt.mB)) = 0;
|
||||
mLinks.get(mGraph.node_index(EdgeAt.mB), mGraph.node_index(EdgeAt.mA)) = 0;
|
||||
|
||||
CullEdges.push_back(it.index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<CullEdges.size(); i++)
|
||||
{
|
||||
mEdges.free_index(CullEdges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This function is a simple routine to prune out any edges which are larger or
|
||||
// smaller than the desired range (min, max).
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void alpha_shape(typename TGraph::user& user, float max, float min=0)
|
||||
{
|
||||
ratl::vector_vs<int, MAXEDGES> CullEdges;
|
||||
float cost;
|
||||
for (TEdges::iterator it=mEdges.begin(); it!=mEdges.end(); it++)
|
||||
{
|
||||
cost = user.cost(mGraph.get_node((*it).mA), mGraph.get_node((*it).mB));
|
||||
if (cost<min || cost>max)
|
||||
{
|
||||
mLinks.get(mGraph.node_index((*it).mA), mGraph.node_index((*it).mB)) = 0;
|
||||
mLinks.get(mGraph.node_index((*it).mB), mGraph.node_index((*it).mA)) = 0;
|
||||
|
||||
CullEdges.push_back(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<CullEdges.size(); i++)
|
||||
{
|
||||
mEdges.free_index(CullEdges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Call this function when you are done with the triangulation and want to copy all
|
||||
// the temp data into your graph.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void finish(typename TGraph::user& user)
|
||||
{
|
||||
mGraph.clear_edges();
|
||||
TEDGE DefaultEdge;
|
||||
for (TEdges::iterator it=mEdges.begin(); it!=mEdges.end(); it++)
|
||||
{
|
||||
user.setup_edge(DefaultEdge, (*it).mA, (*it).mB, (*it).mOnHull, mGraph.get_node((*it).mA), mGraph.get_node((*it).mB));
|
||||
mGraph.connect_node(DefaultEdge, (*it).mA, (*it).mB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef typename ratl::list_vs<int, MAXNODES> THull;
|
||||
typedef typename ratl::list_vs<int, MAXNODES>::iterator THullIter;
|
||||
typedef typename ratl::grid2_vs<int, MAXNODES, MAXNODES> TLinks;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Local Edge Class
|
||||
//
|
||||
// RIGHT
|
||||
// B<-<-<-<-<-<-A
|
||||
// LEFT
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class edge
|
||||
{
|
||||
public:
|
||||
int mA;
|
||||
int mB;
|
||||
|
||||
int mLeft;
|
||||
int mRight;
|
||||
int mFlips;
|
||||
|
||||
THullIter mHullLoc;
|
||||
bool mOnHull;
|
||||
|
||||
void flip_face(int OldFace, int NewFace)
|
||||
{
|
||||
assert(mRight!=mLeft);
|
||||
assert(mLeft!=NewFace && mRight!=NewFace);
|
||||
if (mLeft==OldFace)
|
||||
{
|
||||
mLeft=NewFace;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(mRight==OldFace);
|
||||
mRight = NewFace;
|
||||
}
|
||||
assert(mRight!=mLeft);
|
||||
}
|
||||
void verify(int PtA, int PtB, int Edge)
|
||||
{
|
||||
assert(PtA==mA || PtA==mB);
|
||||
assert(PtB==mA || PtB==mB);
|
||||
assert(mRight==Edge || mLeft==Edge);
|
||||
assert(mRight!=mLeft);
|
||||
assert(mA!=mB);
|
||||
}
|
||||
void verify(int PtA, int PtB, int PtC, int Edge)
|
||||
{
|
||||
assert((PtC==mA && (PtA==mB || PtB==mB)) || (PtC==mB && (PtA==mA || PtB==mA)));
|
||||
|
||||
assert(mRight==Edge || mLeft==Edge);
|
||||
assert(mRight!=mLeft);
|
||||
assert(mA!=mB);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Local Face Class
|
||||
//
|
||||
// _ C
|
||||
// /| \
|
||||
// LEFT/ \RIGHT
|
||||
// / \
|
||||
// B-<-<-<-<-A
|
||||
// BOTTOM
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class face
|
||||
{
|
||||
public:
|
||||
int mA;
|
||||
int mB;
|
||||
int mC;
|
||||
|
||||
int mLeft;
|
||||
int mRight;
|
||||
int mBottom;
|
||||
|
||||
int mFlips;
|
||||
|
||||
int& opposing_node(int A, int B)
|
||||
{
|
||||
if (mA!=A && mA!=B)
|
||||
{
|
||||
return mA;
|
||||
}
|
||||
if (mB!=A && mB!=B)
|
||||
{
|
||||
return mB;
|
||||
}
|
||||
assert(mC!=A && mC!=B);
|
||||
return mC;
|
||||
}
|
||||
|
||||
int& relative_left(int edge)
|
||||
{
|
||||
if (edge==mLeft)
|
||||
{
|
||||
return mRight;
|
||||
}
|
||||
if (edge==mRight)
|
||||
{
|
||||
return mBottom;
|
||||
}
|
||||
assert(edge==mBottom); // If you hit this assert, then the edge is not in this face
|
||||
return mLeft;
|
||||
}
|
||||
int& relative_right(int edge)
|
||||
{
|
||||
if (edge==mLeft)
|
||||
{
|
||||
return mBottom;
|
||||
}
|
||||
if (edge==mRight)
|
||||
{
|
||||
return mLeft;
|
||||
}
|
||||
assert(edge==mBottom); // If you hit this assert, then the edge is not in this face
|
||||
return mRight;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Sort Node Class
|
||||
//
|
||||
// Used To Sort Nodes In Increasing X Order
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class sort_node
|
||||
{
|
||||
public:
|
||||
bool operator<(const sort_node& r) const
|
||||
{
|
||||
return ((*r.mNodePointer)[0] < (*mNodePointer)[0]);
|
||||
}
|
||||
|
||||
int mNodeHandle;
|
||||
TNODE* mNodePointer;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef typename ratl::handle_pool_vs<edge, MAXEDGES> TEdges;
|
||||
typedef typename ratl::handle_pool_vs<edge, MAXEDGES>::iterator TEdgesIter;
|
||||
typedef typename ratl::handle_pool_vs<face, MAXFACES> TFaces;
|
||||
typedef typename ratl::vector_vs<sort_node, MAXNODES> TSortNodes;
|
||||
|
||||
|
||||
TGraph& mGraph; // A Reference To The Graph Points To Triangulate
|
||||
|
||||
TLinks mLinks;
|
||||
TEdges mEdges;
|
||||
TFaces mFaces;
|
||||
|
||||
THull mHull; // The Convex Hull
|
||||
THullIter mHullIter;
|
||||
|
||||
TSortNodes mSortNodes; // Need To Presort Nodes On (x-Axis) For Insertion Hull
|
||||
sort_node mSortNode;
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copy All The Graph Nodes To Our Sort Node Class And Run Heap Sort
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void sort_points()
|
||||
{
|
||||
mSortNodes.clear();
|
||||
for (TGraph::TNodes::iterator i=mGraph.nodes_begin(); i!=mGraph.nodes_end(); i++)
|
||||
{
|
||||
mSortNode.mNodeHandle = mGraph.node_handle(i);
|
||||
mSortNode.mNodePointer = &(*i);
|
||||
mSortNodes.push_back(mSortNode);
|
||||
}
|
||||
mSortNodes.sort();
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Create A New Edge A->B, And Fix Up The Face
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
int add_edge(int A, int B, int Face=0, bool OnHull=true)
|
||||
{
|
||||
assert(A!=B );
|
||||
|
||||
int nEdge = mLinks.get(mGraph.node_index(A), mGraph.node_index(B));
|
||||
|
||||
// Apparently This Edge Does Not Exist, So Make A New One
|
||||
//--------------------------------------------------------
|
||||
if (nEdge==0)
|
||||
{
|
||||
nEdge= mEdges.alloc();
|
||||
|
||||
mHull.insert_after(mHullIter, nEdge);
|
||||
assert(mHullIter!=mHull.end());
|
||||
|
||||
mEdges[nEdge].mA = A;
|
||||
mEdges[nEdge].mB = B;
|
||||
mEdges[nEdge].mHullLoc = mHullIter;
|
||||
mEdges[nEdge].mOnHull = true;
|
||||
mEdges[nEdge].mFlips = 0;
|
||||
mEdges[nEdge].mLeft = 0;
|
||||
mEdges[nEdge].mRight = 0;
|
||||
|
||||
|
||||
mLinks.get(mGraph.node_index(A), mGraph.node_index(B)) = nEdge;
|
||||
mLinks.get(mGraph.node_index(B), mGraph.node_index(A)) = nEdge;
|
||||
}
|
||||
|
||||
// If This Edge DOES Already Exist, Then We Need To Remove It From The Hull
|
||||
//--------------------------------------------------------------------------
|
||||
else if (mEdges[nEdge].mOnHull)
|
||||
{
|
||||
assert(mEdges[nEdge].mHullLoc!=mHull.end());
|
||||
|
||||
if (mHullIter==mEdges[nEdge].mHullLoc)
|
||||
{
|
||||
mHull.erase(mHullIter); // Make Sure To Fix Up The Hull Iter If That Is What We Are Removing
|
||||
}
|
||||
else
|
||||
{
|
||||
mHull.erase(mEdges[nEdge].mHullLoc);
|
||||
}
|
||||
mEdges[nEdge].mOnHull = false;
|
||||
}
|
||||
|
||||
|
||||
// If The Edge Was Made With The Same Orientation Currently Asked For (A->B), Then Mark Face As Right
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
if (mEdges[nEdge].mA==A)
|
||||
{
|
||||
mEdges[nEdge].mRight = Face;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEdges[nEdge].mLeft = Face;
|
||||
}
|
||||
return nEdge;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Create A New Face A->B->C, And Fix Up The Edges & Neighboring Faces
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
int add_face(int A, int B, int C)
|
||||
{
|
||||
int Temp = 0;
|
||||
int nFace = mFaces.alloc();
|
||||
|
||||
// First, Make Sure Node A.x Is Greater Than B and C. If Not, Swap With B or C
|
||||
//------------------------------------------------------------------------------
|
||||
if (mGraph.get_node(B)[0]>mGraph.get_node(A)[0])
|
||||
{
|
||||
Temp = A;
|
||||
A = B;
|
||||
B = Temp;
|
||||
}
|
||||
if (mGraph.get_node(C)[0]>mGraph.get_node(A)[0])
|
||||
{
|
||||
Temp = A;
|
||||
A = C;
|
||||
C = Temp;
|
||||
}
|
||||
|
||||
// Similarly, Make Sure Node B.y Is Greater Than Node C.y
|
||||
//--------------------------------------------------------
|
||||
if (mGraph.get_node(C).LRTest(mGraph.get_node(A), mGraph.get_node(B))==Side_Left)
|
||||
{
|
||||
Temp = C;
|
||||
C = B;
|
||||
B = Temp;
|
||||
}
|
||||
|
||||
// DEBUG ASSERTS
|
||||
//====================================================================================
|
||||
// IF YOU HIT THESE ASSERTS, CHANCES ARE THAT YOU ARE TRYING TO TRIANGULATE OVER A SET
|
||||
// WITH MORE THAN 2 COLINEAR POINTS ON THE SAME FACE. INSERT HULL WILL FAIL IN THIS
|
||||
// FACE. INSERT HULL WILL FAIL IN THIS SITUATION
|
||||
|
||||
assert(mGraph.get_node(C).LRTest(mGraph.get_node(A), mGraph.get_node(B))==Side_Right);
|
||||
assert(mGraph.get_node(A).LRTest(mGraph.get_node(B), mGraph.get_node(C))==Side_Right);
|
||||
assert(mGraph.get_node(B).LRTest(mGraph.get_node(C), mGraph.get_node(A))==Side_Right);
|
||||
//====================================================================================
|
||||
|
||||
mFaces[nFace].mA = A;
|
||||
mFaces[nFace].mB = B;
|
||||
mFaces[nFace].mC = C;
|
||||
|
||||
mFaces[nFace].mRight = add_edge(C, A, nFace);
|
||||
mFaces[nFace].mBottom = add_edge(A, B, nFace);
|
||||
mFaces[nFace].mLeft = add_edge(B, C, nFace);
|
||||
|
||||
mFaces[nFace].mFlips = 0;
|
||||
|
||||
return nFace;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Insertion Hull Triangulation
|
||||
//
|
||||
// This algorithm works by scanning the outer convex hull of the set of points that
|
||||
// have already been triangulated. When encountering a hull edge which evaluates
|
||||
// LEFT in a left right test (remember, the triangles always have clockwise orientation)
|
||||
// it adds a face to the triangulation including the edge as one side of the triangle
|
||||
// and two new edges to the node handle. It's very important to traverse the convex
|
||||
// hull in counter clockwise order (backwards).
|
||||
//
|
||||
// In the example below, we assume the convex hull starts at the edge (CA). (nodeHandle) is
|
||||
// RIGHT of (C->A), so it skips that edge and moves on to (D->C). (nodeHandle) is in fact
|
||||
// LEFT of (D->C), so we would add a new face that would go (D->nodeHandle->C), and we remove
|
||||
// (D->C) from the hull.
|
||||
//
|
||||
//
|
||||
//
|
||||
// (C)-------------(A)
|
||||
// / \ __/ \
|
||||
// (nodeHandle) / \ __/ \
|
||||
// / \ / \
|
||||
// (D)----____(B)_ \
|
||||
// \ | \ __
|
||||
// \ | \__
|
||||
// \ | \
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void insert_point(int nodeHandle)
|
||||
{
|
||||
// Iterate Over The Existing Convex Hull
|
||||
//---------------------------------------
|
||||
for (mHullIter = mHull.begin(); mHullIter!=mHull.end(); mHullIter++)
|
||||
{
|
||||
edge& curEdge = mEdges[*mHullIter];
|
||||
|
||||
// Can This Edge "See" The node Handle We Have Passed In?
|
||||
//---------------------------------------------------------
|
||||
if ( mGraph.get_node(nodeHandle).LRTest(mGraph.get_node(curEdge.mA), mGraph.get_node(curEdge.mB))==Side_Left )
|
||||
{
|
||||
// Then Add The Face And Remove This Edge From The Hull
|
||||
//------------------------------------------------------
|
||||
add_face(curEdge.mA, curEdge.mB, nodeHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Edge Flip Function
|
||||
//
|
||||
// This function scans the edge list for any edge that is "bad" (defined as not
|
||||
// fitting within the circumscribed circle of either adjoining face). When it
|
||||
// encounters one, it "flips" the edge in question and fixes up the adjoining faces
|
||||
// which were altered.
|
||||
//
|
||||
//
|
||||
// The Flip Edge (PtA->PtB):
|
||||
//
|
||||
//
|
||||
//
|
||||
// BEFORE AFTER
|
||||
//
|
||||
// (PtR) (PtA)
|
||||
// / \ / | \
|
||||
// / \ / | \
|
||||
// / (FaceR) \ / V \
|
||||
// / \ / | \
|
||||
// (PtB)-<---------<-(PtA) (PtR) | (PtL)
|
||||
// \ / \ | /
|
||||
// \ (FaceL) / \ V /
|
||||
// \ / \ | /
|
||||
// \ / \ | /
|
||||
// (PtL) (PtB)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
int flip()
|
||||
{
|
||||
int Flipped = 0;
|
||||
|
||||
int EdgeHandle;
|
||||
int PtR, PtL, PtA, PtB;
|
||||
int EdgeRL, EdgeRR, EdgeLL, EdgeLR;
|
||||
|
||||
|
||||
// Iterate Through All The Edges Looking For Potential NON-Delauney Edges
|
||||
//------------------------------------------------------------------------
|
||||
for (TEdgesIter CurEdge=mEdges.begin(); CurEdge!=mEdges.end(); CurEdge++)
|
||||
{
|
||||
// If It Is On The Hull, We Don't Even Need To Look At It
|
||||
//--------------------------------------------------------
|
||||
if (!(*CurEdge).mOnHull)
|
||||
{
|
||||
edge& EdgeAt = *CurEdge;
|
||||
face& FaceR = mFaces[EdgeAt.mRight];
|
||||
face& FaceL = mFaces[EdgeAt.mLeft];
|
||||
|
||||
EdgeHandle = mEdges.index_to_handle(CurEdge.index());
|
||||
PtA = EdgeAt.mA;
|
||||
PtB = EdgeAt.mB;
|
||||
PtR = FaceR.opposing_node(PtA, PtB);
|
||||
PtL = FaceL.opposing_node(PtA, PtB);
|
||||
|
||||
assert(EdgeAt.mRight!=EdgeAt.mLeft);
|
||||
assert(PtA!=PtB);
|
||||
assert(PtR!=PtL);
|
||||
assert(PtA!=PtR && PtA!=PtL);
|
||||
assert(PtB!=PtR && PtB!=PtL);
|
||||
|
||||
// Is This Edge Invalid For Delaunay?
|
||||
//-------------------------------------
|
||||
if (!mGraph.get_node(PtB).InCircle(mGraph.get_node(PtR), mGraph.get_node(PtL), mGraph.get_node(PtA)) &&
|
||||
!mGraph.get_node(PtA).InCircle(mGraph.get_node(PtR), mGraph.get_node(PtB), mGraph.get_node(PtL))
|
||||
)
|
||||
{
|
||||
// Change The Link: Remove The Old, Add The New
|
||||
//----------------------------------------------
|
||||
mLinks.get(mGraph.node_index(PtA), mGraph.node_index(PtB)) = 0;
|
||||
mLinks.get(mGraph.node_index(PtB), mGraph.node_index(PtA)) = 0;
|
||||
|
||||
mLinks.get(mGraph.node_index(PtR), mGraph.node_index(PtL)) = EdgeHandle;
|
||||
mLinks.get(mGraph.node_index(PtL), mGraph.node_index(PtR)) = EdgeHandle;
|
||||
|
||||
|
||||
Flipped++;
|
||||
EdgeAt.mFlips++;
|
||||
FaceL.mFlips++;
|
||||
FaceR.mFlips++;
|
||||
|
||||
// Flip The Edge We Found
|
||||
//------------------------
|
||||
EdgeAt.mA = PtR;
|
||||
EdgeAt.mB = PtL;
|
||||
|
||||
// Calculate Relatave Edges And Points Assuming (EdgeAt) Were mBottom For The Two Faces
|
||||
//--------------------------------------------------------------------------------------
|
||||
EdgeRL = FaceR.relative_left(EdgeHandle);
|
||||
EdgeRR = FaceR.relative_right(EdgeHandle);
|
||||
|
||||
EdgeLL = FaceL.relative_left(EdgeHandle);
|
||||
EdgeLR = FaceL.relative_right(EdgeHandle);
|
||||
|
||||
|
||||
// Fix Edges Which Had Been Rotated To New Faces
|
||||
//-----------------------------------------------
|
||||
mEdges[EdgeLR].flip_face(EdgeAt.mLeft, EdgeAt.mRight);
|
||||
mEdges[EdgeRR].flip_face(EdgeAt.mRight, EdgeAt.mLeft);
|
||||
|
||||
// Rotate The Edges Clockwise
|
||||
//----------------------------
|
||||
FaceR.mLeft = EdgeLR;
|
||||
FaceR.mRight = EdgeRL;
|
||||
FaceR.mBottom = EdgeHandle;
|
||||
|
||||
FaceL.mLeft = EdgeRR;
|
||||
FaceL.mRight = EdgeLL;
|
||||
FaceL.mBottom = EdgeHandle;
|
||||
|
||||
FaceR.mA = PtR;
|
||||
FaceR.mB = PtL;
|
||||
FaceR.mC = PtB;
|
||||
|
||||
FaceL.mA = PtR;
|
||||
FaceL.mB = PtL;
|
||||
FaceL.mC = PtA;
|
||||
|
||||
|
||||
// DEBUG VERIFICATION
|
||||
//========================================================================
|
||||
#ifdef _DEBUG
|
||||
mEdges[FaceR.mLeft ].verify(FaceR.mA, FaceR.mB, FaceR.mC, EdgeAt.mRight);
|
||||
mEdges[FaceR.mRight ].verify(FaceR.mA, FaceR.mB, FaceR.mC, EdgeAt.mRight);
|
||||
mEdges[FaceR.mBottom].verify(FaceR.mA, FaceR.mB, EdgeAt.mRight);
|
||||
|
||||
mEdges[FaceL.mLeft ].verify(FaceL.mA, FaceL.mB, FaceL.mC, EdgeAt.mLeft);
|
||||
mEdges[FaceL.mRight ].verify(FaceL.mA, FaceL.mB, FaceL.mC, EdgeAt.mLeft);
|
||||
mEdges[FaceL.mBottom].verify(FaceL.mA, FaceL.mB, EdgeAt.mLeft);
|
||||
#endif
|
||||
|
||||
assert(EdgeAt.mRight!=EdgeAt.mLeft);
|
||||
assert(PtA!=PtB);
|
||||
assert(PtR!=PtL);
|
||||
//========================================================================
|
||||
}
|
||||
}
|
||||
}
|
||||
return Flipped;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
1776
code/ragl/graph_vs.h
Normal file
1776
code/ragl/graph_vs.h
Normal file
File diff suppressed because it is too large
Load Diff
458
code/ragl/kdtree_vs.h
Normal file
458
code/ragl/kdtree_vs.h
Normal file
@@ -0,0 +1,458 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RAVEN STANDARD TEMPLATE LIBRARY
|
||||
// (c) 2002 Activision
|
||||
//
|
||||
//
|
||||
// KD Tree
|
||||
// -------
|
||||
//
|
||||
//
|
||||
//
|
||||
// NOTES:
|
||||
//
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(RATL_KDTREE_VS_INC)
|
||||
#define RATL_KDTREE_VS_INC
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(RA_DEBUG_LINKING)
|
||||
#pragma message("...including kdtree_vs.h")
|
||||
#endif
|
||||
#if !defined(RAGL_COMMON_INC)
|
||||
#include "ragl_common.h"
|
||||
#endif
|
||||
namespace ragl
|
||||
{
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The List Class
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <class T, int DIMENSION, int SIZE>
|
||||
class kdtree_vs : public ratl::ratl_base
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Capacity Enum
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
enum
|
||||
{
|
||||
CAPACITY = SIZE,
|
||||
NULL_NODE = SIZE+2, // Invalid Node ID
|
||||
TARG_NODE = SIZE+3 // Used To Mark Nodes Add Location
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
kdtree_vs() : mRoot(NULL_NODE)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// How Many Objects Are In This Tree
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
int size() const
|
||||
{
|
||||
return (mPool.size());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Are There Any Objects In This Tree?
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool empty() const
|
||||
{
|
||||
return (mRoot==NULL_NODE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Is This List Filled?
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool full() const
|
||||
{
|
||||
return (mPool.full());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Clear All Elements
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void clear()
|
||||
{
|
||||
mRoot = NULL_NODE;
|
||||
mPool.clear();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Add A New Element To The Tree
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void add(const T& data)
|
||||
{
|
||||
// CREATE: New
|
||||
//--------------------------------------------
|
||||
int nNew = mPool.alloc();
|
||||
mPool[nNew].mData = data;
|
||||
mPool[nNew].mLeft = NULL_NODE;
|
||||
mPool[nNew].mRight = NULL_NODE;
|
||||
|
||||
// LINK: (nNew)->(Parent)
|
||||
//--------------------------------------------
|
||||
if (mRoot==NULL_NODE)
|
||||
{
|
||||
mRoot = nNew;
|
||||
mPool[nNew].mParent = NULL_NODE;
|
||||
return;
|
||||
}
|
||||
|
||||
// LINK: (nNew)->(Parent)
|
||||
//--------------------------------------------
|
||||
mPool[nNew].mParent = find_index(data, mRoot, 0, true, true);
|
||||
|
||||
|
||||
// LINK: (Parent)->(nNew)
|
||||
//--------------------------------------------
|
||||
if (mPool[mPool[nNew].mParent].mLeft==TARG_NODE)
|
||||
{
|
||||
mPool[mPool[nNew].mParent].mLeft = nNew;
|
||||
}
|
||||
else if (mPool[mPool[nNew].mParent].mRight==TARG_NODE)
|
||||
{
|
||||
mPool[mPool[nNew].mParent].mRight = nNew;
|
||||
}
|
||||
|
||||
// Hey! It Didn't Mark Any Targets, Which Means We Found An Exact match To This Data
|
||||
//------------------------------------------------------------------------------------
|
||||
else
|
||||
{
|
||||
mPool.free(nNew);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Does (data) Exist In The Tree?
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool find(const T& data)
|
||||
{
|
||||
assert(mRoot!=NULL_NODE); // If You Hit This Assert, You Are Asking For Data On An Empty Tree
|
||||
|
||||
int node = find_index(data, mRoot, 0, true, true);
|
||||
|
||||
// Exact Find, Or Found Root?
|
||||
//----------------------------
|
||||
if (mPool[node].mData==data || mPool[node].mParent==NULL_NODE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class range_query
|
||||
{
|
||||
public:
|
||||
range_query() {}
|
||||
|
||||
public:
|
||||
ratl::vector_vs<T, SIZE> mReported;
|
||||
T mMins;
|
||||
T mMaxs;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void find(range_query& query)
|
||||
{
|
||||
if (mRoot!=NULL_NODE)
|
||||
{
|
||||
query.mReported.clear();
|
||||
tree_search(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class node
|
||||
{
|
||||
public:
|
||||
int mParent;
|
||||
int mLeft;
|
||||
int mRight;
|
||||
|
||||
T mData;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
class range_bounds
|
||||
{
|
||||
public:
|
||||
int mMins[DIMENSION];
|
||||
int mMaxs[DIMENSION];
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This Private Function Of The Class Does A Standard Binary Tree Search
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
int find_index(const T& data, int curNode, int curDimension, bool returnClosest, bool markTarget)
|
||||
{
|
||||
// Did We Just Go Off The End Of The Tree Or Find The Data We Were Looking For?
|
||||
//------------------------------------------------------------------------------
|
||||
if (curNode==NULL_NODE || mPool[curNode].mData==data)
|
||||
{
|
||||
return curNode;
|
||||
}
|
||||
|
||||
|
||||
// Calculate The Next Dimension For Searching
|
||||
//--------------------------------------------
|
||||
int nextDimension = curDimension+1;
|
||||
if (nextDimension>=DIMENSION)
|
||||
{
|
||||
nextDimension = 0;
|
||||
}
|
||||
|
||||
|
||||
// Search Recursivly Down The Tree Either Left (For Data > Current Node), Or Right
|
||||
//---------------------------------------------------------------------------------
|
||||
int findRecursive;
|
||||
bool goLeft = (data[curDimension] < mPool[curNode].mData[curDimension]);
|
||||
if (goLeft)
|
||||
{
|
||||
findRecursive = find_index(data, mPool[curNode].mLeft, nextDimension, returnClosest, markTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
findRecursive = find_index(data, mPool[curNode].mRight, nextDimension, returnClosest, markTarget);
|
||||
}
|
||||
|
||||
// Success!
|
||||
//----------
|
||||
if (findRecursive!=NULL_NODE)
|
||||
{
|
||||
return findRecursive;
|
||||
}
|
||||
|
||||
// If We Want To Return The CLOSEST Node, And We Went Off The End, Then Return This One
|
||||
//--------------------------------------------------------------------------------------
|
||||
if (returnClosest)
|
||||
{
|
||||
// If We Are Asked To Mark The Target, We Mark (TARG_NODE) At Either mLeft or mRight,
|
||||
// Depending On Where The Node Should Have Been
|
||||
//----------------------------------------------------------------------------------
|
||||
if (markTarget)
|
||||
{
|
||||
if (goLeft)
|
||||
{
|
||||
mPool[curNode].mLeft = TARG_NODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mPool[curNode].mRight = TARG_NODE;
|
||||
}
|
||||
}
|
||||
|
||||
// Go Ahead And Return This Node, It's The One We Would Have Put As The Child
|
||||
return curNode;
|
||||
}
|
||||
|
||||
// Return The Results Of The Recursive Call
|
||||
//------------------------------------------
|
||||
return NULL_NODE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This function just sets up the range bounds and starts the recursive tree search
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void tree_search(range_query& query)
|
||||
{
|
||||
range_bounds bounds;
|
||||
for (int i=0; i<DIMENSION; i++)
|
||||
{
|
||||
bounds.mMins[i] = 0;
|
||||
bounds.mMaxs[i] = 0;
|
||||
}
|
||||
tree_search(query, mRoot, 0, bounds);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void tree_search(range_query& query, int curNode, int curDimension, range_bounds bounds)
|
||||
{
|
||||
assert(curNode<SIZE);
|
||||
|
||||
// Is This Node In The Query Range? If So, Report It
|
||||
//----------------------------------------------------
|
||||
if (curNode!=NULL_NODE && tree_search_node_in_range(query, mPool[curNode]))
|
||||
{
|
||||
query.mReported.push_back(mPool[curNode].mData);
|
||||
}
|
||||
|
||||
// If This Is A Leaf Node, We're Done Here
|
||||
//-----------------------------------------
|
||||
if (curNode==NULL_NODE || (mPool[curNode].mLeft==NULL_NODE && mPool[curNode].mRight==NULL_NODE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate The Next Dimension For Searching
|
||||
//--------------------------------------------
|
||||
int nextDimension = curDimension+1;
|
||||
if (nextDimension>=DIMENSION)
|
||||
{
|
||||
nextDimension = 0;
|
||||
}
|
||||
|
||||
|
||||
// Test To See If Our Subtree Is In Range
|
||||
//----------------------------------------
|
||||
ESide Side = tree_search_bounds_in_range(query, bounds);
|
||||
|
||||
// If The Bounds Are Contained Entirely Within The Query Range, We Report The Sub Tree
|
||||
//-------------------------------------------------------------------------------------
|
||||
if (Side==Side_AllIn)
|
||||
{
|
||||
tree_search_report_sub_tree(query, curNode);
|
||||
}
|
||||
|
||||
// Otherwise, If Our Bounds Intersect The Query Range, We Need To Look Further
|
||||
//-----------------------------------------------------------------------------
|
||||
else if (Side==Side_In)
|
||||
{
|
||||
// Test The Left Child
|
||||
//---------------------
|
||||
if (mPool[curNode].mLeft!=NULL_NODE)
|
||||
{
|
||||
int OldMaxs = bounds.mMaxs[curDimension];
|
||||
if ( !bounds.mMins[curDimension] || ((mPool[curNode].mData[curDimension]) < (mPool[bounds.mMins[curDimension]].mData[curDimension])) )
|
||||
{
|
||||
bounds.mMins[curDimension] = curNode;
|
||||
}
|
||||
tree_search(query, mPool[curNode].mLeft, nextDimension, bounds);
|
||||
bounds.mMaxs[curDimension] = OldMaxs; // Restore Old Maxs For The Right Child Search
|
||||
}
|
||||
|
||||
// Test The Right Child
|
||||
//----------------------
|
||||
if (mPool[curNode].mRight!=NULL_NODE)
|
||||
{
|
||||
if ( !bounds.mMaxs[curDimension] || ((mPool[bounds.mMaxs[curDimension]].mData[curDimension]) < (mPool[curNode].mData[curDimension])) )
|
||||
{
|
||||
bounds.mMaxs[curDimension] = curNode;
|
||||
}
|
||||
tree_search(query, mPool[curNode].mRight, nextDimension, bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This Function Returns True If The Node Is Within The Query Range
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool tree_search_node_in_range(range_query& query, node& n)
|
||||
{
|
||||
for (int dim=0; dim<DIMENSION; dim++)
|
||||
{
|
||||
if (n.mData[dim]<query.mMins[dim] || query.mMaxs[dim]<n.mData[dim])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
ESide tree_search_bounds_in_range(range_query& query, range_bounds& bounds)
|
||||
{
|
||||
ESide S = Side_AllIn;
|
||||
for (int dim=0; dim<DIMENSION; dim++)
|
||||
{
|
||||
// If Any Of Our Dimensions Are Undefined Right Now, Always Return INTERSECT
|
||||
//---------------------------------------------------------------------------
|
||||
if (!bounds.mMaxs[dim] || !bounds.mMins[dim])
|
||||
{
|
||||
return Side_In;
|
||||
}
|
||||
|
||||
// Check To See If They Intersect At All?
|
||||
//----------------------------------------
|
||||
if ((mPool[bounds.mMaxs[dim]].mData[dim]<query.mMins[dim]) ||
|
||||
(query.mMaxs[dim]<mPool[bounds.mMins[dim]].mData[dim]))
|
||||
{
|
||||
return Side_None;
|
||||
}
|
||||
|
||||
// Check To See If It Is Contained
|
||||
//---------------------------------
|
||||
if ((mPool[bounds.mMins[dim]].mData[dim]<query.mMins[dim]) ||
|
||||
(query.mMaxs[dim]<mPool[bounds.mMaxs[dim]].mData[dim]))
|
||||
{
|
||||
S = Side_In;
|
||||
}
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Add The Cur Node And All Childeren Of The Cur Node
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void tree_search_report_sub_tree(range_query& query, int curNode)
|
||||
{
|
||||
assert(curNode<SIZE);
|
||||
|
||||
if (mPool[curNode].mLeft!=NULL_NODE)
|
||||
{
|
||||
query.mReported.push_back(mPool[mPool[curNode].mLeft].mData);
|
||||
tree_search_report_sub_tree(query, mPool[curNode].mRight);
|
||||
}
|
||||
if (mPool[curNode].mRight!=NULL_NODE)
|
||||
{
|
||||
query.mReported.push_back(mPool[mPool[curNode].mRight].mData);
|
||||
tree_search_report_sub_tree(query, mPool[curNode].mRight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Data
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
private:
|
||||
ratl::handle_pool_vs<node, SIZE> mPool; // The Allocation Data Pool
|
||||
int mRoot; // The Beginning Of The Tree
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
232
code/ragl/ragl_common.h
Normal file
232
code/ragl/ragl_common.h
Normal file
@@ -0,0 +1,232 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// RAVEN STANDARD TEMPLATE LIBRARY
|
||||
// (c) 2002 Activision
|
||||
//
|
||||
//
|
||||
// Common
|
||||
// ------
|
||||
// The raven libraries contain a number of common defines, enums, and typedefs which
|
||||
// need to be accessed by all templates. Each of these is included here.
|
||||
//
|
||||
// Also included is a safeguarded assert file for all the asserts in RTL.
|
||||
//
|
||||
// This file is included in EVERY TEMPLATE, so it should be very light in order to
|
||||
// reduce compile times.
|
||||
//
|
||||
//
|
||||
// Format
|
||||
// ------
|
||||
// In order to simplify code and provide readability, the template library has some
|
||||
// standard formats. Any new templates or functions should adhere to these formats:
|
||||
//
|
||||
// - All memory is statically allocated, usually by parameter SIZE
|
||||
// - All classes provide an enum which defines constant variables, including CAPACITY
|
||||
// - All classes which moniter the number of items allocated provide the following functions:
|
||||
// size() - the number of objects
|
||||
// empty() - does the container have zero objects
|
||||
// full() - does the container have any room left for more objects
|
||||
// clear() - remove all objects
|
||||
//
|
||||
//
|
||||
// - Functions are defined in the following order:
|
||||
// Capacity
|
||||
// Constructors (copy, from string, etc...)
|
||||
// Range (size(), empty(), full(), clear(), etc...)
|
||||
// Access (operator[], front(), back(), etc...)
|
||||
// Modification (add(), remove(), push(), pop(), etc...)
|
||||
// Iteration (begin(), end(), insert(), erase(), find(), etc...)
|
||||
//
|
||||
//
|
||||
// NOTES:
|
||||
//
|
||||
//
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(RAGL_COMMON_INC)
|
||||
#define RAGL_COMMON_INC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Includes
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(RA_DEBUG_LINKING)
|
||||
#pragma message("...including ragl_common.h")
|
||||
#endif
|
||||
#if !defined(RAGL_ASSERT_INC)
|
||||
#define RAGL_ASSERT_INC
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#if !defined(FINAL_BUILD)
|
||||
#if !defined(RAGL_PROFILE_INC) && !defined(_XBOX)
|
||||
#define RAGL_PROFILE_INC
|
||||
#include "Windows.h"
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(RAVL_VEC_INC)
|
||||
#include "..\Ravl\CVec.h"
|
||||
#endif
|
||||
#if !defined(RATL_COMMON_INC)
|
||||
#include "..\Ratl\ratl_common.h"
|
||||
#endif
|
||||
namespace ragl
|
||||
{
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Enums
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Typedefs
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Defines
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Graph Node Class
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
class CNode
|
||||
{
|
||||
public:
|
||||
CNode() {}
|
||||
CNode(const CVec3& Pt) : mPoint(Pt) {}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Access Operator (For Triangulation)
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
float operator[](int dimension)
|
||||
{
|
||||
return mPoint[dimension];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Equality Operator (For KDTree)
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool operator==(const CNode& t) const
|
||||
{
|
||||
return (t.mPoint==mPoint);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Left Right Test (For Triangulation)
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
virtual ESide LRTest(const CNode& A, const CNode& B) const
|
||||
{
|
||||
return (mPoint.LRTest(A.mPoint, B.mPoint));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Point In Circle (For Triangulation)
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
virtual bool InCircle(const CNode& A, const CNode& B, const CNode& C) const
|
||||
{
|
||||
return (mPoint.PtInCircle(A.mPoint, B.mPoint, C.mPoint));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
CVec3 mPoint;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Graph Edge Class
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
class CEdge
|
||||
{
|
||||
public:
|
||||
int mNodeA;
|
||||
int mNodeB;
|
||||
bool mOnHull;
|
||||
float mDistance;
|
||||
bool mCanBeInval;
|
||||
bool mValid;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Geometric Reference Class
|
||||
//
|
||||
// This adds one additional function to the common ratl_ref class to allow access for
|
||||
// various dimensions. It is used in both Triangulation and KDTree
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
template <class TDATA, class TDATAREF>
|
||||
class ragl_ref
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructors
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
ragl_ref() {}
|
||||
ragl_ref(const ragl_ref & r) {mDataRef = (TDATAREF)(r.mDataRef);}
|
||||
ragl_ref(const TDATA & r) {mDataRef = (TDATAREF)(& r);}
|
||||
ragl_ref(const TDATAREF r) {mDataRef = (TDATAREF)(r);}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Assignment Operators
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
void operator=(const ragl_ref & r) {mDataRef = (TDATAREF)(r.mDataRef);}
|
||||
void operator=(const TDATA & r) {mDataRef = (TDATAREF)(& r);}
|
||||
void operator=(const TDATAREF r) {mDataRef = (TDATAREF)(r);}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Access Operator (For Triangulation)
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
float operator[](int dimension) const {return (*mDataRef)[dimension];}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Dereference Operator
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
TDATA & operator*() {return (*mDataRef);}
|
||||
const TDATA & operator*() const {return (*mDataRef);}
|
||||
|
||||
TDATAREF handle() const {return mDataRef;}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Equality / Inequality Operators
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool operator== (const ragl_ref& t) const {return (*mDataRef)==(*(t.mDataRef));}
|
||||
bool operator!= (const ragl_ref& t) const {return (*mDataRef)!=(*(t.mDataRef));}
|
||||
bool operator< (const ragl_ref& t) const {return (*mDataRef)< (*(t.mDataRef));}
|
||||
bool operator> (const ragl_ref& t) const {return (*mDataRef)> (*(t.mDataRef));}
|
||||
bool operator<= (const ragl_ref& t) const {return (*mDataRef)<=(*(t.mDataRef));}
|
||||
bool operator>= (const ragl_ref& t) const {return (*mDataRef)>=(*(t.mDataRef));}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Equality / Inequality Operators
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
bool operator== (const TDATA& t) const {return (*mDataRef)==t;}
|
||||
bool operator!= (const TDATA& t) const {return (*mDataRef)!=t;}
|
||||
bool operator< (const TDATA& t) const {return (*mDataRef)< t;}
|
||||
bool operator> (const TDATA& t) const {return (*mDataRef)> t;}
|
||||
bool operator<= (const TDATA& t) const {return (*mDataRef)<=t;}
|
||||
bool operator>= (const TDATA& t) const {return (*mDataRef)>=t;}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Data Reference
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
private:
|
||||
TDATAREF mDataRef;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user