holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.64k stars 504 forks source link

Support vtkMoleculeMapper #2232

Open LunarLanding opened 3 years ago

LunarLanding commented 3 years ago

ALL software version info

holoviews : 1.14.2 panel: 0.11.1 vtk : 9.0.1 pyvista : 0.29.0 numpy : 1.20.2 python: 3.9.2 jupyter : 3.0.12 OS python runs on: Ubuntu 18.04.5 browser : Firefox 88.0 OS browser runs on : Windows 10

Description of expected behavior and the observed behavior

Make vtkMoleculeMapper, with one atom; add it to a pyvista plot; render it via panel on a jupyter lab notebook Expected image of a sphere, instead get a blank canvas and a javascript error.

Complete, minimal, self-contained example code that reproduces the issue

# MoleculeMapper minimal test
import vtk
import panel as pn
pn.extension('vtk')

import numpy as np
import pyvista as pv

N=1
a=np.zeros((N,3))
b=pv.wrap(a)
ids=np.ones(N,dtype=np.uint8)
b['atomic_number']=ids
b['radii']=.1*np.ones(N,dtype=float)
b.set_active_scalars('atomic_number')
poly = b

ps2mf=vtk.vtkPointSetToMoleculeFilter()
ps2mf.SetInputData(poly)

moleculeMapper=vtk.vtkMoleculeMapper()
moleculeMapper.SetRenderAtoms(True)
moleculeMapper.SetAtomicRadiusTypeToCustomArrayRadius()
moleculeMapper.SetInputConnection(ps2mf.GetOutputPort())

mapper = moleculeMapper
mapper.Update()

pl = pv.Plotter(notebook=True)
pl.add_actor(mapper);

pl.save_graphic('a.svg')

from IPython.display import Image,SVG
SVG('a.svg')

image

pv_plot = pl
render_window = pv_plot.ren_win

renderer = pv_plot.renderer

pane = pn.pane.VTK(render_window, width=500, height=500, sizing_mode="stretch_both")

pane

(blank output)

Stack traceback and/or browser JavaScript console output

Uncaught TypeError: t.oglmapper is undefined
    traverseOpaquePass index.js:63
    traverse index.js:27
    traverse index.js:35
    traverse index.js:75
    traverseAllPasses index.js:1066
    u index.js:348
    handleAnimation index.js:501

Screenshots or screencasts of the bug in action

See code section.

Additional details

It seems panel's serializer for vtk does not support vtkMolecule or vtkMoleculeMapper. If this is too specific a niche usage, the crucial part of this mapper, performance wise, is using vtkSphereMapper, which renders many spheres using a few triangles for each; having support for it would already cover my use-case. I can make a new issue for it if necessary.

vtkMoleculeMapper is implemented in vtk.js by default.

vtk from conda-forge has vtkMolecule and vtkMoleculeMapper classes, however the opengl implementation is not included by default. Therefore in MVE I show the intended output via a screenshot obtained with pyvista.

My use-case calls for rendering efficiently up to 100k spheres; vtkMoleculeMapper uses vtkSphereMapper to render each sphere with a few triangles, which reaches the necessary performance for interactivity. I tested this by editing the input range on their live example here https://kitware.github.io/vtk-js/examples/SphereMapper.html#Live-example

xavArtley commented 3 years ago

As a workaround could you use a vtkGlyph3d mapper? https://github.com/holoviz/panel/pull/2027

LunarLanding commented 3 years ago

