{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Path editor\n\nSharing events across GUIs.\n\nThis example demonstrates a cross-GUI application using Matplotlib event\nhandling to interact with and modify objects on the canvas.\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>This example exercises the interactive capabilities of Matplotlib, and this\n    will not appear in the static documentation. Please run this code on your\n    machine to see the interactivity.\n\n    You can copy and paste individual parts, or download the entire example\n    using the link at the bottom of the page.</p></div>\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\nimport numpy as np\n\nfrom matplotlib.backend_bases import MouseButton\nfrom matplotlib.patches import PathPatch\nfrom matplotlib.path import Path\n\nfig, ax = plt.subplots()\n\npathdata = [\n    (Path.MOVETO, (1.58, -2.57)),\n    (Path.CURVE4, (0.35, -1.1)),\n    (Path.CURVE4, (-1.75, 2.0)),\n    (Path.CURVE4, (0.375, 2.0)),\n    (Path.LINETO, (0.85, 1.15)),\n    (Path.CURVE4, (2.2, 3.2)),\n    (Path.CURVE4, (3, 0.05)),\n    (Path.CURVE4, (2.0, -0.5)),\n    (Path.CLOSEPOLY, (1.58, -2.57)),\n]\n\ncodes, verts = zip(*pathdata)\npath = Path(verts, codes)\npatch = PathPatch(\n    path, facecolor='green', edgecolor='yellow', alpha=0.5)\nax.add_patch(patch)\n\n\nclass PathInteractor:\n    \"\"\"\n    A path editor.\n\n    Press 't' to toggle vertex markers on and off.  When vertex markers are on,\n    they can be dragged with the mouse.\n    \"\"\"\n\n    showverts = True\n    epsilon = 5  # max pixel distance to count as a vertex hit\n\n    def __init__(self, pathpatch):\n\n        self.ax = pathpatch.axes\n        canvas = self.ax.figure.canvas\n        self.pathpatch = pathpatch\n        self.pathpatch.set_animated(True)\n\n        x, y = zip(*self.pathpatch.get_path().vertices)\n\n        self.line, = ax.plot(\n            x, y, marker='o', markerfacecolor='r', animated=True)\n\n        self._ind = None  # the active vertex\n\n        canvas.mpl_connect('draw_event', self.on_draw)\n        canvas.mpl_connect('button_press_event', self.on_button_press)\n        canvas.mpl_connect('key_press_event', self.on_key_press)\n        canvas.mpl_connect('button_release_event', self.on_button_release)\n        canvas.mpl_connect('motion_notify_event', self.on_mouse_move)\n        self.canvas = canvas\n\n    def get_ind_under_point(self, event):\n        \"\"\"\n        Return the index of the point closest to the event position or *None*\n        if no point is within ``self.epsilon`` to the event position.\n        \"\"\"\n        xy = self.pathpatch.get_path().vertices\n        xyt = self.pathpatch.get_transform().transform(xy)  # to display coords\n        xt, yt = xyt[:, 0], xyt[:, 1]\n        d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)\n        ind = d.argmin()\n        return ind if d[ind] < self.epsilon else None\n\n    def on_draw(self, event):\n        \"\"\"Callback for draws.\"\"\"\n        self.background = self.canvas.copy_from_bbox(self.ax.bbox)\n        self.ax.draw_artist(self.pathpatch)\n        self.ax.draw_artist(self.line)\n\n    def on_button_press(self, event):\n        \"\"\"Callback for mouse button presses.\"\"\"\n        if (event.inaxes is None\n                or event.button != MouseButton.LEFT\n                or not self.showverts):\n            return\n        self._ind = self.get_ind_under_point(event)\n\n    def on_button_release(self, event):\n        \"\"\"Callback for mouse button releases.\"\"\"\n        if (event.button != MouseButton.LEFT\n                or not self.showverts):\n            return\n        self._ind = None\n\n    def on_key_press(self, event):\n        \"\"\"Callback for key presses.\"\"\"\n        if not event.inaxes:\n            return\n        if event.key == 't':\n            self.showverts = not self.showverts\n            self.line.set_visible(self.showverts)\n            if not self.showverts:\n                self._ind = None\n        self.canvas.draw()\n\n    def on_mouse_move(self, event):\n        \"\"\"Callback for mouse movements.\"\"\"\n        if (self._ind is None\n                or event.inaxes is None\n                or event.button != MouseButton.LEFT\n                or not self.showverts):\n            return\n\n        vertices = self.pathpatch.get_path().vertices\n\n        vertices[self._ind] = event.xdata, event.ydata\n        self.line.set_data(zip(*vertices))\n\n        self.canvas.restore_region(self.background)\n        self.ax.draw_artist(self.pathpatch)\n        self.ax.draw_artist(self.line)\n        self.canvas.blit(self.ax.bbox)\n\n\ninteractor = PathInteractor(patch)\nax.set_title('drag vertices to update path')\nax.set_xlim(-3, 4)\nax.set_ylim(-3, 4)\n\nplt.show()"
      ]
    }
  ],
  "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.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}