|
Home | Message board |
|
I wrote this C++ code to maintain the dynamic sets of tetrahedra in my programs.
Vertex object is rather straightforward:
class CVertex
{
public:
double X,Y,Z;
// add your own members...
};
typedef CVertex *CVertexPtr;
Tetrahedron object is also simple:
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.
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.
Imagine, we need to assign bace to be adjacent to bcad,
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:
Each rotation can be in unique way represented as X*A, where
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,
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. |