[an error occurred while processing this directive] [an error occurred while processing this directive] [an error occurred while processing this directive]
Games for Windows | Games for PocketPC | Games for SmartPhone | Online games
Home | Message board

Tetrahedron data structure

I wrote this C++ code to maintain the dynamic sets of tetrahedra in my programs.
You may find my data structures useful if you write the program that handles geometrical objects
such as tetrahedral meshes or related stuff...

Vertex object is rather straightforward:
it contains 3 space coordinates and whatever else information you want to keep there.

class CVertex
{
public:
    double X,Y,Z;
    // add your own members...
};
typedef CVertex *CVertexPtr;

Tetrahedron object is also simple:
it contains four pointers to its vertices and four pointers to the adjacent tetrahedra.

class CTetrahedron
{
public:
    CVertex *V[4];
    CPtr P[4];
    // ...
};

Each tetrahedron can be wieved from one of twelve viewpoints:

enum Rotation {
    abcd,acdb,adbc,badc,bcad,bdca,
    cabd,cbda,cdab,dacb,dbac,dcba
};
Superposition, as well as other operations with rotations, is implemented using a lookup table:
inline Rotation operator*(Rotation a,Rotation b){ return Mult[b][a];}

Tetrahedron pointer is the main object type that is used to control all operations on tetrahedra.
It contains the regular C++ pointer to the tetrahedron, and the rotation.

class CPtr
{
public:
    CTetrahedron *P;
    Rotation R;
    CPtr(CTetrahedron* p=0,Rotation r=abcd):P(p),R(r){}
    // ...
};
Tetrahedron pointer class implements the functions to access the vertices and adjacent tetrahedra,
and a bunch of rotation functions:
    CPtr operator*(Rotation r){ return CPtr(P,R*r);}
    CPtr Abcd(){ return *this;} // just to have the complete set...
    CPtr Acdb(){ return *this*acdb;}
    CPtr Adbc(){ return *this*adbc;}
    CPtr Badc(){ return *this*badc;}
    CPtr Bcad(){ return *this*bcad;}
    CPtr Bdca(){ return *this*bdca;}
    CPtr Cabd(){ return *this*cabd;}
    CPtr Cbda(){ return *this*cbda;}
    CPtr Cdab(){ return *this*cdab;}
    CPtr Dacb(){ return *this*dacb;}
    CPtr Dbac(){ return *this*dbac;}
    CPtr Dcba(){ return *this*dcba;}
Accessing vertices in the correct order is done via the lookup table:
    CVertexPtr& operator[](int n){ return P->V[Vert[R][n]];}


Tetrahedra abcd and acbe
are adjacent to each other

Accessing adjacent tetrahedra is a bit more tricky.
Tetrahedron keeps four pointers to the tetrahedra adjucent to abcd, badc, cdab, dcba,
all other pointers have to be recalculated both as r- and l-values.

Imagine, we need to assign bace to be adjacent to bcad,
in practice, that would mean assigning acbe adjucent to abcd.

A special helper class Adjacent pointer is used to perform these on-the-fly substitutions:

class CPtr
{
    // ...
    // type conversion from adjacent pointer:
    CPtr(CAdjPtr&A):P(A.Ptr.P->P[NAdj[A.Ptr.R]].P),R(A.Ptr.P->P[NAdj[A.Ptr.R]].R*TAdj[A.Ptr.R]){}
    // ...
    CAdjPtr Adj(){ return CAdjPtr(*this);}
    // ...
};

class CAdjPtr
{
public:
    CPtr &Ptr;
    CAdjPtr(CPtr&p):Ptr(p){}
    void operator=(CPtr&p){ Ptr.P->P[NAdj[Ptr.R]].P=p.P; Ptr.P->P[NAdj[Ptr.R]].R=p.R*RAdj[Ptr.R];}
    // ...
    CPtr Adj(){ return Ptr;} // adjacent to my adjacent is myself!
    // ...
};

Note, that there are two tables used in both type convertion and assignment operator:
one to find the correct pointer in array, and another to rotate that pointer.

Each rotation can be in unique way represented as X*A, where
X ∈ {abcd, badc, cdab, dcba} - adjacent tetrahedra for these rotations are stored in the array;
and A ∈ {abcd, bcad, cabd} - permutations that leave the last vertex intact.
Adjacency operator T=acb$ permutes two vertices of the base triangle and excahanges the apexes,
suct that X*T is the tetrahedra adjacent to X. It's easy to see that T-1=T.
If Y = X*T is adjacent to X, then Y*T*A*T is adjacent to X*A.
Tables TAdj[] and RAdj[] from the code above store the values T*A*T and (T*A*T)-1.
Though, in the case of the tetrahedron T*A*T = A-1, this might not be so in the higher dimensions.

Finally, to make the adjacent pointer look and feel exactly like a normal tetrahedron pointer in the expressions like

P.Cabd().Dbac().Adj().Bcad() ...
we redefine all tetrahedron pointer functions for the adjacent pointer in the following way:
    CVertexPtr& operator[](int n){ return CPtr(*this)[n];}
    CPtr Abcd(){ return CPtr(*this).Abcd();}
    // etc...


Download complete code: tetrahedron1.h.


Compile-time rotations

Consider the expression:

p1 = p.Acdb().Badc().Bcad();   // equivalent to p1 = p;
It will take 3 table lookups during the run-time to calculate the new pointer.
Can we do better?

- This will require a bunch of additional helper classes:

class CabcdPtr; class CacdbPtr; class CadbcPtr; class CbadcPtr;
class CbcadPtr; class CbdcaPtr; class CcabdPtr; class CcbdaPtr;
class CcdabPtr; class CdacbPtr; class CdbacPtr; class CdcbaPtr;
Each helper class has the reference to the original pointer,
and the set of rotation functions that are just type conversions to other helpers:
class CacdbPtr
{
public:
// the only member:
    CPtr &Ptr;
// constructor:
    CacdbPtr(CPtr&p):Ptr(p){}
// rotations:
    CacdbPtr Abcd(){ return Ptr;}
    CadbcPtr Acdb(){ return Ptr;}
    CabcdPtr Adbc(){ return Ptr;}
    CcabdPtr Badc(){ return Ptr;}
    CcdabPtr Bcad(){ return Ptr;}
    CcbdaPtr Bdca(){ return Ptr;}
    CdacbPtr Cabd(){ return Ptr;}
    CdcbaPtr Cbda(){ return Ptr;}
    CdbacPtr Cdab(){ return Ptr;}
    CbadcPtr Dacb(){ return Ptr;}
    CbcadPtr Dbac(){ return Ptr;}
    CbdcaPtr Dcba(){ return Ptr;}
// other CPtr functions:
    CAdjPtr Adj(){ return CPtr(*this).Adj();}
    CVertexPtr& operator[](int n){ return CPtr(*this)[n];}
};

Original pointer class gets new constructor from each helper class,
and its rotation functions are also changed to type conversions.

    CPtr(CacdbPtr&A):P(A.Ptr.P),R(A.Ptr.R*acdb){}
    CacdbPtr Acdb(){ return *this;}    // instead of CPtr Acdb();

The compiler will substitute the the expression discussed above by the series of inline functions:

CacdbPtr CPtr::Acdb();
CcabdPtr CacdbPtr::Badc();
CabcdPtr CcabdPtr::Bcad();
CPtr::CPtr(CabcdPtr);
thus, doing all rotations in compile time.




Download complete code: tetrahedron2.h.


(C) 2000-11, PYVA-NET