Thanks for the tips, but I'm still not satisfied with replies. What you
told be about ctors and dtors and abstraction and virtual functions
I've known for a long time and I've written thousands of abstract
classes in my lifetime. This Mesh class in fact is already written and
of course the Vertex,HalfEdge and Face classes do have ctors and dtors
etc.
Bart: you are talking about my "abstract" code relying on operations in
derived classes and that it should be part of the abstract interface.
But it doesn't really make sense in this case. I do want the next/prev
pointers of edges and so on to be inside the base classes, because it
doesn't make any sense to leave the user of the Mesh class to write and
maintain these links. All the user wants to do is to be able to specify
where the vertices of the mesh are and how they are connected. This is
done through the mesh construction functions of Mesh class like:
Mesh::addVertex(float x, float y, float z) - returns a pointer to a
newly created Vertex class containing the given coordinates
Mesh::addFace(Vertex **verts, int size) - creates a face having the
[size] number of specified vertices and returns the newly created face.
all the other required topology is constructed as well (edges between
the vertices that don't exist yet etc.)
How the vertices and edges and faces are linked together is not the
work of a user of the Mesh class but instead this should be taken care
of automatically by the Mesh class. However as I create a face by
specifying which its vertices are, I want to get returned (and I want
the mesh to create) a face of my derived type so that it includes my
"normal" member along with the basic stuff needed to link it with other
mesh entities. The trick is I want at the same the pointers of the mesh
entities to become of my derived class types so I can walk around the
mesh in a comfortable way. Of course I already implemented the
adjacency iterators like VertEdgeIter (iterates over edges connected to
a vertex), VertFaceIter (iterates over adjacent faces of a vertex),
FaceEdgeIter (iterate over edges of a face) etc. But I don't want to
use a FaceEdgeIter everytime I just want to pick the next edge (I'm
talking about the "client" code, the code that an end-user of class
Mesh writes). That's why I thought would be nice to have the prev/next
pointers to become of my derived type as I specify these derived
classes as templates arguments of the Mesh class.
Look at these implementations, they all use classes structured in the
same way I am talking about
:
http://w3.impa.br/~esdras/tops/classMesh_1_1Surf.html http://www.ee.surrey.ac.uk/Research/...Edge_Mesh.html http://www.openmesh.org/
This last one resembles the most what I am trying to achieve. And here
we come to the solution that Peter suggested - that the nested classes
of Mesh class should be derives of the base classes provided through
the template arguments instead of the contrary. This could make sense
if I expose it to the Mesh class user like this:
- Mesh class takes template arguments for each of the basic mesh
entities
- These template arguments are "traits" (<-- these is the main trick!)
of the final mesh entity classes.
- The classes finally used by the user are the "final mesh classes"
(this is mainly the trick that OpenMesh implementation uses)
The only unnatural thing is that each of the traits has to be given
even if we don't want to add any functionallity. But this could be
solved by defining empty base trait classes to use them if
derivation is not needed. See this example:
//////////MESH_CLASS_CODE/////////////
template <class PointType, class VertTraits, class HalfEdgeTraits,
class FaceTraits>
class Mesh {
//forward declaration of class HalfEdge so we can inter-link them
class HalfEdge;
//final vertex class
class Vertex : public VertTraits {
PointType point;
HalfEdge *edge;
};
//final face class
class Face : public FaceTraits {
HalfEdge *edge;
};
//final half-edge class
class HalfEdge : public HalfEdgeTraits {
Vertex *vert;
Face *face;
HalfEdge *next;
HalfEdge *twin;
};
//implementation of functions follows
//....
};
class BaseVertTraits {
};
class BaseHalfEdgeTraits {
};
class BaseFaceTraits {
};
//////////END::MESH_CLASS_CODE/////////////
Now, when the user of the class wants to add a normal member to a face
it does so like this:
class MyFaceTraits {
float normal;
};
typedef Mesh <
Vector3,
BaseVertTraits, //<---- base traits used
BaseHalfEdgeTraits, //<---- base traits used
MyFaceTraits, //<---- custom traits used
MyMesh;
And then all the links inside the final classes and all the classes
returned by functions of Mesh class use the final nested classes like
this:
void calcTriangleNormal(MyMesh::Face *f) {
MyMesh::Vertex *v1 = f->edge->vert;
MyMesh::Vertex *v2 = f->edge->next->vert;
MyMesh::Vertex *v3 = f->edge->next->next->vert;
Vector2 side1 = v2->point - v1->point;
Vector2 side2 = v3->point - v1->point;
f->normal = normalize(cross(side1,side2));
}
int main (int argc, char ** argv) {
MyMesh mesh;
MyMesh::Vertex *v1 = mesh.addVertex(Vector3(-1,0,0));
MyMesh::Vertex *v2 = mesh.addVertex(Vector3(1,0,0));
MyMesh::Vertex *v3 = mesh.addVertex(Vector3(0,1,0));
MyMesh::Face *f = mesh.addFace(v1, v2, v3);
calcTriangleNormal(f);
};
Of course this would be extended to faces with arbitrary number of
vertices. What do you think about this kind of implementation?