Skip to content

Jupyter Display Module

jupyter_display

This is a module with a function, display, that provides functionality for displaying a Manifold or a Graph as an interactive 3D model in a Jupyter Notebook. It is based on plotly.

set_export_mode

set_export_mode(_exp_mode: bool = True)

Deprecated and no longer needed. display now always returns a plain go.Figure, which renders correctly in Jupyter (regardless of position in a cell), in Marimo, and when a notebook is exported to HTML. This function is kept only for backwards compatibility and does nothing.

Source code in pygel3d/jupyter_display.py
def set_export_mode(_exp_mode: bool = True):
    """ Deprecated and no longer needed. display now always returns a plain
    go.Figure, which renders correctly in Jupyter (regardless of position in
    a cell), in Marimo, and when a notebook is exported to HTML. This function
    is kept only for backwards compatibility and does nothing.
    """

display

display(m: Manifold | Graph, wireframe: bool = True, smooth: bool = True, data: ArrayLike | None = None)

The display function shows an interactive presentation of the Manifold, m, inside a Jupyter Notebook. wireframe=True means that a wireframe view of the mesh is superimposed on the 3D model. If smooth=True, the mesh is rendered with vertex normals. Otherwise, the mesh is rendered with face normals. If data=None, the mesh is shown in a light grey color. If data contains an array of scalar values per vertex, these are mapped to colors used to color the mesh. Finally, note that m can also be a Graph. In that case the display function just draws the edges as black lines.

Source code in pygel3d/jupyter_display.py
def display(m: Manifold | Graph, wireframe: bool = True, smooth: bool = True, data: ArrayLike | None = None):
    """ The display function shows an interactive presentation of the Manifold, m, inside
        a Jupyter Notebook. wireframe=True means that a wireframe view of the mesh is
        superimposed on the 3D model. If smooth=True, the mesh is rendered with vertex
        normals. Otherwise, the mesh is rendered with face normals. If data=None, the
        mesh is shown in a light grey color. If data contains an array of scalar values
        per vertex, these are mapped to colors used to color the mesh. Finally, note that
        m can also be a Graph. In that case the display function just draws the edges as
        black lines. """
    mesh_data = []
    if isinstance(m, Manifold):
        xyz = array([ p for p in m.positions()])
        m_tri = Manifold(m)
        triangulate(m_tri, clip_ear=False)
        ijk = array([[ idx for idx in m_tri.circulate_face(f,'v')] for f in m_tri.faces()])
        mesh = go.Mesh3d(x=xyz[:,0],y=xyz[:,1],z=xyz[:,2],
                i=ijk[:,0],j=ijk[:,1],k=ijk[:,2],color='#dddddd',flatshading=not smooth)
        if data is not None:
            mesh['intensity'] = data
            mesh['contour'] = {'show': True, 'color': '#ff0000'}
        mesh_data += [mesh]
        if wireframe:
            pos = m.positions()
            xyze = []
            for h in m.halfedges():
                if h < m.opposite_halfedge(h):
                    p0 = pos[m.incident_vertex(m.opposite_halfedge(h))]
                    p1 = pos[m.incident_vertex(h)]
                    xyze.append(array(p0))
                    xyze.append(array(p1))
                    xyze.append(array([None, None, None]))
            xyze = array(xyze)
            trace1=go.Scatter3d(x=xyze[:,0],y=xyze[:,1],z=xyze[:,2],
                       mode='lines',
                       line=dict(color='rgb(125,0,0)', width=1),
                       hoverinfo='none')
            mesh_data += [trace1]
    elif isinstance(m, Graph):
        pos = m.positions()
        xyze = []
        for v in m.nodes():
            for w in m.neighbors(v):
                if v < w:
                    p0 = pos[v]
                    p1 = pos[w]
                    xyze.append(array(p0))
                    xyze.append(array(p1))
                    xyze.append(array([None, None, None]))
        xyze = array(xyze)
        trace1=go.Scatter3d(x=xyze[:,0],y=xyze[:,1],z=xyze[:,2],
                   mode='lines',
                   line=dict(color='rgb(0,0,0)', width=1),
                   hoverinfo='none')
        mesh_data += [trace1]


    lyt = go.Layout(width=850,height=800)
    lyt.scene.aspectmode="data"
    return go.Figure(mesh_data,lyt)

The jupyter_display module provides visualization tools for Jupyter notebooks using Plotly, enabling interactive 3D graphics that can be exported to HTML.

Display Function

The main function for displaying geometry in Jupyter notebooks:

import pygel3d.jupyter_display as jd
import pygel3d.hmesh as hmesh

# Load mesh
m = hmesh.load("model.obj")

# Display in notebook
jd.display(m, smooth=True, wireframe=False)

Function Parameters

