Improved documentation about allocation. Added sample on allocation.
This commit is contained in:
parent
d784bebe07
commit
fc5a0216ac
|
@ -1,6 +1,7 @@
|
|||
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = trimesh_base \
|
||||
trimesh_allocate \
|
||||
trimesh_attribute \
|
||||
trimesh_ball_pivoting \
|
||||
trimesh_closest \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
include(../common.pri)
|
||||
TARGET = trimesh_allocate
|
||||
SOURCES += trimesh_allocate.cpp ../../../wrap/ply/plylib.cpp
|
|
@ -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
|
||||
------------
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue