{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "import pyvista as pv\n",
    "\n",
    "from pyvista import examples\n",
    "\n",
    "pn.extension('vtk', design='material', sizing_mode='stretch_width', template='material')\n",
    "\n",
    "pn.state.template.config.raw_css.append(\"\"\"\n",
    "#main {\n",
    "  padding: 0;\n",
    "}\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this example we use the pyvista library to load a dataset and generate easily a VTK scene"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m = examples.download_st_helens().warp_by_scalar()\n",
    "\n",
    "# default camera position\n",
    "cpos = [\n",
    "    (567000.9232163235, 5119147.423216323, 6460.423216322832),\n",
    "    (562835.0, 5114981.5, 2294.5),\n",
    "    (-0.4082482904638299, -0.40824829046381844, 0.8164965809277649)\n",
    "]\n",
    "\n",
    "# pyvista plotter\n",
    "pl = pv.Plotter(notebook=True);\n",
    "actor = pl.add_mesh(m, smooth_shading=True, lighting=True)\n",
    "pl.camera_position = cpos #set camera position\n",
    "\n",
    "# save initial camera properties\n",
    "renderer = list(pl.ren_win.GetRenderers())[0]\n",
    "initial_camera = renderer.GetActiveCamera()\n",
    "initial_camera_pos = {\n",
    "    \"focalPoint\": initial_camera.GetFocalPoint(),\n",
    "    \"position\": initial_camera.GetPosition(),\n",
    "    \"viewUp\": initial_camera.GetViewUp()\n",
    "}\n",
    "\n",
    "# Panel creation using the VTK Scene created by the plotter pyvista\n",
    "orientation_widget = True\n",
    "enable_keybindings = True\n",
    "vtkpan = pn.pane.VTK(\n",
    "    pl.ren_win, margin=0, sizing_mode='stretch_both', orientation_widget=orientation_widget,\n",
    "    enable_keybindings=enable_keybindings, min_height=600\n",
    ")\n",
    "vtkpan"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "WidgetBox with colorbars and actor selection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Creation of a mapping between Custom name and the VTK object reference\n",
    "actor_ref = [\"None\", actor.__this__]\n",
    "actor_names = [\"None\", 'St Helen']\n",
    "actor_opts = {k:v for k,v in zip(actor_names, actor_ref)}\n",
    "\n",
    "options = {}\n",
    "actor_selection = pn.widgets.Select(value=None, options=actor_opts, margin=0, name=\"Actor Selection\")\n",
    "actor_selection"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "WidgetBoxes with general parameters of the vtk Scene (Widgets, Background, Lights,...)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Scene Layout\n",
    "color = ''.join(['#'] + ['{:02x}'.format(int(v*255)) for v in pl.background_color[:3]])\n",
    "bind_and_orient = pn.widgets.CheckBoxGroup(\n",
    "    value=['Orientation Widget', 'Key Bindings'], options=['Orientation Widget', 'Key Bindings']\n",
    ")\n",
    "reset_camera = pn.widgets.Button(name='Reset Camera')\n",
    "background_color = pn.widgets.ColorPicker(value=color, name='Background Color')\n",
    "scene_props = pn.WidgetBox(bind_and_orient, reset_camera, background_color, sizing_mode='stretch_width')\n",
    "\n",
    "# Light properties\n",
    "light_box_title = pn.widgets.StaticText(value='Light properties')\n",
    "light_type = pn.widgets.Select(value='HeadLight', options=['HeadLight', 'SceneLight', 'CameraLight'])\n",
    "light_intensity = pn.widgets.FloatSlider(start=0, end=1, value=1, name=\"Intensity\")\n",
    "light_props = pn.WidgetBox(light_box_title, light_type, light_intensity, sizing_mode='stretch_width')\n",
    "\n",
    "pn.Column(scene_props, light_props, max_width=320)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "WidgetBox with properties of the Actors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#layout actor props\n",
    "opacity = pn.widgets.FloatSlider(value=1, start=0, end=1, name='Opacity', disabled=True)\n",
    "lighting = pn.widgets.Toggle(value=True, name='Lighting', disabled=True)\n",
    "interpolation = pn.widgets.Select(value='Phong', options=['Flat','Phong'], name='Interpolation', disabled=True)\n",
    "edges = pn.widgets.Toggle(value=False, name='Show Edges', disabled=True)\n",
    "edges_color = pn.widgets.ColorPicker(value='#ffffff', name='Edges Color', disabled=True)\n",
    "representation = pn.widgets.Select(value='Surface', options=['Points','Wireframe','Surface'], name='Representation', disabled=True)\n",
    "frontface_culling = pn.widgets.Toggle(value=False, name='Frontface Culling', disabled=True)\n",
    "backface_culling = pn.widgets.Toggle(value=False, name='Backface Culling', disabled=True)\n",
    "ambient = pn.widgets.FloatSlider(value=0, start=-1, end=1, name='Ambient', disabled=True)\n",
    "diffuse = pn.widgets.FloatSlider(value=1, start=0, end=2, name='Diffuse', disabled=True)\n",
    "specular = pn.widgets.FloatSlider(value=0, start=0, end=10, name='Specular', disabled=True)\n",
    "specular_power = pn.widgets.FloatSlider(value=100, start=0, end=100, name='Specular Power', disabled=True)\n",
    "\n",
    "actor_props = pn.WidgetBox(\n",
    "    opacity, lighting, interpolation, edges, edges_color,\n",
    "    representation, frontface_culling, backface_culling,\n",
    "    ambient, diffuse, specular, specular_power\n",
    ")\n",
    "\n",
    "actor_props"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Linking all widgets together using jslinks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Linking\n",
    "light_type.jslink(vtkpan, code={'value':\"\"\"\n",
    "const light = target.renderer_el.getRenderer().getLights()[0]\n",
    "if (source.value == 'HeadLight')\n",
    "    light.setLightTypeToHeadLight()\n",
    "else if (source.value == 'CameraLight')\n",
    "    light.setLightTypeToCameraLight()\n",
    "else if (source.value == 'SceneLight')\n",
    "    light.setLightTypeToSceneLight()\n",
    "target.renderer_el.getRenderWindow().render()\n",
    "\"\"\"})\n",
    "\n",
    "light_intensity.jslink(vtkpan, code={'value':\"\"\"\n",
    "const light = target.renderer_el.getRenderer().getLights()[0]\n",
    "light.setIntensity(source.value)\n",
    "target.renderer_el.getRenderWindow().render()\n",
    "\"\"\"})\n",
    "\n",
    "\n",
    "bind_and_orient.jslink(vtkpan, code = {'active':\"\"\"\n",
    "target.orientation_widget = source.active.includes(0)\n",
    "target.enable_keybindings = source.active.includes(1)\n",
    "\"\"\"})\n",
    "\n",
    "reset_camera.js_on_click(args={'target': vtkpan, 'initial_camera':initial_camera_pos},\n",
    "                         code=\"target.camera = initial_camera\");\n",
    "\n",
    "background_color.jslink(vtkpan, code={'value':\"\"\"\n",
    "const hextoarr = (color) => {return [parseInt(color.slice(1,3),16)/255, parseInt(color.slice(3,5),16)/255, parseInt(color.slice(5,7),16)/255]}\n",
    "target.renderer_el.getRenderer().setBackground(hextoarr(source.color))\n",
    "target.renderer_el.getRenderWindow().render()\n",
    "\"\"\"});\n",
    "\n",
    "opacity.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setOpacity(source.value)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "lighting.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setLighting(source.active)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "edges.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setEdgeVisibility(source.active)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "interpolation.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    if(source.value==\"Flat\"){\n",
    "        actor.getProperty().setInterpolationToFlat()\n",
    "    }else{\n",
    "        actor.getProperty().setInterpolationToPhong()\n",
    "    }\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "edges_color.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const hextoarr = (color) => {return [parseInt(color.slice(1,3),16)/255, parseInt(color.slice(3,5),16)/255, parseInt(color.slice(5,7),16)/255]}\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setEdgeColor(hextoarr(source.color))\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "representation.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    if(source.value==\"Points\"){\n",
    "        actor.getProperty().setRepresentationToPoints()\n",
    "    }else if(source.value==\"Wireframe\"){\n",
    "        actor.getProperty().setRepresentationToWireframe()\n",
    "    }else if(source.value==\"Surface\"){\n",
    "        actor.getProperty().setRepresentationToSurface()\n",
    "    }\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "frontface_culling.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setFrontfaceCulling(source.active)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "backface_culling.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setBackfaceCulling(source.active)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "ambient.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setAmbient(source.value)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "diffuse.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setDiffuse(source.value)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "specular.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setSpecular(source.value)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "specular_power.jscallback(args={\"target\":vtkpan, \"actor_selection\":actor_selection}, value=\"\"\"\n",
    "if (actor_selection.value!=\"None\"){\n",
    "    const actor = target.getActors(actor_selection.value)[0]\n",
    "    actor.getProperty().setSpecularPower(source.value)\n",
    "    target.renderer_el.getRenderWindow().render()\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "actor_selection.jslink(target=vtkpan, code = {'value' : \"\"\"\n",
    "if (source.value!=\"None\"){\n",
    "    const actor = target.getActors(source.value)[0]\n",
    "    target.outline.setInputData(actor.getMapper().getInputData())\n",
    "    target.renderer_el.getRenderer().addActor(target.outline_actor)\n",
    "    \n",
    "    //synchronize actor props and widgets values\n",
    "    const properties = actor.getProperty()\n",
    "    opacity.setv({value: properties.getOpacity()}, {silent: true})\n",
    "    lighting.setv({active: !!properties.getLighting()}, {silent: true})\n",
    "    edges.active = !!properties.getEdgeVisibility()\n",
    "    const actor_color = \"#\" + properties.getEdgeColor().map((c) => (\"0\" + Math.round(255*c).toString(16,2)).slice(-2)).join('')\n",
    "    edges_color.setv({color: actor_color}, {silent: true})\n",
    "    const interp_string = properties.getInterpolationAsString()\n",
    "    interpolation.setv({value: interp_string[0] + interp_string.slice(1).toLocaleLowerCase()}, {silent: true})\n",
    "    const repr_string = properties.getRepresentationAsString()\n",
    "    representation.setv({value: repr_string[0] + repr_string.slice(1).toLocaleLowerCase()}, {silent: true})\n",
    "    frontface_culling.setv({active: !!properties.getFrontfaceCulling()}, {silent: true})\n",
    "    backface_culling.setv({active: !!properties.getBackfaceCulling()}, {silent: true})\n",
    "    ambient.setv({value: properties.getAmbient()}, {silent: true})\n",
    "    diffuse.setv({value: properties.getDiffuse()}, {silent: true})\n",
    "    specular.setv({value: properties.getSpecular()}, {silent: true})\n",
    "    specular_power.setv({value: properties.getSpecularPower()}, {silent: true})\n",
    "    //enable widgets modifications\n",
    "    opacity.disabled = false\n",
    "    lighting.disabled = false\n",
    "    interpolation.disabled = false\n",
    "    edges.disabled = false\n",
    "    edges_color.disabled = false\n",
    "    representation.disabled = false\n",
    "    frontface_culling.disabled = false\n",
    "    backface_culling.disabled = false\n",
    "    ambient.disabled = false\n",
    "    diffuse.disabled = false\n",
    "    specular.disabled = false\n",
    "    specular_power.disabled = false\n",
    "} else {\n",
    "    target.renderer_el.getRenderer().removeActor(target.outline_actor)\n",
    "    opacity.disabled = true\n",
    "    lighting.disabled = true\n",
    "    interpolation.disabled = true\n",
    "    edges.disabled = true\n",
    "    edges_color.disabled = true\n",
    "    representation.disabled = true\n",
    "    frontface_culling.disabled = true\n",
    "    backface_culling.disabled = true\n",
    "    ambient.disabled = true\n",
    "    diffuse.disabled = true\n",
    "    specular.disabled = true\n",
    "    specular_power.disabled = true\n",
    "}\n",
    "target.renderer_el.getRenderWindow().render()\n",
    "\n",
    "\"\"\"}, args={\"opacity\":opacity, \"lighting\":lighting, \"interpolation\": interpolation, \"edges\": edges, \"edges_color\": edges_color,\n",
    "            \"representation\": representation, \"frontface_culling\": frontface_culling, \"backface_culling\": backface_culling,\n",
    "            \"ambient\": ambient, \"diffuse\": diffuse, \"specular\": specular, \"specular_power\": specular_power});\n",
    "\n",
    "pn.Column(\n",
    "    \"This example demonstrates the use of **VTK and pyvista** to display a *scene*\",\n",
    "    pn.Row(\n",
    "        vtkpan.servable(title='VTK - Mt. St Helens'),\n",
    "        pn.Column(\n",
    "            actor_selection,\n",
    "            pn.Tabs(\n",
    "                ('Scene controller', pn.Column(scene_props, light_props)),\n",
    "                ('Actor properties',actor_props)\n",
    "            )\n",
    "        ).servable(target='sidebar')\n",
    "    ), min_height=600\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
