Skip to content

Commit

Permalink
Add picking support (#38)
Browse files Browse the repository at this point in the history
* Update to react-vtk-js@1.2.0

* update generated files

* Update to react-vtk-js@1.2.1

* update generated files

* improve pyvista-terrain demo with picking

* add support for multi-array fields

* udpate generated files

* Apply black to demos

* Change default to None instead of empty array

This ensures we are safe from mutations

* npm run build

* Bump version

Co-authored-by: xhlulu <xhlperso@gmail.com>
  • Loading branch information
jourdain and xhlulu authored Apr 7, 2021
1 parent daca5c1 commit 1284cca
Show file tree
Hide file tree
Showing 28 changed files with 380 additions and 98 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: dashVtk
Title: React based declarative usage of vtk.js for Dash
Version: 0.0.6
Version: 0.0.7
Description: React based declarative usage of vtk.js for Dash
Depends: R (>= 3.0.2)
Imports:
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "DashVtk"
uuid = "1b08a953-4be3-4667-9a23-818b1eccd4c7"
authors = ["Kitware Inc <sebastien.jourdain@kitware.com> and Plotly Technologies <xinghan@plot.ly>"]
version = "0.0.6"
version = "0.0.7"

[deps]
Dash = "1b08a953-4be3-4667-9a23-3db579824955"
Expand Down
4 changes: 2 additions & 2 deletions R/internal.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
.dashVtk_js_metadata <- function() {
deps_metadata <- list(`dash_vtk` = structure(list(name = "dash_vtk",
version = "0.0.6", src = list(href = NULL,
version = "0.0.7", src = list(href = NULL,
file = "deps"), meta = NULL,
script = 'dash_vtk.min.js',
stylesheet = NULL, head = NULL, attachment = NULL, package = "dashVtk",
all_files = FALSE), class = "html_dependency"),
`dash_vtk` = structure(list(name = "dash_vtk",
version = "0.0.6", src = list(href = NULL,
version = "0.0.7", src = list(href = NULL,
file = "deps"), meta = NULL,
script = 'dash_vtk.min.js.map',
stylesheet = NULL, head = NULL, attachment = NULL, package = "dashVtk",
Expand Down
6 changes: 3 additions & 3 deletions R/vtkGeometryRepresentation.R
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# AUTO GENERATED FILE - DO NOT EDIT

vtkGeometryRepresentation <- function(children=NULL, id=NULL, actor=NULL, mapper=NULL, property=NULL, colorMapPreset=NULL, colorDataRange=NULL) {
vtkGeometryRepresentation <- function(children=NULL, id=NULL, actor=NULL, mapper=NULL, property=NULL, colorMapPreset=NULL, colorDataRange=NULL, showCubeAxes=NULL, cubeAxesStyle=NULL) {

props <- list(children=children, id=id, actor=actor, mapper=mapper, property=property, colorMapPreset=colorMapPreset, colorDataRange=colorDataRange)
props <- list(children=children, id=id, actor=actor, mapper=mapper, property=property, colorMapPreset=colorMapPreset, colorDataRange=colorDataRange, showCubeAxes=showCubeAxes, cubeAxesStyle=cubeAxesStyle)
if (length(props) > 0) {
props <- props[!vapply(props, is.null, logical(1))]
}
component <- list(
props = props,
type = 'GeometryRepresentation',
namespace = 'dash_vtk',
propNames = c('children', 'id', 'actor', 'mapper', 'property', 'colorMapPreset', 'colorDataRange'),
propNames = c('children', 'id', 'actor', 'mapper', 'property', 'colorMapPreset', 'colorDataRange', 'showCubeAxes', 'cubeAxesStyle'),
package = 'dashVtk'
)

Expand Down
6 changes: 3 additions & 3 deletions R/vtkView.R
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# AUTO GENERATED FILE - DO NOT EDIT

vtkView <- function(children=NULL, id=NULL, style=NULL, className=NULL, background=NULL, interactorSettings=NULL, cameraPosition=NULL, cameraViewUp=NULL, cameraParallelProjection=NULL, triggerRender=NULL, triggerResetCamera=NULL) {
vtkView <- function(children=NULL, id=NULL, style=NULL, className=NULL, background=NULL, interactorSettings=NULL, cameraPosition=NULL, cameraViewUp=NULL, cameraParallelProjection=NULL, triggerRender=NULL, triggerResetCamera=NULL, pickingModes=NULL, clickInfo=NULL, hoverInfo=NULL) {

props <- list(children=children, id=id, style=style, className=className, background=background, interactorSettings=interactorSettings, cameraPosition=cameraPosition, cameraViewUp=cameraViewUp, cameraParallelProjection=cameraParallelProjection, triggerRender=triggerRender, triggerResetCamera=triggerResetCamera)
props <- list(children=children, id=id, style=style, className=className, background=background, interactorSettings=interactorSettings, cameraPosition=cameraPosition, cameraViewUp=cameraViewUp, cameraParallelProjection=cameraParallelProjection, triggerRender=triggerRender, triggerResetCamera=triggerResetCamera, pickingModes=pickingModes, clickInfo=clickInfo, hoverInfo=hoverInfo)
if (length(props) > 0) {
props <- props[!vapply(props, is.null, logical(1))]
}
component <- list(
props = props,
type = 'View',
namespace = 'dash_vtk',
propNames = c('children', 'id', 'style', 'className', 'background', 'interactorSettings', 'cameraPosition', 'cameraViewUp', 'cameraParallelProjection', 'triggerRender', 'triggerResetCamera'),
propNames = c('children', 'id', 'style', 'className', 'background', 'interactorSettings', 'cameraPosition', 'cameraViewUp', 'cameraParallelProjection', 'triggerRender', 'triggerResetCamera', 'pickingModes', 'clickInfo', 'hoverInfo'),
package = 'dashVtk'
)

Expand Down
11 changes: 7 additions & 4 deletions dash_vtk/GeometryRepresentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ class GeometryRepresentation(Component):
- mapper (dict; optional): Properties to set to the actor
- property (dict; optional): Properties to set to the actor.property
- colorMapPreset (string; default 'erdc_rainbow_bright'): Preset name for the lookup table color map
- colorDataRange (list of numbers; default [0, 1]): Data range use for the colorMap"""
- colorDataRange (list of numbers; default [0, 1]): Data range use for the colorMap
- showCubeAxes (boolean; optional): Show/Hide Cube Axes for the given representation
- cubeAxesStyle (dict; optional): Configure cube Axes style by overriding the set of properties defined
https://github.com/Kitware/vtk-js/blob/HEAD/Sources/Rendering/Core/CubeAxesActor/index.js#L703-L719"""
@_explicitize_args
def __init__(self, children=None, id=Component.UNDEFINED, actor=Component.UNDEFINED, mapper=Component.UNDEFINED, property=Component.UNDEFINED, colorMapPreset=Component.UNDEFINED, colorDataRange=Component.UNDEFINED, **kwargs):
self._prop_names = ['children', 'id', 'actor', 'mapper', 'property', 'colorMapPreset', 'colorDataRange']
def __init__(self, children=None, id=Component.UNDEFINED, actor=Component.UNDEFINED, mapper=Component.UNDEFINED, property=Component.UNDEFINED, colorMapPreset=Component.UNDEFINED, colorDataRange=Component.UNDEFINED, showCubeAxes=Component.UNDEFINED, cubeAxesStyle=Component.UNDEFINED, **kwargs):
self._prop_names = ['children', 'id', 'actor', 'mapper', 'property', 'colorMapPreset', 'colorDataRange', 'showCubeAxes', 'cubeAxesStyle']
self._type = 'GeometryRepresentation'
self._namespace = 'dash_vtk'
self._valid_wildcard_attributes = []
self.available_properties = ['children', 'id', 'actor', 'mapper', 'property', 'colorMapPreset', 'colorDataRange']
self.available_properties = ['children', 'id', 'actor', 'mapper', 'property', 'colorMapPreset', 'colorDataRange', 'showCubeAxes', 'cubeAxesStyle']
self.available_wildcard_properties = []

_explicit_args = kwargs.pop('_explicit_args')
Expand Down
15 changes: 11 additions & 4 deletions dash_vtk/View.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,21 @@ class View(Component):
- cameraViewUp (list; default [0, 1, 0]): Initial camera position from an object in [0,0,0]
- cameraParallelProjection (boolean; default False): Use parallel projection (default: false)
- triggerRender (number; default 0): Property use to trigger a render when changing.
- triggerResetCamera (number; default 0): Property use to trigger a resetCamera when changing."""
- triggerResetCamera (number; default 0): Property use to trigger a resetCamera when changing.
- pickingModes (list of strings; optional): List of picking listeners to bind. The supported values are `click` and `hover`. By default it is disabled (empty array).
- clickInfo (dict; optional): Read-only prop. To use this, make sure that `pickingModes` contains `click`.
This prop is updated when an element in the map is clicked. This contains
the picking info describing the object being clicked on.
- hoverInfo (dict; optional): Read-only prop. To use this, make sure that `pickingModes` contains `hover`.
This prop is updated when an element in the map is hovered. This contains
the picking info describing the object being hovered."""
@_explicitize_args
def __init__(self, children=None, id=Component.UNDEFINED, style=Component.UNDEFINED, className=Component.UNDEFINED, background=Component.UNDEFINED, interactorSettings=Component.UNDEFINED, cameraPosition=Component.UNDEFINED, cameraViewUp=Component.UNDEFINED, cameraParallelProjection=Component.UNDEFINED, triggerRender=Component.UNDEFINED, triggerResetCamera=Component.UNDEFINED, **kwargs):
self._prop_names = ['children', 'id', 'style', 'className', 'background', 'interactorSettings', 'cameraPosition', 'cameraViewUp', 'cameraParallelProjection', 'triggerRender', 'triggerResetCamera']
def __init__(self, children=None, id=Component.UNDEFINED, style=Component.UNDEFINED, className=Component.UNDEFINED, background=Component.UNDEFINED, interactorSettings=Component.UNDEFINED, cameraPosition=Component.UNDEFINED, cameraViewUp=Component.UNDEFINED, cameraParallelProjection=Component.UNDEFINED, triggerRender=Component.UNDEFINED, triggerResetCamera=Component.UNDEFINED, pickingModes=Component.UNDEFINED, clickInfo=Component.UNDEFINED, hoverInfo=Component.UNDEFINED, **kwargs):
self._prop_names = ['children', 'id', 'style', 'className', 'background', 'interactorSettings', 'cameraPosition', 'cameraViewUp', 'cameraParallelProjection', 'triggerRender', 'triggerResetCamera', 'pickingModes', 'clickInfo', 'hoverInfo']
self._type = 'View'
self._namespace = 'dash_vtk'
self._valid_wildcard_attributes = []
self.available_properties = ['children', 'id', 'style', 'className', 'background', 'interactorSettings', 'cameraPosition', 'cameraViewUp', 'cameraParallelProjection', 'triggerRender', 'triggerResetCamera']
self.available_properties = ['children', 'id', 'style', 'className', 'background', 'interactorSettings', 'cameraPosition', 'cameraViewUp', 'cameraParallelProjection', 'triggerRender', 'triggerResetCamera', 'pickingModes', 'clickInfo', 'hoverInfo']
self.available_wildcard_properties = []

_explicit_args = kwargs.pop('_explicit_args')
Expand Down
14 changes: 7 additions & 7 deletions dash_vtk/dash_vtk.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dash_vtk/dash_vtk.min.js.map

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions dash_vtk/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@
"computed": false
}
},
"showCubeAxes": {
"type": {
"name": "bool"
},
"required": false,
"description": "Show/Hide Cube Axes for the given representation"
},
"cubeAxesStyle": {
"type": {
"name": "object"
},
"required": false,
"description": "Configure cube Axes style by overriding the set of properties defined\nhttps://github.com/Kitware/vtk-js/blob/HEAD/Sources/Rendering/Core/CubeAxesActor/index.js#L703-L719"
},
"children": {
"type": {
"name": "union",
Expand Down Expand Up @@ -1169,6 +1183,30 @@
"computed": false
}
},
"pickingModes": {
"type": {
"name": "arrayOf",
"value": {
"name": "string"
}
},
"required": false,
"description": "List of picking listeners to bind. The supported values are `click` and `hover`. By default it is disabled (empty array)."
},
"clickInfo": {
"type": {
"name": "object"
},
"required": false,
"description": "Read-only prop. To use this, make sure that `pickingModes` contains `click`.\nThis prop is updated when an element in the map is clicked. This contains\nthe picking info describing the object being clicked on."
},
"hoverInfo": {
"type": {
"name": "object"
},
"required": false,
"description": "Read-only prop. To use this, make sure that `pickingModes` contains `hover`.\nThis prop is updated when an element in the map is hovered. This contains\nthe picking info describing the object being hovered."
},
"children": {
"type": {
"name": "union",
Expand Down
4 changes: 2 additions & 2 deletions dash_vtk/package-info.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dash_vtk",
"version": "0.0.6",
"version": "0.0.7",
"description": "React based declarative usage of vtk.js for Dash",
"repository": {
"type": "git",
Expand All @@ -25,7 +25,7 @@
"license": "MIT",
"dependencies": {
"ramda": "^0.26.1",
"react-vtk-js": "1.1.4"
"react-vtk-js": "1.2.1"
},
"devDependencies": {
"@babel/core": "^7.5.4",
Expand Down
48 changes: 47 additions & 1 deletion dash_vtk/utils/vtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@
}


def to_mesh_state(dataset, field_to_keep=None):
def to_mesh_state(dataset, field_to_keep=None, point_arrays=None, cell_arrays=None):
'''Expect any dataset and extract its surface into a dash_vtk.Mesh state property'''
if dataset is None:
return None

if point_arrays is None:
point_arrays = []
if cell_arrays is None:
cell_arrays = []

# Make sure we have a polydata to export
polydata = None
if dataset.IsA('vtkPolyData'):
Expand Down Expand Up @@ -77,6 +82,42 @@ def to_mesh_state(dataset, field_to_keep=None):
js_types = to_js_type[str(values.dtype)]
location = 'PointData'

# other arrays (points)
point_data = []
for name in point_arrays:
array = polydata.GetPointData().GetArray(name)
if array:
dataRange = array.GetRange(-1)
nb_comp = array.GetNumberOfComponents()
values = vtk_to_numpy(array).ravel()
js_types = to_js_type[str(values.dtype)]
point_data.append({
'name': name,
'values': values,
'numberOfComponents': nb_comp,
'type': js_types,
'location': 'PointData',
'dataRange': dataRange,
})

# other arrays (cells)
cell_data = []
for name in point_arrays:
array = polydata.GetCellData().GetArray(name)
if array:
dataRange = array.GetRange(-1)
nb_comp = array.GetNumberOfComponents()
values = vtk_to_numpy(array).ravel()
js_types = to_js_type[str(values.dtype)]
cell_data.append({
'name': name,
'values': values,
'numberOfComponents': nb_comp,
'type': js_types,
'location': 'CellData',
'dataRange': dataRange,
})

state = {
'mesh': {
'points': points,
Expand All @@ -103,6 +144,11 @@ def to_mesh_state(dataset, field_to_keep=None):
},
})

if len(point_data):
state.update({ 'pointArrays': point_data })
if len(cell_data):
state.update({ 'cellArrays': cell_data })

return state


Expand Down
78 changes: 73 additions & 5 deletions demos/pyvista-terrain-following-mesh/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from dash.dependencies import Input, Output, State

import random
import json
import numpy as np
import pyvista as pv
from pyvista import examples
Expand Down Expand Up @@ -44,6 +45,7 @@ def updateWarp(factor=1):

vtk_view = dash_vtk.View(
id="vtk-view",
pickingModes=["hover"],
children=[
dash_vtk.GeometryRepresentation(
id="vtk-representation",
Expand All @@ -68,8 +70,19 @@ def updateWarp(factor=1):
],
colorMapPreset="erdc_blue2green_muted",
colorDataRange=color_range,
property={"edgeVisibility": True,},
)
property={"edgeVisibility": True},
showCubeAxes=True,
cubeAxesStyle={"axisLabels": ["", "", "Altitude"]},
),
dash_vtk.GeometryRepresentation(
id="pick-rep",
actor={"visibility": False},
children=[
dash_vtk.Algorithm(
id="pick-sphere", vtkClass="vtkSphereSource", state={"radius": 100},
)
],
),
],
)

Expand All @@ -96,31 +109,86 @@ def updateWarp(factor=1):
value="erdc_rainbow_bright",
),
),
dbc.Col(
children=dcc.Checklist(
id="toggle-cube-axes",
options=[{"label": " Show axis grid", "value": "grid"},],
value=[],
labelStyle={"display": "inline-block"},
),
),
],
style={"height": "12%", "align-items": "center"},
),
html.Div(
html.Div(vtk_view, style={"height": "100%", "width": "100%"}),
style={"height": "88%"},
),
html.Pre(
id="tooltip",
style={
"position": "absolute",
"bottom": "25px",
"left": "25px",
"zIndex": 1,
"color": "white",
},
),
],
)


@app.callback(
[
Output("vtk-representation", "showCubeAxes"),
Output("vtk-representation", "colorMapPreset"),
Output("vtk-representation", "colorDataRange"),
Output("vtk-polydata", "points"),
Output("vtk-polydata", "polys"),
Output("vtk-array", "values"),
Output("vtk-view", "triggerResetCamera"),
],
[Input("dropdown-preset", "value"), Input("scale-factor", "value")],
[
Input("dropdown-preset", "value"),
Input("scale-factor", "value"),
Input("toggle-cube-axes", "value"),
],
)
def updatePresetName(name, scale_factor):
def updatePresetName(name, scale_factor, cubeAxes):
points, polys, elevation, color_range = updateWarp(scale_factor)
return [name, color_range, points, polys, elevation, random.random()]
return [
"grid" in cubeAxes,
name,
color_range,
points,
polys,
elevation,
random.random(),
]


@app.callback(
[
Output("tooltip", "children"),
Output("pick-sphere", "state"),
Output("pick-rep", "actor"),
],
[Input("vtk-view", "clickInfo"), Input("vtk-view", "hoverInfo"),],
)
def onInfo(clickData, hoverData):
info = hoverData if hoverData else clickData
if info:
if (
"representationId" in info
and info["representationId"] == "vtk-representation"
):
return (
[json.dumps(info, indent=2)],
{"center": info["worldPosition"]},
{"visibility": True},
)
return dash.no_update, dash.no_update, dash.no_update
return [""], {}, {"visibility": False}


if __name__ == "__main__":
Expand Down
14 changes: 7 additions & 7 deletions deps/dash_vtk.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion deps/dash_vtk.min.js.map

Large diffs are not rendered by default.

Loading

0 comments on commit 1284cca

Please sign in to comment.