Failure when picking cells
cardinalgeo opened this issue · comments
Describe the bug
Picking cells in a simple tube filter, applied to a two-line-segment polyline, sometimes fails inexplicably.
To Reproduce
Steps to reproduce the behavior:
- Run the script that follows
- Click on the actor to "pick" its cells
- Inspect the viewer and command line output; when pick is successful, a tooltip appears with the data value corresponding to the cell and the last output on the command line shows a value > -1. when the pick is unsuccessful, no tooltip appears and the last output on the command line shows a value = -1.
Code
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify, html, trame, vtk as vtk_widgets
from vtkmodules.vtkFiltersCore import vtkTubeFilter
from vtk import reference, vtkGenericCell
import vtk.util.numpy_support as vtknumpy
import vtk
import numpy as np
# -----------------------------------------------------------------------------
# Trame
# -----------------------------------------------------------------------------
server = get_server()
state, ctrl = server.state, server.controller
# -----------------------------------------------------------------------------
# Constants
# -----------------------------------------------------------------------------
VIEW_INTERACT = [
{"button": 1, "action": "Rotate"},
{"button": 2, "action": "Pan"},
{"button": 3, "action": "Zoom", "scrollEnabled": True},
{"button": 1, "action": "Pan", "alt": True},
{"button": 1, "action": "Zoom", "control": True},
{"button": 1, "action": "Pan", "shift": True},
{"button": 1, "action": "Roll", "alt": True, "shift": True},
]
# -----------------------------------------------------------------------------
# Generate dataset
# -----------------------------------------------------------------------------
points = vtk.vtkPoints()
points.SetNumberOfPoints(4)
line = vtk.vtkLine()
lines = vtk.vtkCellArray()
# create first line segment
points.SetPoint(0, 0, 0, 0)
line.GetPointIds().SetId(0, 0)
points.SetPoint(1, 1, 1, 1)
line.GetPointIds().SetId(1, 1)
lines.InsertNextCell(line)
# create second line segment
points.SetPoint(2, 1, 1, 1)
line.GetPointIds().SetId(0, 2)
points.SetPoint(3, 2, 2, 2)
line.GetPointIds().SetId(1, 3)
lines.InsertNextCell(line)
linesPolyData = vtk.vtkPolyData()
linesPolyData.SetPoints(points)
linesPolyData.SetLines(lines)
vtkfields= vtknumpy.numpy_to_vtk([0,1])
vtkfields.SetName("data")
vtkfields.SetNumberOfComponents(1)
linesPolyData.GetCellData().AddArray(vtkfields)
# -----------------------------------------------------------------------------
# VTK pipeline
# -----------------------------------------------------------------------------
lines_tubes = vtkTubeFilter()
lines_tubes.SetInputData(linesPolyData)
radius = 2
lines_tubes.SetRadius(radius)
n_of_sides = 6
lines_tubes.SetNumberOfSides(n_of_sides)
lines_tubes.Update()
lines_polydata = lines_tubes.GetOutput()
state.lines = None
# Extract fieldParameters
fieldParameters = {"data": {"range": [0, 1]}}
cd = linesPolyData.GetCellData()
nb_arrays = cd.GetNumberOfArrays()
# -----------------------------------------------------------------------------
# Web App setup
# -----------------------------------------------------------------------------
state.update(
{
# Fields available
"fieldParameters": fieldParameters,
# picking controls
"pickingMode": "click",
"modes": [
{"value": "click", "icon": "mdi-cursor-default-click-outline"},
],
"field": "data",
# Picking feedback
"pickData": None,
"tooltip": "",
"pixel_ratio": 2,
# Meshes
"linesVisible": True,
}
)
# -----------------------------------------------------------------------------
# Callbacks
# -----------------------------------------------------------------------------
@state.change("pickData")
def update_tooltip(pickData, pixel_ratio, **kwargs):
state.tooltip = ""
state.tooltipStyle = {"display": "none"}
data = pickData
if data and data["representationId"] == "lines":
xyx = data["worldPosition"]
gen_cell = vtkGenericCell()
sub_id = reference(0)
pc = [0, 0, 0]
weights = [0,0,0,0,0,0,0,0,0,0,0,0]
face_idx = lines_polydata.FindCell(xyx, gen_cell, -1, 1, sub_id, pc, weights)
idx = int(np.floor(face_idx / n_of_sides))
print(" ")
print("New Click:")
print(f"xyx={xyx}, gen_cell={gen_cell}, sub_id={sub_id}, pc={pc}, weights={weights}")
print(f"index={idx}")
if idx > -1:
messages = []
for i in range(nb_arrays):
array = cd.GetAbstractArray(i)
if array.IsNumeric():
name = array.GetName()
if name == state.field:
value_str = f"{array.GetValue(idx):.2f}"
norm_str = ""
messages.append(f"{name}: {value_str} {norm_str}")
if len(messages):
x, y, z = data["displayPosition"]
state.tooltip = "\n".join(messages)
state.tooltipStyle = {
"position": "absolute",
"left": f"{(x / pixel_ratio )+ 10}px",
"bottom": f"{(y / pixel_ratio ) + 10}px",
"zIndex": 10,
"pointerEvents": "none",
}
# -----------------------------------------------------------------------------
# UI
# -----------------------------------------------------------------------------
with SinglePageLayout(server) as layout:
# Let the server know the browser pixel ratio
trame.ClientTriggers(mounted="pixel_ratio = window.devicePixelRatio")
with layout.content:
with vuetify.VContainer(
fluid=True, classes="pa-0 fill-height", style="position: relative;"
):
with vuetify.VCard(
style=("tooltipStyle", {"display": "none"}), elevation=2, outlined=True
):
with vuetify.VCardText():
html.Pre("{{ tooltip }}")
with vtk_widgets.VtkView(
ref="view",
picking_modes=("[pickingMode]",),
interactor_settings=("interactorSettings", VIEW_INTERACT),
click="pickData = $event"
):
with vtk_widgets.VtkGeometryRepresentation(
id="lines",
v_if="lines",
actor=("{ visibility: linesVisible }",),
):
vtk_widgets.VtkMesh("lines", dataset=lines_polydata, cell_arrays=["data"])
# -----------------------------------------------------------------------------
# Jupyter
# -----------------------------------------------------------------------------
def show(**kwargs):
from trame.app import jupyter
jupyter.show(server, **kwargs)
# -----------------------------------------------------------------------------
# CLI
# -----------------------------------------------------------------------------
if __name__ == "__main__":
server.start(port=8080)
Expected behavior
I expect that, upon clicking the tube filter actor, a cell within the actor will be picked. Inexplicably, the cell that is clicked will occasionally not be registered as picked. Furthermore, this "bad behavior" gets worse as the number of clicks continues (i.e., initially the proportion of clicks that result in a failed pick is low, but that increases with the cumulative number of clicks). The behavior described is exhibited in the movie below:
Screenshots
MRE_pick_failure_vtk.mov
I realize that this might be an issue with something other than Trame, but I thought I'd start here!
Platform:
Trame:
- Version 1.x
- Version 2.x
Device:
- Desktop
- Mobile
OS:
- Windows
- MacOS
- Linux
- Android
- iOS
Browsers Affected:
- Chrome
- Firefox
- Microsoft Edge
- Safari
- Opera
- Brave
- IE 11
py-web-vue version:
vMAJOR.MINOR.PATCH
Closing for now as workaround found