Commit afe9f5ed authored by Alexander Dielen's avatar Alexander Dielen

added updated versions of the old python tutorials

parent 715432b0
Pipeline #6208 passed with stages
in 1 minute and 38 seconds
......@@ -20,6 +20,9 @@
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
sys.path.insert(0, os.path.abspath('../build'))
# -- General configuration ------------------------------------------------
......@@ -122,7 +125,8 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# html_theme = 'alabaster'
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
......@@ -132,6 +136,7 @@ html_theme = 'alabaster'
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
html_theme_path = ["_themes", ]
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
......
***************************
The Halfedge Data Structure
***************************
This section describes the underlying data structure that is used to store the
mesh vertices, edges and faces, as well as their connectivity information.
There are many popular data structures used to represent polygonal meshes. For
a detailed comparison of them refer to the papers at the end of this section.
The data structure used in this project is the so called halfedge data
structure. While face-based structures store their connectivity in faces
referencing their vertices and neighbors, edge-based structures put the
connectivity information into the edges. Each edge references its two vertices,
the faces it belongs to and the two next edges in these faces. If one now splits
the edges (i.e. an edge connecting vertex A and vertex B becomes two directed
halfeges from A to B and vice versa) one gets a halfedge-based data structure.
The following figure illustrates the way connectivity is stored in this
structure:
.. image:: img/halfedge_structure.gif
:width: 225
:align: center
- Each vertex references one outgoing halfedge, i.e. a halfedge that starts at
this vertex (1).
- Each face references one of the halfedges bounding it (2).
- Each halfedge provides a handle to
- the vertex it points to (3),
- the face it belongs to (4),
- the next halfedge inside the face (ordered counter-clockwise) (5),
- the opposite halfedge (6),
- the previous halfedge in the face (7).
Having these links between the items, it is now possible to circulate around a
face in order to enumerate all its vertices, halfedges, or neighboring faces.
When starting at a vertex' halfedge and iterating to the opposite of its
previous one, one can easily circulate around this vertex and get all its
one-ring neighbors, the incoming/outgoing halfedges, or the adjacent faces.
All this functionality is encapsulated into the so-called circulators,
described in :ref:`iterators`.
.. note:: In order to efficiently classify a boundary vertex, the outgoing
halfedge of these vertices must be a boundary halfedge (see
:func:`~openmesh.TriMesh.is_boundary`). Whenever you modify the topology
using low-level topology changing functions, be sure to guarantee this
behaviour (see :func:`~openmesh.TriMesh.adjust_outgoing_halfedge`).
While the halfedge-based structures usually consume more memory than their
face-based counter-parts they have the following important advantages:
- It is easy to mix faces of arbitrary vertex count in one mesh.
- We now have an explicit representation of vertices, faces, and
edges/halfedges. This becomes extremely useful if one has to store data per
edge/halfedge since this can easily be modelled by member variables of these
types.
- Circulating around a vertex in order to get its one-ring neighbors is an
important operation for many kinds of algorithms on polygonal meshes. For
face-based structures this leads to many if-then branchings, the halfedge
structure provides this funcionality without conditional branching in
constant time.
***********************************
First Steps: Creating a simple mesh
***********************************
This section demonstrates how to create a new mesh, add some vertices and faces
to it and then modify the newly inserted points.
First, we will import the openmesh and numpy modules:
.. code:: python
import openmesh as om
import numpy as np
Next, we can create an empty mesh:
.. code:: python
mesh = om.TriMesh()
OpenMesh provides two mesh types: One for polygonal meshes (PolyMesh) and one
for triangle meshes (TriMesh). You should use triangle meshes whenever
possible, since they are usually more efficient. In addition, some algorithms
are only implemented for triangle meshes while triangle meshes inherit the full
functionality of polygonal meshes.
Now that we have our empty mesh object we can add a couple of vertices:
.. code:: python
vh0 = mesh.add_vertex([0, 1, 0])
vh1 = mesh.add_vertex([1, 0, 0])
vh2 = mesh.add_vertex([2, 1, 0])
vh3 = mesh.add_vertex([0,-1, 0])
vh4 = mesh.add_vertex([2,-1, 0])
The :func:`~openmesh.TriMesh.add_vertex` member function takes numpy arrays with
shape (3,) as point coordinates and returns a handle to the newly inserted
vertex. As shown in the code above we can also pass lists with 3 elements as
point coordinates. The lists are automatically converted to numpy arrays.
In order to add a new face to our mesh we have to call
:func:`~openmesh.TriMesh.add_face`. This function takes the handles of the
vertices that make up the new face and returns a handle to the newly inserted
face:
.. code:: python
fh0 = mesh.add_face(vh0, vh1, vh2)
fh1 = mesh.add_face(vh1, vh3, vh4)
fh2 = mesh.add_face(vh0, vh3, vh1)
We can also pass a list of vertex handles to :func:`~openmesh.TriMesh.add_face`:
.. code:: python
vh_list = [vh2, vh1, vh4]
fh3 = mesh.add_face(vh_list)
Our mesh should now look like this:
.. code:: python
# 0 ==== 2
# |\ 0 /|
# | \ / |
# |2 1 3|
# | / \ |
# |/ 1 \|
# 3 ==== 4
We can access the point coordinates of each vertex by calling
:func:`~openmesh.TriMesh.point`. This member function takes a vertex handle and
returns a numpy array with shape (3,):
.. code:: python
point = mesh.point(vh0)
We can also get an array containing all points of a mesh by calling
:func:`~openmesh.TriMesh.points`. The returned array has shape (n, 3), where n
is the number of vertices:
.. code:: python
point_array = mesh.points()
The latter is useful if we want to update all points of a mesh at once. For
example, we can translate our mesh along the x-axis like this:
.. code:: python
point_array += np.array([1, 0, 0])
The arrays returned by :func:`~openmesh.TriMesh.point` and
:func:`~openmesh.TriMesh.points` both reference the underlying mesh data. This
means that changes made to either one of these arrays affect the original mesh.
The complete source for this section looks like this:
.. code:: python
import openmesh as om
import numpy as np
mesh = om.TriMesh()
# add a a couple of vertices to the mesh
vh0 = mesh.add_vertex([0, 1, 0])
vh1 = mesh.add_vertex([1, 0, 0])
vh2 = mesh.add_vertex([2, 1, 0])
vh3 = mesh.add_vertex([0,-1, 0])
vh4 = mesh.add_vertex([2,-1, 0])
# add a couple of faces to the mesh
fh0 = mesh.add_face(vh0, vh1, vh2)
fh1 = mesh.add_face(vh1, vh3, vh4)
fh2 = mesh.add_face(vh0, vh3, vh1)
# add another face to the mesh, this time using a list
vh_list = [vh2, vh1, vh4]
fh3 = mesh.add_face(vh_list)
# 0 ==== 2
# |\ 0 /|
# | \ / |
# |2 1 3|
# | / \ |
# |/ 1 \|
# 3 ==== 4
# get the point with vertex handle vh0
point = mesh.point(vh0)
# get all points of the mesh
point_array = mesh.points()
# translate the mesh along the x-axis
point_array += np.array([1, 0, 0])
******************
Garbage Collection
******************
TODO
......@@ -10,7 +10,30 @@ Contents:
.. toctree::
:maxdepth: 2
:caption: Tutorial
install
firststeps
iterators
properties
indexarrays
garbagecollection
readwrite
.. toctree::
:maxdepth: 2
:caption: Advanced Topics
datastructure
.. toctree::
:maxdepth: 2
:caption: Class Reference
trimesh
polymesh
Indices and tables
......@@ -19,4 +42,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
************
Index Arrays
************
TODO
************
Installation
************
TODO
Running the tests
#################
In your cmake build directory (e.g. build/):
.. code:: python
ctest --verbose
.. _iterators:
*************************
Iterators and Circulators
*************************
This section demonstrates how to use mesh iterators and circulators. The
example outputs on this page are based on the mesh from the previous section,
which looks like this:
.. code:: python
# 0 ==== 2
# |\ 0 /|
# | \ / |
# |2 1 3|
# | / \ |
# |/ 1 \|
# 3 ==== 4
Iterators
#########
Iterators make it possible to enumerate the items of a mesh. For example, the
code below iterates over all vertices of a mesh:
.. code:: python
for vh in mesh.vertices():
print(vh.idx())
Using the mesh from the previous section, this will produce the following
output:
.. code:: python
0
1
2
3
4
.. note:: Iterators and circulators return handles to mesh items instead of
the items themself. For example, the vertex iterator returns vertex handles
instead of actual vertices/points. You can access the vertex coordinates
by calling :func:`~openmesh.TriMesh.point` with the appropriate vertex
handle.
We can also iterate over all halfedges, edges and faces by calling
:func:`~openmesh.TriMesh.halfedges`, :func:`~openmesh.TriMesh.edges` and
:func:`~openmesh.TriMesh.faces` respectively:
.. code:: python
# iterate over all halfedges
for heh in mesh.halfedges():
print(heh.idx())
# iterate over all edges
for eh in mesh.edges():
print(eh.idx())
# iterate over all faces
for fh in mesh.faces():
print(fh.idx())
Circulators
###########
Circulators provide the means to iterate over items adjacent to another item.
For example, to iterate over the 1-ring of a vertex we can call
:func:`~openmesh.TriMesh.vv`, which is short for vertex-vertex circulator, and
pass the handle of the center vertex:
.. code:: python
for vh in mesh.vv(vh1):
print(vh.idx())
Using the mesh from the previous section, this will produce the following
output:
.. code:: python
4
3
0
2
We can also iterate over the adjacent halfedges, edges and faces of a vertex:
.. code:: python
# iterate over all incoming halfedges
for heh in mesh.vih(vh1):
print(heh.idx())
# iterate over all outgoing halfedges
for heh in mesh.voh(vh1):
print(heh.idx())
# iterate over all adjacent edges
for eh in mesh.ve(vh1):
print(eh.idx())
# iterate over all adjacent faces
for fh in mesh.vf(vh1):
print(fh.idx())
To iterate over the items adjacent to a face we can use the following functions:
.. code:: python
# iterate over the face's vertices
for vh in mesh.fv(fh0):
print(vh.idx())
# iterate over the face's halfedges
for heh in mesh.fh(fh0):
print(heh.idx())
# iterate over the face's edges
for eh in mesh.fe(fh0):
print(eh.idx())
# iterate over all edge-neighboring faces
for fh in mesh.ff(fh0):
print(fh.idx())
********
PolyMesh
********
.. autoclass:: openmesh.PolyMesh
:members:
**********
Properties
**********
TODO
*************
I/O Functions
*************
OpenMesh provides two functions that read and write meshes from and to files:
:func:`~openmesh.read_mesh` and :func:`~openmesh.write_mesh`
.. code:: python
import openmesh as om
mesh = om.TriMesh()
om.read_mesh(mesh, "bunny.ply")
# modify mesh ...
om.write_mesh(mesh, "bunny.ply")
The file type is automatically deduced from the file extension. OpenMesh
currently supports five file types: .obj, .off, .ply, .stl and .om
The behaviour of the I/O functions can be fine-tuned by passing an instance of
the :class:`~openmesh.Options` class to either :func:`~openmesh.read_mesh` or
:func:`~openmesh.write_mesh`. When reading a file the options are used as hints,
i.e. depending on the format we can help the reader to interpret the data
correctly. When writing a file the options determine whether or not to use the
binary variant of the respective file format and the desired byte-ordering.
.. code:: python
import openmesh as om
mesh = om.TriMesh()
# hint: read vertex normals
options = om.Options()
options += om.Options.VertexNormal
om.read_mesh(mesh, "bunny.ply", options)
# write binary file
options = om.Options()
options += om.Options.Binary
om.write_mesh(mesh, "bunny_binary.ply", options)
The :class:`~openmesh.Options` class controls the behaviour of the I/O functions
by means of enabled/disabled bits in a bitset. The following list contains all
available option bits:
- mode bits - control binary reading/writing
- Options.Binary
- Options.MSB
- Options.LSB
- Options.Swap (MSB|LSB)
- property bits - controls which standard properties to read/write
- Options.VertexNormal
- Options.VertexTexCoord
- Options.VertexColor
- Options.FaceNormal
- Options.FaceColor
- Options.ColorAlpha
- Options.ColorFloat
Multiple options can be combined using simple arithmetic:
.. code:: python
options = om.Options()
options += om.Options.VertexNormal
options += om.Options.VertexColor
*******
TriMesh
*******
.. autoclass:: openmesh.TriMesh
:members:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment