Improved documentation about allocation. Added sample on allocation.

This commit is contained in:
Paolo Cignoni 2012-10-25 22:53:33 +00:00
parent d784bebe07
commit fc5a0216ac
5 changed files with 150 additions and 54 deletions

View File

@ -1,6 +1,7 @@
TEMPLATE = subdirs
SUBDIRS = trimesh_base \
trimesh_allocate \
trimesh_attribute \
trimesh_ball_pivoting \
trimesh_closest \

View File

@ -0,0 +1,100 @@
/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004-2012 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/*! \file trimesh_allocate.cpp
\ingroup code_sample
\brief the minimal example of using the attributes
Attributes are a simple mechanism to associate user-defined 'attributes' to the simplicies and to the mesh.
\ref attributes for more Details
*/
#include<vcg/complex/complex.h>
#include<vcg/complex/algorithms/create/platonic.h>
class MyEdge;
class MyFace;
class MyVertex;
struct MyUsedTypes : public vcg::UsedTypes< vcg::Use<MyVertex> ::AsVertexType,
vcg::Use<MyFace> ::AsFaceType>{};
class MyVertex : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f,vcg::vertex::Normal3f>{};
class MyFace : public vcg::Face< MyUsedTypes, vcg::face::VertexRef, vcg::face::Normal3f> {};
class MyMesh : public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> > {};
int main()
{
MyMesh m;
vcg::tri::Allocator<MyMesh>::AddVertices(m,3);
vcg::tri::Allocator<MyMesh>::AddFaces(m,1);
MyMesh::VertexPointer ivp[3];
MyMesh::VertexIterator vi=m.vert.begin();
ivp[0]=&*vi;(*vi).P()=MyMesh::CoordType ( 0.0, 0.0, 0.0); ++vi;
ivp[1]=&*vi;(*vi).P()=MyMesh::CoordType ( 1.0, 0.0, 0.0); ++vi;
ivp[2]=&*vi;(*vi).P()=MyMesh::CoordType ( 0.0, 1.0, 0.0); ++vi;
MyFace &f=m.face[0];
f.V(0)=ivp[0];
f.V(1)=ivp[1];
f.V(2)=ivp[2];
// a potentially dangerous pointer to a mesh element
MyMesh::FacePointer fp = &m.face[0];
vcg::tri::Allocator<MyMesh>::PointerUpdater<MyMesh::FacePointer> pu;
// now the fp pointer could be no more valid due to eventual re-allocation of the m.face vector.
vcg::tri::Allocator<MyMesh>::AddVertices(m,3);
vcg::tri::Allocator<MyMesh>::AddFaces(m,1,pu);
// check if an update of the pointer is needed and do it.
if(pu.NeedUpdate()) pu.Update(fp);
// Now fill the mesh with an Icosahedron and then delete some faces
vcg::tri::Icosahedron(m);
vcg::tri::Allocator<MyMesh>::DeleteFace(m,m.face[1]);
vcg::tri::Allocator<MyMesh>::DeleteFace(m,m.face[3]);
// If you loop in a mesh with deleted elements you have to skip them!
MyMesh::FaceIterator fi;
for(fi = m.face.begin(); fi!=m.face.end(); ++fi )
{
if(!fi->IsD()) // <---- Check added
{
MyMesh::CoordType b = vcg::Barycenter(*fi);
}
}
// WRONG WAY of iterating: FN() != m.face.size() if there are deleted elemen
for(int i=0;i<m.FN();++i)
{
if(!fi->IsD())
{
MyMesh::CoordType b = vcg::Barycenter(*fi);
}
}
}

View File

@ -0,0 +1,3 @@
include(../common.pri)
TARGET = trimesh_allocate
SOURCES += trimesh_allocate.cpp ../../../wrap/ply/plylib.cpp

View File

@ -1,79 +1,63 @@
/** \page allocation Allocating and DeAllocating mesh elements
/** \page allocation Allocating and Deleting mesh elements
Creating elements
=================
To create a simple single triangle mesh or to add elements to an existing mesh you should use the AddVertices and AddFaces functions, elements are added at the end of the mesh. These functions returns a pointer to the first allocated element.
Adding element to a vector can cause reallocation, and therefore invalidation of any pointer that points to the mesh elements. These fucntion manage safely re-allocation and updating of pointers for all the pointers stored internally in the mesh (e.g. if you add some vertices and that causes a reallocation of the vertex vector, the pointers from faces to vertices will be automatically updated by the Allocator functions.
\code
#include <vcg/complex/trimesh/allocate.h>
//...define MyMesh
MyMesh m;
m.Clear();
Allocator<MyMesh>::AddVertices(m,3);
Allocator<MyMesh>::AddFaces(m,1);
MyMesh::VertexPointer ivp[3];
VertexIterator vi=m.vert.begin();
ivp[0]=&*vi;(*vi).P()=CoordType ( 1.0, 1.0, 1.0); ++vi;
ivp[1]=&*vi;(*vi).P()=CoordType (-1.0, 1.0,-1.0); ++vi;
ivp[2]=&*vi;(*vi).P()=CoordType (-1.0,-1.0, 1.0); ++vi;
FaceIterator fi=m.face.begin();
(*fi).V(0)=ivp[0]; (*fi).V(1)=ivp[1]; (*fi).V(2)=ivp[2];
\endcode
\dontinclude trimesh_allocate.cpp
\skip MyMesh
\until f.V(2)=ivp[2];
look to platonic.h for more examples.
If you keep interally some pointers to the mesh elements adding elements can invalidate them. In that case you should pass to the code>Allocator</code> functions a PointerUpdater to be used to update your private pointers.
\code
MyMesh m;
...
...
MyMesh::VertexPointer vp = &m.vert[0]; // a potentially dangerous pointer
PointerUpdater<VertexPointer> pu;
// now the vp pointer could be no more valid due to eventual re-allocation of the m.vert vector.
Allocator<MyMesh>::AddVertices(m,3,pu);
// check if an update of the pointer is needed and do it.
if(pu.NeedUpdate()) pu.Update(vp);
\endcode
If you keep interally some pointers to the mesh elements adding elements can invalidate them. In that case you should pass to the vcg::tri::Allocator functions a PointerUpdater to be used to update your private pointers.
\dontinclude trimesh_allocate.cpp
\skip dangerous
\until NeedUpdate
Destroying Elements
-------------------
Lazy deletion strategy.
Note that the two basic deletion strategies are very low level functions. They simply mark as deleted the corresponding entries without affecting the rest of the structures. So for example if you delete a vertex with these structures without checking that all the faces incident on it have been removed you create a non consistent situation...
The library adopts a <em>Lazy Deletion Strategy</em> i.e. the elements in the vector that are deleted are only \b flagged as deleted, but they are still there.
Note that the basic deletion functions are very low level ones. They simply mark as deleted the corresponding entries without affecting the rest of the structures. So for example if you delete a vertex with these structures without checking that all the faces incident on it have been removed you could create a non consistent situation.
Similarly, but less dangerously, when you delete a face its vertices are left around so at the end you can have unreferenced floating vertices.
The following snippet of code delete a few faces from an icosahedron.
\dontinclude trimesh_allocate.cpp
\skip Icosahedron
\until face[3]
After such a deletion the vector of the faces still contains 20 elements (number of faces of an icosahedron) but the \c m.FN() function correctly reports 18 faces.
Therefore if your algorithm performs deletions it happens that the size of a container could be different from the number of valid element of your meshes:
\code
Allocator<MyMesh>::DeleteVertex(m,v);
Allocator<MyMesh>::DeleteFace(m,v);
m.vert.size() != m.VN()
m.face.size() != m.FN()
\endcode
If your algorithm performs deletion the size of a container could be different from the number of valid element of your meshes (e.g.:
\code
m.vert.size() != m.vn
m.face.size() != m.fn
\endcode
Therefore when you scan the containers of vertices and faces you could encounter deleted elements so you should take care with a simple !IsD() check:
\code
MyMesh::FaceIterator vi;
for(fi = m.face.begin(); vi!=m.face.end(); ++fi )
if(!(*fi).IsD()) // <---- Check added
{
MyMesh::CoordType b = vcg::Barycenter(*fi);
}
\endcode
\dontinclude trimesh_allocate.cpp
\skip FaceIterator
\until }
\until }
In some situations, particularly when you have to loop many many times over the element of the mesh without deleting/creating anything, it can be practical and convenient to get rid of deleted elements by explicitly calling the two garbage collecting functions:
\code
Allocator<MyMesh>::CompactVertexVector(m);
Allocator<MyMesh>::CompactFaceVector(m);
vcg::tri::Allocator<MyMesh>::CompactVertexVector(m);
vcg::tri::Allocator<MyMesh>::CompactFaceVector(m);
\endcode
After calling these function it is safe to not check the IsD() state of every element and always holds that:
\code
m.vert.size() == m.vn
m.face.size() == m.fn
m.vert.size() == m.VN()
m.face.size() == m.FN()
\endcode
Note that if there are no deleted elements in your mesh, the compactor functions returns immediately.
\note If there are no deleted elements in your mesh, the compactor functions returns immediately (it just test that the container size match the number of the elements) so it is safe to call it before lenghty operations.
\note If you are messing around with deleted elements it is rather dangerous to loop over mesh elements using \c FN() or \c VN() as end guards in the for; the following code snipped is \b WRONG:
\dontinclude trimesh_allocate.cpp
\skip WRONG
\until }
\until }
How to copy a mesh
------------

View File

@ -126,6 +126,7 @@ public:
the pointers that can be changed when resizing the involved vectors of vertex or faces.
It can also be used to prevent any update of the various mesh fields
(e.g. in case you are building all the connections by hand as in a importer);
\sa \ref allocation
*/
template<class SimplexPointerType>
class PointerUpdater
@ -133,6 +134,10 @@ public:
public:
PointerUpdater(void) : newBase(0), oldBase(0), newEnd(0), oldEnd(0), preventUpdateFlag(false) { ; }
void Clear(){newBase=oldBase=newEnd=oldEnd=0;}
/*! \brief Update a pointer to an element of a mesh after a reallocation
The updating is correctly done only if this PointerUpdater have been passed to the corresponing allocation call. \sa \ref allocation
*/
void Update(SimplexPointerType &vp)
{
//if(vp>=newBase && vp<newEnd) return;
@ -143,6 +148,9 @@ public:
if(!remap.empty())
vp = newBase + remap[vp-newBase];
}
/*!
\brief return true if the allocation operation that initialized this PointerUpdater has caused a reallocation
*/
bool NeedUpdate() {if((oldBase && newBase!=oldBase && !preventUpdateFlag) || !remap.empty()) return true; else return false;}
SimplexPointerType newBase;
@ -303,7 +311,7 @@ public:
return last;// deve restituire l'iteratore alla prima faccia aggiunta;
}
/** Function to add n vertices to the mesh.
/** Function to add n edges to the mesh.
First wrapper, with no parameters
*/
static EdgeIterator AddEdges(MeshType &m, int n)
@ -312,7 +320,7 @@ public:
return AddEdges(m, n,pu);
}
/** Function to add n vertices to the mesh.
/** Function to add n edges to the mesh.
Second Wrapper, with a vector of vertex pointers to be updated.
*/
static EdgeIterator AddEdges(MeshType &m, int n, std::vector<EdgePointer*> &local_vec)