The Parametric Pseudo-Manifold (PPS) Library 1.0
|
This section discusses the implementation of a concrete class for building parametric pseudo surfaces using the PPS library. As we mentioned before, a concrete class must inherits from the PPS
class, and then implement the pure virtual method described in sections The generic mesh API and Defining geometry. In order to do that, the user must choose a data structure for representing the input triangle mesh, and define a parametric surface over the triangle mesh. Here, let us assume that we choose the DCEL data structure (the one that comes with the PPS library code - see directory "dcel"). In addition, let us use the union of PN triangle surface patches as the parametric surface defined over the triangle mesh. This is an extremely simple way of defining cubic (triangular) Bézier patches over mesh faces which join with -continuity along common edges and vertices. You can find more details on PN triangle surface patches in
The class we are about to define is in the file ppsfrompnt.h
inside directory "ppsfrompnt". We named the class PPSfromPNT
. The code starts by including the files below:
#include "bezier.h" // pps::Bezier #include "pps.h" // pps::PPS #include "surface.h" // dcel::Surface #include "pntriangle.h" // PNTriangle
File bezier.h
contains the definition of class Bezier
, which represents the shape functions used by the parametrizations of the parametric pseudo surfaces. File pps.h
contains the PPS
class. File surface.h
is the main file of the DCEL data structure. Finally, file pntriangle.h
contains an implementation of the PN triangle surface.
Next, we include three more files, which have to do with attributes we assign with vertex, half-edge, and face elements of the DCEL data structure:
#include "vertex_attribute.h" // VertexAttribute #include "halfedge_attribute.h" // HalfedgeAttribute #include "face_attribute.h" // FaceAttribute
Finally, we can start defining the ppsfrompnt::PPSfromPNT
class:
class PPSfromPNT : public PPS< dcel::Surface< VertexAttribute , FaceAttribute , int , HalfedgeAttribute > > { public: ...
The above code defines a class named PPSfromPNT
as a child of class PPS
. Note that class PPS is parametrized by the type of the input triangle mesh:
This means that dcel::Surface<
VertexAttribute , FaceAttribute , int , HalfedgeAttribute > is our triangle mesh type. The PPS
class has a pointer, called pps::PPS::_mesh
, for an object of type Mesh
, which is the parameter of the PPS
class. So, the above code instantiates this parameter as being dcel::Surface
, i.e., a triangle mesh represented by the DCEL data structure. Note that class dcel::Surface
has itself four parameters, which account for the types of the attributes of its vertex, face, edge, and half-edge attributes. We only define attributes for vertices, faces, and half-edges. Those attributes are represented by the classes ppsfrompnt::VertexAttribute
, ppsfrompnt::FaceAttribute
, and ppsfrompnt::HalfedgeAttribute
, respectively.
The code for the PPSfromPNT
class constructor assigns the given triangle mesh to the pointer pps::PPS::_mesh
, initializes some attributes, and then calls a private method to build the shape functions assigned with each vertex:
PPSfromPNT::PPSfromPNT( Surface* mesh ) : PPS< Surface >( mesh ) { // // Sets the owner of each half-edge attribute set. // for ( EdgeIterator eit = edges_begin() ; !is_done( eit ) ; move_forward( eit ) ) { Edge* edge = get_edge( eit ) ; Halfedge* h1 = edge->get_first_halfedge() ; Halfedge* h2 = edge->get_second_halfedge() ; h1->get_attributes().set_owner( h1 ) ; h2->get_attributes().set_owner( h2 ) ; } // // Create the PN triangle surface. // build_pnt_surface() ; }
What really matters in the above code is the initialization of the pointer pps::PPS::_mesh, as the rest of the code is dependent on our choices of the DCEL data structure and of the PN triangle surface. In particular, the initialization of attributes is related to the way we store the PPS information in the DCEL data structure. In turn, the method build_pnt_surface()
constructs a PN triangle for each face of the mesh. So, if another surface were adopted by the user, a distinct function would be used to create the surface patches.
Besides the constructor and destructor of the PPSfromPNT
class, the only public method is
void PPSfromPNT::eval_surface( Face* face , double u , double v , double w , double& x , double& y , double& z ) const { // // Make sure the face has a PN triangle associated with it. // PNTriangle* patch = face->get_attributes().get_patch() ; assert( patch != 0 ) ; // // Make sure the coordinates are non-negative and add up to 1. // assert( ( u >= 0 ) && ( u <= 1 ) && ( v >= 0 ) && ( v <= 1 ) && ( w >= 0 ) && ( w <= 1 ) && ( fabs( 1 - ( u + v + w ) ) <= 1e-15 ) ) ; // // Evaluate the patch. // patch->point( u , v , x , y , z ) ; }
The above method is our implementation of the corresponding pure virtual method of class PPS
. The method computes a point on a PN triangle surface, which is then used by the PPS
class for generating the shape functions and parametrizations of the parametric pseudo surface. Note that the above method simply gets the PN triangle patch associated with the given face, and then samples the patch at the given point.
The remaining relevant methods of class PPSfromPNT
are the ones that implement the other twenty six pure virtual methods of class PPS
. The implementation of these methods are completely dependent on the DCEL data structure, but they are extremely simple, as can be seen below:
inline bool mesh_has_boundary() const { return false ; }
inline bool mesh_is_simplicial() const { return true ; }
inline unsigned int get_id( Halfedge* h ) const { return h->get_attributes().get_pps_id() ; }
inline Vertex* get_org( Halfedge* h ) const { return h->get_origin() ; }
inline Vertex* get_dst( Halfedge* h ) const { return h->get_next()->get_origin() ; }
inline Edge* get_edge( Halfedge* h ) const { return h->get_edge() ; }
inline Face* get_face( Halfedge* h ) const { return h->get_face() ; }
inline Halfedge* get_prev( Halfedge* h ) const { return h->get_prev() ; }
inline Halfedge* get_next( Halfedge* h ) const { return h->get_next() ; }
inline Halfedge* get_mate( Halfedge* h ) const { return h->get_mate() ; }
inline Halfedge* get_halfedge( Face* face ) const { return face->get_halfedge() ; }
inline Halfedge* get_halfedge( Vertex* vertex ) const { return vertex->get_halfedge() ; }
inline unsigned get_degree( Vertex* vertex ) const { return vertex->get_halfedge()->get_attributes().get_origin_vertex_degree() ; }
inline Bezier* get_shape_function( Vertex* vertex ) const { return vertex->get_attributes().get_patch() ; }
inline void set_shape_function( Vertex* vertex, Bezier* patch ) { vertex->get_attributes().set_patch( patch ) ; }
inline VertexIterator vertices_begin() const { return get_mesh()->vertices_begin() ; }
inline bool is_done( const VertexIterator& iterator ) const { return iterator == get_mesh()->vertices_end() ; }
inline void move_forward( VertexIterator& iterator ) const { ++iterator ; }
inline Vertex* get_vertex( const VertexIterator& iterator ) const { return *iterator ; }
inline EdgeIterator edges_begin() const { return get_mesh()->edges_begin() ; }
inline bool is_done( const EdgeIterator& iterator ) const { return iterator == get_mesh()->edges_end() ; }
inline void move_forward( EdgeIterator& iterator ) const { ++iterator ; }
inline Edge* get_edge( const EdgeIterator& iterator ) const { return *iterator ; }
inline FaceIterator faces_begin() const { return get_mesh()->faces_begin() ; }
inline bool is_done( const FaceIterator& iterator ) const { return iterator == get_mesh()->faces_end() ; }
inline void move_forward( FaceIterator& iterator ) const { ++iterator ; }
inline Face* get_face( const FaceIterator& iterator ) const { return *iterator ; }
If the user wishes to use the DCEL data structure as the data structure for representing the input triangle mesh, then she/he can use (as it is) the implementation above for the pure virtual methods of class PPS
. However, if another data structure is used, then the user must implement the methods above in a similar way. We really hope that the flexibility offered by the PPS
class pays off the effort need to implement a derived class.