@xavArtley vtkSphereMapper uses one triangle per sphere (in shader code here https://github.com/Kitware/vtk-js/blob/master/Sources/Rendering/OpenGL/SphereMapper/index.js ) and renders spheres correctly as long as the sphere is fully visible on screen. vtkGlyph3d with a coarse resolution of 8 for both phi and theta will use 64 faces per sphere.

The fps goes from 3fps to 40 fps, details follow below, but using vtkGlyph3D for this does not work.

PS: support for glyph3D mapper was very helpful, and it fills many use cases; I'm trying to expand the usage of pane/vtkjs to some new problems, which lead to this issue.

Measured avg fps on a surface pro 4 (integrated graphics) while rotating and zooming back and forth.

I can predict a lower bound for the performance of vtkSphereMapper by using resolution = 1 for the sphere glyph, which uses 6 faces per glyph. Using the live example gives worse results (with the plane at xy, down from 40fps at (0,0,-1) to 6fps along the (1,1,0) direction), which I attribute to the size of its particles varying orders of magnitude above the minimum radius, which is not the use case of molecular renderings.

vtkGlyph3d with resolution 8 will yield 3fps. Dropping resolution to 5 gives 7fps which is barely ok but now spheres will look like pentagons up close. Dropping resolution to 1 gives 40fps.

Resolution 8: image

Resolution 5: image

Test code:

import pyvista as pv
import vtk
import panel as pn
pn.extension('vtk')

def make_glyph(N):
    Nt = N
    Np = N
    print(f"~ # of faces in glyph:{Nt*Np=}")
    sphereSource2 = vtk.vtkSphereSource()
    sphereSource2.SetThetaResolution(Nt)
    sphereSource2.SetPhiResolution(Np)
    return sphereSource2

def make_grid(N):
    sphereSource = vtk.vtkSphereSource()
    Nr = int(N**.5)
    Nt = Nr
    Np = Nr
    sphereSource.SetThetaResolution(Nt)
    sphereSource.SetPhiResolution(Np)
    source = sphereSource
    print(f"~ # of glyphs:{Nt*Np=}")

    return source

def make_glyph3dMapper(source,glyph):

    mapper = vtk.vtkGlyph3DMapper();

    mapper.SetInputConnection(source.GetOutputPort())
    mapper.SetSourceConnection(glyph.GetOutputPort())
    mapper.SetScaleFactor(.01)
    mapper.Update()

    return mapper

def make_pane(mapper):

    actor = vtk.vtkActor();
    actor.SetMapper(mapper);

    ren = vtk.vtkRenderer()
    ren.AddActor(actor)
    ren.ResetCamera()

    win = vtk.vtkRenderWindow()
    win.AddRenderer(ren)
    return pn.pane.VTK(win, width=500, height=500)

# resolution = 8 # 3fps
# resolution = 5 # 7fps
resolution = 1 # 40fps
glyph = make_glyph(resolution)
glyph_mapper = vtk.vtkPolyDataMapper();
glyph_mapper.SetInputConnection(glyph.GetOutputPort())
glyph_mapper.Update()

# N = 100
N = int(100e3)
grid = make_grid(N)
pn.Column(*[
    make_pane(glyph_mapper),
    make_pane(make_glyph3dMapper(grid,glyph)),
])
xavArtley commented 3 years ago

If understand correctly you would like to use the vtkSphereMapper of vtk.js because it render sphere more efficiently.

The problem is there is no equivalent class on VTK side (vtkSphereMapper does not exist). vtkSphereMapper is just a class around vtkMapper with a radius parameter and a scaleArray, but I don't see wich VTK class could be serialized correctly

On the other side vtkMoleculeMapper does not exist on vtk.js, there is a vtkMolecule class but I don't see how to use it.

xavArtley commented 3 years ago

May be I can try to serialize a Vtk moleculemapper into a vtkjs spheremapper and stickmapper

LunarLanding commented 3 years ago

I wasn't aware vtkSphereMapper was exclusive to vtk-js. Mapping vtkMoleculeMapper to SphereMapper and StickMapper on vtk-js would solve my use-case. But I understand how that would be harder to implement/maintain. Perhaps I can ask vtk-js to have a MoleculeMapper..

On Sat, 24 Apr 2021, 09:02 Xavier Artusi, @.***> wrote:

May be I can try to serialize a Vtk moleculemapper into a vtkjs spheremapper and stickmapper

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/holoviz/panel/issues/2232#issuecomment-826054208, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABB4J6TNPTHNZPR6DLHM6MDTKJ3JTANCNFSM43OQRMPQ .