display(obj, **kwargs)

  • obj: Manifold or Graph object to display
  • smooth: Use smooth shading (default: True)
  • wireframe: Show wireframe (default: False)
  • color: Mesh color as string or RGB list (default: 'lightblue')
  • width: Figure width in pixels (default: 800)
  • height: Figure height in pixels (default: 600)

Features

Interactive 3D Widgets

  • Rotate, pan, and zoom with mouse
  • Hover to see coordinates
  • Camera controls in toolbar
  • Full Plotly interactivity

HTML Export

  • Notebooks can be exported to HTML
  • 3D visualizations remain interactive
  • Perfect for assignments and presentations
  • Works in nbviewer and GitHub

Google Colab Support

  • Fully compatible with Google Colab
  • No additional setup required
  • Same functionality as local Jupyter

Example Usage

Basic Display

import pygel3d.jupyter_display as jd
import pygel3d.hmesh as hmesh

# Load mesh
m = hmesh.load("bunny.obj")

# Display
jd.display(m)

Custom Styling

import pygel3d.jupyter_display as jd
import pygel3d.hmesh as hmesh

# Load mesh
m = hmesh.load("model.obj")

# Display with custom appearance
jd.display(m, 
           smooth=True,
           wireframe=True,
           color='coral',
           width=1000,
           height=800)

Multiple Objects

import pygel3d.jupyter_display as jd
import pygel3d.hmesh as hmesh

# Load multiple meshes
m1 = hmesh.load("model1.obj")
m2 = hmesh.load("model2.obj")

# Display separately
print("Model 1:")
jd.display(m1, color='red')

print("Model 2:")
jd.display(m2, color='blue')

Color by Attribute

import pygel3d.jupyter_display as jd
import pygel3d.hmesh as hmesh
import numpy as np

# Load mesh
m = hmesh.load("model.obj")

# Compute per-vertex attribute (e.g., height)
positions = m.positions()
z_coords = [positions[3*i+2] for i in range(m.no_vertices())]

# Display with color mapping
# Note: For custom attribute coloring, you may need to create
# a custom Plotly figure or use the hmesh scalar field functions
jd.display(m)

Displaying Graphs

import pygel3d.jupyter_display as jd
import pygel3d.graph as graph

# Load graph
g = graph.load("skeleton.graph")

# Display
jd.display(g, color='green')

Jupyter Notebook Workflow

Setup Cell

# Install if needed
!pip install PyGEL3D plotly

# Import modules
import pygel3d.hmesh as hmesh
import pygel3d.jupyter_display as jd

Processing and Visualization

# Load mesh
m = hmesh.load("input.obj")

# Show original
print("Original mesh:")
jd.display(m)

# Process
hmesh.cc_smooth(m)
hmesh.triangulate(m)

# Show result
print("Processed mesh:")
jd.display(m, color='lightgreen')

Comparison View

# Load two meshes
m1 = hmesh.load("before.obj")
m2 = hmesh.load("after.obj")

# Display side by side
from IPython.display import display, HTML

display(HTML("<h3>Before</h3>"))
jd.display(m1, width=400)

display(HTML("<h3>After</h3>"))
jd.display(m2, width=400)

Google Colab Setup

# First cell: Install dependencies
!apt-get install libgl1 libglu1
!pip install PyGEL3D plotly

# Import modules
import pygel3d.hmesh as hmesh
import pygel3d.jupyter_display as jd

# Upload file (if needed)
from google.colab import files
uploaded = files.upload()

# Load and display
m = hmesh.load(list(uploaded.keys())[0])
jd.display(m)

Tips and Best Practices

Performance

  • Large meshes may be slow in the browser
  • Consider simplification for complex models
  • Use decimation for very large meshes

Visualization

  • Smooth shading is better for organic models
  • Wireframe helps see topology
  • Light colors work well on default backgrounds

Notebooks

  • Add markdown cells to explain each step
  • Use clear section headings
  • Include parameter descriptions
  • Export to HTML for sharing

Export Quality

  • Ensure cells are executed before export
  • Test exported HTML in browser
  • Check that 3D widgets are interactive
  • Use "Trust Notebook" if needed

Plotly Integration

The module uses Plotly, so you can access advanced Plotly features:

import plotly.graph_objects as go
import pygel3d.jupyter_display as jd
import pygel3d.hmesh as hmesh

# For advanced customization, you may need to work
# directly with Plotly's mesh3d objects
# See Plotly documentation for details

Troubleshooting

Widget Not Displaying

  • Ensure Plotly is installed: pip install plotly
  • Restart kernel and re-run cells
  • Check browser console for errors

Export Issues

  • Use "File > Download as > HTML" in Jupyter
  • Ensure all cells are executed
  • Check that notebook is trusted

Performance Issues

  • Simplify large meshes before display
  • Reduce figure size (width/height)
  • Close unused notebooks