{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Path editor\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\nimport matplotlib\nimport matplotlib.path as mpath\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\n\nPath = mpath.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 = mpath.Path(verts, codes)\npatch = mpatches.PathPatch(path, facecolor=\"green\", edgecolor=\"yellow\", alpha=0.5)\nax.add_patch(patch)\n\n\nclass PathInteractor:\n\n showverts = True\n epsilon = 5 # max pixel distance to count as a vertex hit\n\n def __init__(self, pathpatch: matplotlib.patches.PathPatch):\n \"\"\"Path editor class. ``t`` toggle vertex markers on and off.\n When vertex markers are on, you can move them, delete them. \n\n Args:\n pathpatch (matplotlib.patches.PathPatch): PathPatch instance\n \"\"\"\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(x, y, marker=\"o\", markerfacecolor=\"r\", animated=True)\n\n self._ind = None # the active vert\n\n canvas.mpl_connect(\"draw_event\", self.draw_callback)\n canvas.mpl_connect(\"button_press_event\", self.button_press_callback)\n canvas.mpl_connect(\"key_press_event\", self.key_press_callback)\n canvas.mpl_connect(\"button_release_event\", self.button_release_callback)\n canvas.mpl_connect(\"motion_notify_event\", self.motion_notify_callback)\n self.canvas = canvas\n\n def draw_callback(self, event: matplotlib.backend_bases.DrawEvent):\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 self.canvas.blit(self.ax.bbox)\n\n def pathpatch_changed(self, pathpatch: matplotlib.patches.PathPatch):\n \"this method is called whenever the pathpatchgon object is called\"\n # only copy the artist props to the line (except visibility)\n vis = self.line.get_visible()\n plt.Artist.update_from(self.line, pathpatch)\n self.line.set_visible(vis) # don't use the pathpatch visibility state\n\n def get_ind_under_point(self, event):\n \"get the index of the vertex under point if within epsilon tolerance\"\n\n # display coords\n xy = np.asarray(self.pathpatch.get_path().vertices)\n xyt = self.pathpatch.get_transform().transform(xy)\n xt, yt = xyt[:, 0], xyt[:, 1]\n d = np.sqrt((xt - event.x) ** 2 + (yt - event.y) ** 2)\n ind = d.argmin()\n\n if d[ind] >= self.epsilon:\n ind = None\n\n return ind\n\n def button_press_callback(self, event):\n \"whenever a mouse button is pressed\"\n if not self.showverts:\n return\n if event.inaxes is None:\n return\n if event.button != 1:\n return\n self._ind = self.get_ind_under_point(event)\n\n def button_release_callback(self, event):\n \"whenever a mouse button is released\"\n if not self.showverts:\n return\n if event.button != 1:\n return\n self._ind = None\n\n def key_press_callback(self, event):\n \"whenever a key is pressed\"\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\n self.canvas.draw()\n\n def motion_notify_callback(self, event):\n \"on mouse movement\"\n if not self.showverts:\n return\n if self._ind is None:\n return\n if event.inaxes is None:\n return\n if event.button != 1:\n return\n x, y = event.xdata, event.ydata\n\n vertices = self.pathpatch.get_path().vertices\n\n vertices[self._ind] = x, y\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.8.10" } }, "nbformat": 4, "nbformat_minor": 0 }