{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Sphere Widget\n\nThe sphere widget can be enabled and disabled by the\n`pyvista.Plotter.add_sphere_widget`{.interpreted-text role=\"func\"} and\n`pyvista.Plotter.clear_sphere_widgets`{.interpreted-text role=\"func\"}\nmethods respectively. This is a very versatile widget as it can control\nvertex location that can be used to control or update the location of\njust about anything.\n\nWe don\\'t have any convenient helper methods that utilize this widget\nout of the box, but we have added a lot of ways to use this widget so\nthat you can easily add several widgets to a scene.\n\nLet\\'s look at a few use cases that all update a surface mesh.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        ""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Example A\n\nUse a single sphere widget\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\n\nimport pyvista as pv\n\n# Create a triangle surface\nsurf = pv.PolyData()\nsurf.points = np.array(\n    [\n        [-10, -10, -10],\n        [10, 10, -10],\n        [-10, 10, 0],\n    ],\n)\nsurf.faces = np.array([3, 0, 1, 2])\n\np = pv.Plotter()\n\n\ndef callback(point):\n    surf.points[0] = point\n\n\np.add_sphere_widget(callback)\np.add_mesh(surf, color=True)\n\np.show_grid()\np.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "And here is a screen capture of a user interacting with this\n\n![image](../../images/gifs/sphere-widget-a.gif)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Example B\n\nUse several sphere widgets at once\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\n\nimport pyvista as pv\n\n# Create a triangle surface\nsurf = pv.PolyData()\nsurf.points = np.array(\n    [\n        [-10, -10, -10],\n        [10, 10, -10],\n        [-10, 10, 0],\n    ],\n)\nsurf.faces = np.array([3, 0, 1, 2])\n\n\np = pv.Plotter()\n\n\ndef callback(point, i):\n    surf.points[i] = point\n\n\np.add_sphere_widget(callback, center=surf.points)\np.add_mesh(surf, color=True)\n\np.show_grid()\np.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "And here is a screen capture of a user interacting with this\n\n![image](../../images/gifs/sphere-widget-b.gif)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Example C\n\nThis one is the coolest - use four sphere widgets to update\nperturbations on a surface and interpolate between them with some\nboundary conditions\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\nfrom scipy.interpolate import griddata\n\nimport pyvista as pv\n\n\ndef get_colors(n):\n    \"\"\"A helper function to get n colors\"\"\"\n    from itertools import cycle\n\n    import matplotlib as mpl\n\n    cycler = mpl.rcParams['axes.prop_cycle']\n    colors = cycle(cycler)\n    colors = [next(colors)['color'] for i in range(n)]\n    return colors\n\n\n# Create a grid to interpolate to\nxmin, xmax, ymin, ymax = 0, 100, 0, 100\nx = np.linspace(xmin, xmax, num=25)\ny = np.linspace(ymin, ymax, num=25)\nxx, yy, zz = np.meshgrid(x, y, [0])\n\n# Make sure boundary conditions exist\nboundaries = np.array([[xmin, ymin, 0], [xmin, ymax, 0], [xmax, ymin, 0], [xmax, ymax, 0]])\n\n# Create the PyVista mesh to hold this grid\nsurf = pv.StructuredGrid(xx, yy, zz)\n\n# Create some initial perturbations\n# - this array will be updated inplace\npoints = np.array([[33, 25, 45], [70, 80, 13], [51, 57, 10], [25, 69, 20]])\n\n\n# Create an interpolation function to update that surface mesh\ndef update_surface(point, i):\n    points[i] = point\n    tp = np.vstack((points, boundaries))\n    zz = griddata(tp[:, 0:2], tp[:, 2], (xx[:, :, 0], yy[:, :, 0]), method='cubic')\n    surf.points[:, -1] = zz.ravel(order='F')\n\n\n# Get a list of unique colors for each widget\ncolors = get_colors(len(points))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Begin the plotting routine\np = pv.Plotter()\n\n# Add the surface to the scene\np.add_mesh(surf, color=True)\n\n# Add the widgets which will update the surface\np.add_sphere_widget(update_surface, center=points, color=colors, radius=3)\n# Add axes grid\np.show_grid()\n\n# Show it\np.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "And here is a screen capture of a user interacting with this\n\n![image](../../images/gifs/sphere-widget-c.gif)\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}