Skip to content

Instantly share code, notes, and snippets.

@alwc
Forked from smidm/ellipse_bounding_box.ipynb
Created March 1, 2019 06:03
Show Gist options
  • Save alwc/5cee411c02a710d187816c6bf89a41da to your computer and use it in GitHub Desktop.
Save alwc/5cee411c02a710d187816c6bf89a41da to your computer and use it in GitHub Desktop.
Compute bounding box for an ellipse.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pylab as plt\n",
"from matplotlib.patches import Ellipse\n",
"from ipywidgets import interact, interactive, fixed, interact_manual\n",
"import ipywidgets as widgets"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"def get_ellipse_bb(x, y, major, minor, angle_deg):\n",
" \"\"\"\n",
" Compute tight ellipse bounding box.\n",
" \n",
" see https://stackoverflow.com/questions/87734/how-do-you-calculate-the-axis-aligned-bounding-box-of-an-ellipse#88020\n",
" \"\"\"\n",
" t = np.arctan(-minor / 2 * np.tan(np.radians(angle_deg)) / (major / 2))\n",
" [max_x, min_x] = [x + major / 2 * np.cos(t) * np.cos(np.radians(angle_deg)) -\n",
" minor / 2 * np.sin(t) * np.sin(np.radians(angle_deg)) for t in (t, t + np.pi)]\n",
" t = np.arctan(minor / 2 * 1. / np.tan(np.radians(angle_deg)) / (major / 2))\n",
" [max_y, min_y] = [y + minor / 2 * np.sin(t) * np.cos(np.radians(angle_deg)) +\n",
" major / 2 * np.cos(t) * np.sin(np.radians(angle_deg)) for t in (t, t + np.pi)]\n",
" return min_x, min_y, max_x, max_y"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_ellipse(x=50, y=50, major=30, minor=10, angle_deg=45):\n",
" plt.figure(2)\n",
" plt.clf()\n",
" ax = plt.gca()\n",
" ax.add_patch(Ellipse((x, y), major, minor, -angle_deg, \n",
" edgecolor='r',\n",
" facecolor='none'))\n",
" min_x, min_y, max_x, max_y = get_ellipse_bb(x, y, major, minor, angle_deg)\n",
" plt.vlines([max_x, min_x], 0, 100)\n",
" plt.hlines([max_y, min_y], 0, 100)\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\nRK5CYII=\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x7f569d28dc90>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot_ellipse()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "bd088170054242c0a6f4ba5b3a462287",
"version_major": 2,
"version_minor": 0
},
"text/html": [
"<p>Failed to display Jupyter Widget of type <code>interactive</code>.</p>\n",
"<p>\n",
" If you're reading this message in Jupyter Notebook or JupyterLab, it may mean\n",
" that the widgets JavaScript is still loading. If this message persists, it\n",
" likely means that the widgets JavaScript library is either not installed or\n",
" not enabled. See the <a href=\"https://ipywidgets.readthedocs.io/en/stable/user_install.html\">Jupyter\n",
" Widgets Documentation</a> for setup instructions.\n",
"</p>\n",
"<p>\n",
" If you're reading this message in another notebook frontend (for example, a static\n",
" rendering on GitHub or <a href=\"https://nbviewer.jupyter.org/\">NBViewer</a>),\n",
" it may mean that your frontend doesn't currently support widgets.\n",
"</p>\n"
],
"text/plain": [
"interactive(children=(IntSlider(value=30, description=u'major', max=40), IntSlider(value=10, description=u'minor', max=40), IntSlider(value=45, description=u'angle_deg', max=180, min=-180), Output(layout=Layout(height=u'350px'))), _dom_classes=('widget-interact',))"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"interactive_plot = interactive(plot_ellipse, \n",
" x=fixed(50), \n",
" y=fixed(50),\n",
" major=(0, 40),\n",
" minor=(0, 40),\n",
" angle_deg=(-180, 180),\n",
" )\n",
"output = interactive_plot.children[-1]\n",
"output.layout.height = '350px'\n",
"interactive_plot "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
},
"toc": {
"nav_menu": {},
"number_sections": true,
"sideBar": false,
"skip_h1_title": false,
"toc_cell": false,
"toc_position": {},
"toc_section_display": "block",
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment