J. Andreas Bærentzen, January 2018 (revised February 2021)
The PyGEL Python library for geometry processing provides Python3 bindings for a subset of the features of the C++ based GEL library.
PyGEL is a Python package consisting of five modules:
hmesh
provides Manifold
which is a class that represents polygonal meshes using the halfedge representation. hmesh
also provides a slew of functions for manipulating polygonal meshes and the MeshDistance
class which makes it simple to compute the distance to a triangle mesh.graph
contains the Graph
class which is used for graphs: i.e. collections of vertices (in 3D) connected by edges. Unlike a Manifold
, a Graph
does not have to represent a surface. There are also some associated functions which may be useful: in particular, there is the LS_skeletonize
function which computes a curve skeleton from a Graph
and returns the result as a new Graph
. gl_display
provides the Viewer
class which makes it simple to visualize meshes and graphs.jupyter_display
makes it easy to use PyGEL in the context of a Jupyter Notebook. This module contains a function that allows you to create a widget for interactively visualizing a mesh or a graph in a Notebook. The feature is based on the Plotly library and it is possible to export the resulting notebooks to HTML while preserving the interactive 3D graphics in the notebook (as we will see below).spatial
contains the I3DTree
class which is simply a kD-tree specialized for mapping 3D points to integers - typically indices. Of course, scipy.spatial
has a more generic class, so this is perhaps not the most important part of PyGEL.This text provides a brief introduction to PyGEL. If you are planning to use PyGEL, please also cast a glance at the PyGEL Reference Documentation. All features of the API are concisely described in the reference.
PyGEL is on PyPi under the name PyGEL3D. Turned out PyGEL was taken by another project which I am sure is excellent and does something very different, so we still refer to PyGEL as PyGEL, but for the purpose of installation from PyPi, the package is called PyGEL3D. Maybe all you need to do is
> pip install PyGEL3D
Now, you may have to use pip3
(because Python3) and it might be necessary to install with --user
. However, it also depends on which platform you are on. GEL and PyGEL are compiled automatically for Windows, MacOS and Linux (specifically Ubuntu 18.04) using GitHub actions every time we push to GitHub. However, it is hard to make sure that the resulting binaries work without fail on every computer, so you may be out of luck with PyPI.
In that case, GEL and PyGEL can easily be compiled using CMake. Clone the github repository and be on your (hopefully merry) way. Instructions are in the README.
It is recommended that you either use the standard distribution from Python.org or the Anaconda distribution. Anaconda may make it a little easier to install the packages that you need and it also has the advantage that you can install everything as a user with no administrator priviliges. That being said, the regular Python distribution should also work well for PyGEL. If you use the standard distribution, Pip is the tool you would normally use to install new components.
Regardless of distribution, you need to have Plotly (for Python) installed. However, normally you don't have to worry because if you install either from PyPi or a version you built yourself it will ultimately be done with pip and this takes care of dependencies. If you plan to use PyGEL from a Jupyter notebook, you clearly need Jupyter notebook.
The GEL library was created by the author (Andreas Bærentzen) with significant contributions from colleagues at the Section for Visual Computing of the Computer Science and Applied Mathematics Department at The Technical University of Denmark. The library has been used for several research projects and as the basis for exercises in a long running course on Geometry Processing. The GEL library is also the recommended platform for solving problems from our book "Guide to Computational Geometry Processing". However, GEL is a C++ library and while that has not been a big limitation in the past, we wanted to offer a geometry processing course in a more general form -- and to undergraduates. In this context, it seemed prudent to switch to Python. While there are several other geometry processing libraries with Python bindings, it turned out to be surprisingly straight forward to use ctypes to develop Python bindings for GEL, and since none of the other options seemed to be a drop in replacement, we decided to create PyGEL.
The scope of PyGEL is a little narrower than the scope of GEL. For instance, PyGEL does not contain voxel grids or a vector matrix library for 2,3, and 4D vectors and matrices. To a large extent this is because C++ libraries often work best if they have few dependencies whereas this appears to be a bit less of a concern with Python where most of the packages we need can be installed with ease using e.g. pip. If you happen to have a strong interest in some of the choices that led to how PyGEL was developed, the process is documented here
Perhaps, the pivotal feature of GEL, and by extension PyGEL, can be said to be HMesh - the polygonal mesh representation. HMesh is halfedge based. Originally, the halfedge representation was chosen since it is more efficient than the older winged edge representation while also making connectivity information readily available. Furthermore, the halfedge representation makes it trivial to support polygons with arbitrary numbers of edges. From an implementation point of view, many operations are probably harder to implement than for simpler mesh representations, but at this point, the GEL code base seems fairly robust while offering a rich set of mesh manipulation operations.
An important concern (for teaching) was to be able to run PyGEL inside a Jupyter Notebook. It is probably best to use a programming editor or an IDE to develop code, but for presentation purposes, the ability provided by JN to mix code, output from code, and text is very hard to beat. One big stumbling block was that we wanted students to be able to show 3D models inside a Jupyter Notebook. Several frameworks ostensibly make this possible, but because of subtle bugs almost none of these do so in a way that allows the 3D model to survive into a static HTML page generated from the Notebook. One exception turned out to be Plotly. HTML pages generated from ipynb files using the Nbconvert program would actually retain interactive views of 3D models. Thus, the PyGEL package contains a module called jupyter_display
with a single function, display
, that converts a mesh into an interactive view, embedded in the notebook. One - absolutely maddening - caveat, though, is that LaTeX formulas in the Markdown text remain LaTeX code when you do that. There is some bug which causes MathJax to be unintentionally neutralized by Plotly. Hopefully, that will be resolved, but for now the bug remains.
To use PyGEL you need to import the appropriate module. The code below starts by doing that, then it loads a mesh (i.e. a Manifold
) called m
, creates a Viewer
called viewer
, and, finally, it displays m
using viewer
. Note that the script below could be run from a Jupyter notebook but also whatever editor you might prefer or from an interactive Python shell.
from pygel3d import hmesh, gl_display as gl
m = hmesh.load("../data/bunnygtest.obj")
viewer = gl.Viewer()
viewer.display(m)
Having executed the code above, you should see a window displaying a 3D model of a bunny, and it is now possible to play with the mesh. You can rotate by pressing the left mouse button and dragging the mouse. You can zoom using the right mouse button, and if you hold shift the right mouse button pans instead of zooming. One thing that might be puzzling is that the display
function does not return. That is because the viewer is in the same thread of execution as the rest of your script: it is not running in parallel. However, if you want to return the script, all you have to do is to press ESC
inside the window. Doing so, you might notice that the image freezes but the window does not go away? That is still entirely as intended. We might want to return to the window after all - either to visualize a different mesh or simply to look a bit more at the one we have. To make the window active, just call event_loop
:
viewer.event_loop()
You will have noticed that the window with the bunny came alive again. Hit ESC
one more time to exit the viewer. If you really want the window to go away, you can either wait for the entire script to terminate or explicitly delete the object like so:
del viewer
The display
function in Viewer
has a number of parameters which were not involved in the simple example above. In the example, display
was called only with the mesh m
. Instead of m
we could have called display
with a Graph
. The Viewer
is happy to display both types of object even at the same time. However, it is much more full fledged as a mesh viewer than a graph viewer.
mode
: a single character that determines how the mesh is visualized:smooth
: if True (which is default) we use vertex normals. Otherwise, face normals.bg_col
: background color. The default is dark grey [0.3,0.3,0.3]data
: per vertex data for visualization. This is either a scalar or vector field. It is ignored unless one of these two visualization modes is selected. The default here is None
.reset_view
: if False
view is as left in the previous display call. If
True
, the view is reset to the default. This can be useful if you make changes to a mesh and then want to return to the view you had, and the default is indeed False
.once
: if True we immediately exit the event loop and return. However,
the window stays and if the event loop is called from this or any
other viewer, the window will still be responsive. Default is False
.If you are using PyGEL from within a Jupyter Notebook you may find it absolutely intolerable to use the gl_display.Viewer
. At least I find that the Jupyter widgets for visualizing meshes are far more useful in Jupyter and some of my students have insisted that the gl_display.Viewer
is broken. It is not, but it can feel that way when it stops the execution of the cells in your notebook.
So let us talk about the jupyter_display
module. Sometimes, we do want the ability to show a mesh in a Jupyter Notebook. To do so, we need a different visualization tool. As now mentioned a few times, such a tool has been developed. It is based on the visualization library called Plotly which works quite well in conjunction with Jupyter Notebooks. Displaying a mesh with jupyter_display
is as easy as with gl_display
but the features are fewer and a bit different. In the following example, jupyter_display
is used to visualize a mesh using wireframe (default) and flat shading. Perhaps, it behooves me to also explain the set_export_mode
function. This function must be called if you want to be able to export the notebook to HTML with the interactive visualization widgets preserved! Also, it appears that if you do not call it, display
must be the last thing you do in a cell. I hate these gotchas, but it is a rather tall software stack we are working on, and it gets a bit wobbly.
from pygel3d import jupyter_display as jd
jd.set_export_mode(True)
jd.display(m, smooth=False)