Skip to content

Instantly share code, notes, and snippets.

@simrit1
Forked from WetHat/PY-2dConvexHull.ipynb
Created July 27, 2022 20:22
Show Gist options
  • Save simrit1/36fc7f1f8e92ba5332840157a5a6bb7a to your computer and use it in GitHub Desktop.
Save simrit1/36fc7f1f8e92ba5332840157a5a6bb7a to your computer and use it in GitHub Desktop.
2d Convex Hull from a Point Cloud with Python
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"toc-hr-collapsed": false
},
"source": [
"# Computing 2D Convex Hulls with Python\n",
"\n",
"A [convex hull](https://medium.com/@pascal.sommer.ch/a-gentle-introduction-to-the-convex-hull-problem-62dfcabee90c)\n",
"is a polygon which is the smallest convex polygon on the 2D plane, that encloses all of the points in a point cloud $P_n = \\left\\{ \\vec{p_i} \\;\\big|\\; i=1 \\dots n \\wedge \\vec{p_i} \\in \\mathbb{R}^2 \\right\\}$. As described in [Introduction to Convex Hull Applications](http://www.montefiore.ulg.ac.be/~briquet/algo3-chull-20070206.pdf), convex hulls are used in a variety of application domains.\n",
"\n",
"![Convex Hull](https://ds055uzetaobb.cloudfront.net/uploads/tantSbEgDe-ch2.gif)\n",
"\n",
"This notebook explores:\n",
"* an implementation of the [QuickHull](https://en.wikipedia.org/wiki/Quickhull) subdivision algorithm for computing convex hulls of point clouds\n",
"* a marching algorithm to add points to existing convex hulls one by one.\n",
"\n",
"**Note**: For production code it is recommended to use\n",
"[scipy.spatial.ConvexHull](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.ConvexHull.html) \n",
"from the [scipy](https://www.scipy.org/) package."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## About this Jupyter Notebook\n",
"\n",
"This _Gist_ was created using:\n",
"* the [Jupyter Lab](https://jupyter.org/) computational notebook.\n",
"* the _python3_ kernel\n",
"\n",
"Though Python is not best language to implement computational intensive algorithms, it can\n",
"_describe_ algorithms in a simple, conceptual way. We also take advantage of the Python\n",
"package ecosystem and make extensive use of:\n",
"* [matplotlib](https://matplotlib.org/) - to illustrate bits and pieces of the algorithm.\n",
"* [numpy](https://numpy.org/) - for vector algebra.\n",
"* [typing](https://docs.python.org/3/library/typing.html) - to add type hints to methods as function\n",
" in order to convey semantics and usage."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Some Utitlities and Environment Setup\n",
"\n",
"In this section we create a few general purpose utilities, so that we do not\n",
"need to clutter the algorithms with distracting details."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import collections as cl\n",
"import numpy as np\n",
"import numpy.linalg as npl\n",
"import matplotlib\n",
"import matplotlib.pyplot as plt\n",
"import functools\n",
"from typing import Callable, NewType, Iterable, Tuple\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Measuring performance"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def callcounted(fnc : Callable) -> Callable:\n",
" '''Decorator to count the number of method calls.'''\n",
" @functools.wraps(fnc)\n",
" def _callcounter (self,*args,**kwargs):\n",
" _callcounter.callcount +=1\n",
" return fnc(self,*args,**kwargs)\n",
" _callcounter.callcount = 0\n",
" return _callcounter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Drawing tools"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def draw_polygon(ax,points: Iterable[np.ndarray]):\n",
" '''Draw a polygon with direction arrows.'''\n",
" s = points[0]\n",
" points.append(s) # close the loop\n",
" for p in points[1:]:\n",
" ax.arrow(s[0],s[1],p[0]-s[0],p[1]-s[1],\n",
" length_includes_head=True,head_width= 0.05, head_length=0.1, capstyle='projecting')\n",
" s = p\n",
" ax.scatter([pt[0] for pt in points],[pt[1] for pt in points])"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def make_coordinate_system(subplots: int = 1, dx = (-10,130), dy = (-10,110)):\n",
" '''Set up a figure in a canonocal way.'''\n",
" plts = plt.subplots(1,subplots,sharey=True)\n",
"\n",
" if subplots == 1:\n",
" axs = (plts[1],)\n",
" else:\n",
" axs = plts[1]\n",
" for ax in axs:\n",
" ax.grid(True)\n",
" ax.set_aspect('equal')\n",
" ax.set_xlabel('x')\n",
" ax.set_xlim(dx)\n",
" ax.set_ylim(dy)\n",
"\n",
" axs[0].set_ylabel('y')\n",
" return plts"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Points on the 2-d Plane\n",
"\n",
"To represent 2-dimensional points in a point cloud we are going to use numpy arrays.\n",
"This way we do not have to take care of the details of vector algebra.\n",
"\n",
"With this the point $\\vec{p} = \\begin{bmatrix} 1 \\\\ 2 \\end{bmatrix} $\n",
"is represented as:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"p = np.array([1,2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Drawing this point on the 2d plane"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 144x144 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system(1,dx = (0,3), dy = (0,3))\n",
"fig.set_size_inches(2, 2, forward=True)\n",
"ax.scatter([p[0]], [p[1]])\n",
"ax.set_xticks(np.linspace(0,3,num=7))\n",
"ax.set_yticks(np.linspace(0,3,num=7))\n",
"ax.set_title('2D-Plane')\n",
"ax.annotate('$\\\\vec{p}$', xy=p, xytext=(15,1), ha='right', textcoords='offset points')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2-D Polygons\n",
"\n",
"As basis for representing a convex hull we start with a\n",
"[polygon](https://en.wikipedia.org/wiki/Polygon).\n",
"A polygon is a plane figure that is described by a finite number of straight line segments connected to form a closed polygonal chain or polygonal loop. We note that a convex hull\n",
"is a _special_ polygon where all edges have convex angles. For convenience we orient the edges (straight line segments) of a polygon so that they loop counter-clockwise around the interior of the polygon. The orientation\n",
"conventions helps us to easily classify point as inside (to the left of all edges of a convex polygon)\n",
"or outside (to the right of all edges of a convex polygon)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## PolygonEdge Class\n",
"\n",
"The edges of a polygon are oriented straight line segments bounded by a start and end vertex.\n",
"\n",
"In the convext of convex hull calculations we will mainly focus on edges $e(\\vec{a},\\vec{b})$ \n",
"defined on points from the point cloud $P_n$:\n",
"\n",
"$$\n",
"e(\\vec{a},\\vec{b}) = \\left\\{ \\vec{a},\\vec{b} \\;\\big|\\;\n",
"\\vec{a},\\vec{b} \\in P_n \\wedge \\vec{a} \\neq \\vec{b} \\right\\}\n",
"$$\n",
" \n",
"Points have signed distances when measured against an the (infinite) straight line on which the\n",
"oriented edge is defined. Points to the right of an oriented edge have positive distances,\n",
"points to the left have negative distances."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"class PolygonEdge:\n",
" '''A edge which is part of a closed, counter-clockwise loop of edges (polygon).'''\n",
" def __init__(self,start_pt: np.ndarray, end_pt: np.ndarray,\n",
" next : 'PolygonEdge' = None, previous: 'PolygonEdge' = None):\n",
" self.start = start_pt\n",
" delta = end_pt-start_pt\n",
" # difference vector to end point.\n",
" # end = start + delta\n",
" self.delta = delta\n",
" norm = npl.norm(delta)\n",
" if norm > 0:\n",
" self.direction = delta / norm\n",
" else:\n",
" self.direction = None\n",
"\n",
" self.next = next\n",
" if next:\n",
" next.previous = self\n",
"\n",
" self.previous = previous\n",
" if previous:\n",
" previous.next = self\n",
"\n",
" @property\n",
" def end(self):\n",
" '''Get the end vertex of the edge.'''\n",
" if self.next:\n",
" # next edge's start\n",
" return self.next.start\n",
" else:\n",
" # compute it if edge is not part of a polygon\n",
" return self.start + self.delta\n",
"\n",
" def draw(self, ax: matplotlib.axes.Axes, head_size : float = 0.1,\n",
" color = 'black') -> None:\n",
" '''Draw edge.'''\n",
" end = self.end\n",
" ax.arrow(self.start[0],self.start[1],self.delta[0],self.delta[1],\n",
" length_includes_head=True,head_width = head_size / 2, head_length=head_size,\n",
" color=color)\n",
"\n",
" @callcounted\n",
" def distance(self,pt: np.ndarray) -> float:\n",
" ''' Signed distance of a point to the (unbounded) straight line\n",
" of this edge.\n",
" + x\n",
" /│\n",
" / │ d = (x-s) x dir\n",
" / │\n",
" / │\n",
" +----.--->--+ <- edge\n",
" s x' e\n",
" Points to the right have positive distances, points to the left\n",
" have negative distances.\n",
" '''\n",
" return np.cross(pt-self.start,self.direction)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Creating a sample polygon edge named $edge$ bounded by the two vertices $\\vec{p_1}$ and $\\vec{p_2}$."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"p1 = np.array([1,1])\n",
"p2 = np.array([2,3])\n",
"edge = PolygonEdge(p1,p2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Drawing this polygon edge with explanatory annotations."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAMwAAAEWCAYAAAAq8HgPAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nO3dfZzNBfr/8ddlDCYzzEhKlqFCCGNmichN1swmqUdqEYUWxVpbGyu16c7WFhWtZO0mitZdWz+rsSkRodwVipTC102S+xlmMDPX749zzI5xZuZ8ZuZzzpkz1/PxOA/nnM/Nuc4xb9f5fM4xl6gqxhj/VAh2AcaUJRYYYxywwBjjgAXGGAcsMMY4YIExxgELTACJyJMiMjvYdZSEiOwWkV8Fu45gscAUg/eHJkNE0kXkJxF5Q0Sig11XcYjITBE5630u5y+bg11XqLLAFN+tqhoNJAKtgT8HuZ6SeEFVo/NcWga7oFBlgSkhVd0PLAGuAxCRK0VkkYgcFZGdIjLE13Yi8r6I/D7ffVtE5Hbv9WQR2SEiJ0Rkqoh8IiKDvcsqiMifRWSPiBwSkTdFpLp3WX0RUREZICL/JyKHReSx4j4/EbnH+zhH8u9HRKJEZJaIHBOR7SLyJxHZl2f5lSLyjoj8LCK7RGRkcesIFRaYEhKRukB34AvvXf8C9gFXAncCz4pIVx+bzgL659lPS6AOkCoiNYGFwFjgUmAHcEOebQd6L12Aq4BoYEq+/XcAGgNdgXEi0qQYz60p8Bpwj/f5XAr8Is8qTwD1vTV0y/d8KgD/ATZ7n1dX4EERSXFaR0hRVbs4vAC7gXTgOLAHmApEAXWBbCAmz7rPATO9158EZnuvVwaOAg29tycCU73X7wXW5tmHAHuBwd7by4DheZY3Bs4BFfH8ACvwizzL1wF9CnguM4FM73M5f5nlXTYOmJtn3arAWeBX3ts/ACl5lg8G9nmvXw/8X77HGgu8Eey/v5JcKhY7aeZ2Vf0o7x0iciVwVFXT8ty9B/hl/o1V9YyIzAf6i8hTQF88HQk8/5rvzbOu5n2r412+J99jVAQuz3PfwTzXT+PpQgWZqKq+jsHy13FKRI4UtDzf9XjgShE5nue+CGBVIXWEPAtM6ToA1BCRmDyhqQfsL2D9WcBbwKfAaVVd673/R/K89RER4cK3Qgfw/ECeVw/IAn7Kt15J/QjkvpUTkUvwvC3Lu/wXwDbv7bp5lu0Fdqlqw1KsJ+jsGKYUqepeYA3wnIhUEZEWwG+BOQWsvxbIAV7EE5zz3geai8jtIlIR+B1wRZ7l/wIeEpEG3tPZzwLzVDWrlJ/SQqCHiHQQkUrA01z4MzMfGCsicSJSBxiRZ9k64KSIjPGeHIgQketEpHUp1xhQFpjS1xfPccQB4F3gCVX9sJD13wSaA7kfaKrqYeAu4AXgCNAU2ACc8a4yA0/AVgK78ByDXHDGzaE/5fsc5rC3jq/xhPVtPN3kGJ4TGuc97b29C/gIT8DOeLfNBm4FErzLDwP/BKqXoM6gE+/BmAkSEbkXGKqqHQpZpwKeH8x+qro8YMU5JCLD8Jxc6BTsWtxiHSaIvMcEw4HpPpaliEisiFQGHsVzpuyzAJdYKBGpLSLtvZ8LNQYextNVw5ZrgfG+h18nIptF5GvvmaD86wz0fqj1pfcy2K16Qo3384if8Ryov+1jlXbA93jeytyK56xcRuAq9Esl4O9AGvAx8P/wnGIPW669JfOe2amqqukiEonnTNAfVPWzPOsMBH6pqiMK2I0xIcW108rqSWK692ak92IHTKZMc/VzGBGJADYC1wCvqurnPlbrJSIdgW+Bh7ynZvPvZygwFKBKlSpJ9erVc7FqZ3JycqhQIbQOBUOtplCr59tvvz2sqpcVa+NAfJ0AiAWWA9flu/9SoLL3+gPAx0Xtq1GjRhpKli9fHuwSLhJqNYVaPcAGLebPckBir6rHgRXAr/Pdf0RVz3+28A8gKRD1GFNcbp4lu0xEYr3Xo4BfAd/kW6d2nps9ge1u1WNMaXDzGKY2MMt7HFMBmK+qi0XkaTwtcREwUkR64vke1FE8X1k3JmS5eZZsC9DKx/3j8lwfi+cr38aUCaFz6sKYMsACY4wDFhhjHLDAGOOABcYYBywwxjhggTHGAQuMMQ5YYIxxwAJjjAMWGGMcsMAY44AFxhgHLDDGOGCBMcYBC4wxDlhgjHHAAmOMAxYYYxywwBjjgAXGGAcsMMY4YIExxgELjAuiowsbWFx8zz77rCv7Nf6zwJQBqkpOTk5IBCYzMzPYJQSVBcaH2bNn06ZNGxISErj//vvJzs4GPJ3jscceo2XLlrRt25affvoJgF27dtGuXTtat27N448/7nOfY8aMYerU/w3nevLJJ3nxxRcBmDBhAq1bt6ZFixY88cQTAOzevZsmTZowfPhwEhMT+e1vf0tGRgYJCQn069evwDr37NlD//79OXz4MDk5Odx4440sXbq01F6ba665hhtvvJF58+Zx7ty5UttvmVHcX/sfrIvb4y62bdumPXr00LNnz6qq6rBhw3TWrFmqqgrookWLVFV19OjR+swzz+jy5cv11ltvzV1nypQpWrVq1Yv2u2nTJu3YsWPu7SZNmuiePXv0gw8+0CFDhmhOTo5mZ2frLbfcop988onu2rVLRUTXrl2bu03e/RZW56hRo7RXr176wgsv6NChQ0vz5dH4+Hh96aWXtHPnzlq7dm198skn9cCBA4VuY+Mu/ODnjMvKIjJPRHaKyOciUt+tevy1bNkyNm7cSOvWrUlISGDZsmX88MMPAFSqVIkePXoAkJSUxO7duwFYvXo1ffv2BeCee+7xud9WrVpx6NAhDhw4wObNm4mLi6NevXosXbqUpUuX0qpVKxITE/nmm2/47rvvAIiPj6dt27aO67zllltIS0tj2rRpTJw4sdReG4C4uDg6derE8uXLWbp0KQcPHqRp06b07duX1atXn5/7E7bc/O39Z4CbNM+MSxFZonlmXAK/BY6p6jUi0gd4HujtYk1FUlUGDBjAc889d9GyyMhIPKM7ISIigqysrNxl5+8vzJ133snChQs5ePAgffr0yX28sWPHcv/991+w7u7du6latWqx6szMzGTfvn0ApKenExMTU2RtRVFVTp8+zSWXXMKxY8cAuO6663jttdd47rnnmDlzJoMGDaJq1aqMGDGCvn37snTHMSZ8sIM+ddN47K8fMzqlMbe3qlPiWoIp2DMubwOe9F5fCEwREdEg/jPVtWtXbrvtNh566CFq1arF0aNHSUtLIz4+vsBt2rdvz9y5c+nfvz9z5swpcL0+ffowZMgQDh8+zCeffAJASkoKjz/+OP369SM6Opr9+/cTGRnpc/vIyEjOnTtHZGRkoXVOnz6dfv36ER8fz5AhQ1i8eDEA2dnZHD9+nOPHj3Ps2DGOHTuWe92fPytWrEhcXBzVqlW7oK7Y2FgefPBBRo4cyYcffsiUKVP446g/UfHaLlRpeTPUrcn+4xmM/fdWgDIdmmDPuKwD7AVQ1SwROYFnjN9hN+sqTNOmTRk/fjzJycnk5OQQGRnJq6++WmhgJk+ezN13383kyZPp1atXges1a9aMtLQ06tSpQ+3anllSycnJbN++nXbt2gGeEwuzZ88mIiLiou2HDh1KixYtSExMZM6cOT7r3L17N9988w0LFiwgIiKCd955hzfeeINBgwYxYMAAFi1axGWXXUZsbCxxcXEX/Vm3bl2f98fGxlK5cuVCX7sKFSqQkpJCSkoKSX+azfZ//42jfx/Mt7FPAr8k41w2Ez7YUaYD49rY8QsexDOJ7F3g96r6VZ77vwZSVHWf9/b3QBtVPZJv+9yhsJdddlnS/PnzXa/ZX+np6a597lJcBdU0fvx4rr/+erp16+bq46elpTFxyt/ZtGYlv/pVV37Ttx+Hz/2vazavU93Vxy9Kly5dNqrqL4u1cXHPFji9AE8Ao/Ld9wHQznu9Ip7OIoXtx4bCFq2gmoYPH65/+9vfir3fjIwMPXDggGZkZPhcnpWVpdOmTdNatWpptataKhUqKlJBX3nrXY0fs1jjxyzWG55bVuzHLy2U4CyZa2/JROQy4JyqHs8z4/L5fKstAgYAa4E78UxRDu/TLEEUFxfH/v37+eGHHxwdu5y/rqpERkYyePBgJk2adMG+V61axciRI6lWrRp/+ctf+N2I30NOFhVr1AHvCZGoyAhGpzQOxlMvNcGecfk68JaI7MQz47KPi/WUe9dccw0PPvggc+fOLfAYpnbt2gUew0RFRTF79mxSU1Nz97l3715Gjx7NmjVrmDBhAi1atOD666/n7BnPNwIubZgIQJ3YKDtLVhj1b8ZlJnCXWzWYCw0cOJCBAweWaB9xcXEcP36cjIwMJk6cyKRJkxgxYgQzZszg1KlTtGjRgvR0z8nRmJgYJj/Uj8svr87v+3Uu+RMIAa6eJTPhJzY2li1bttC0aVOSkpLYuHEj9evXJzMzk27dunHkyJHcDy/PnTtH27Zt2bVrV5CrLj32XTLjyNVXX03jxo15/fXXWbhwIfXr1ycnJ4fevXuzY8eOC75fVrlyZerVqxfEakufdRjjyBVXXMGyZcsuuO+RRx7ho48+uuibzK1bt/brGxBliXUYUyIzZszg1Vdf5fTp0xfcX6lSJZKTk4NUlXusw5hiW758OSNGjCAjI+OiZVWqVOGGG24IQlXusg5jiuWbb76hZ8+ePsMCkJGRQWJiYoCrcp8Fxjj2888/06VLF06dOlXgOg0aNCAqKiqAVQWGBcY44uv0sS9dunQJYFWBY4Exfivo9HF+MTExdO7cOXCFBZAFxvitoNPH+Z07dy73vyuEGwuM8cvrr7/u8/SxL+H4geV5dlrZFCknJ4fhw4dz9uxZv9YPxw8sz7MOY4pUoUIFUlNTSUhIKPT3DED4fmB5ngXG+KVr165s2rSJRYsWkZCQUOB/V65SpUrYHr+ABcY4ICLcdNNNbNq0iTNnzgBc1HFOnz5NUlJSMMoLCAuMcezTTz8FYPv27bkd53xwrrrqqrD8wPI8C4xxrGPHjgBce+21uR1n0aJFJCUlcd999wW5OnfZWTLjyKpVqwBPdznv/Fu1DRs2BKusgLEOYxzJ213KIwuM8Zuv7lLeWGCM38p7dwELjPGTdRcPC4zxi3UXDwuMKZJ1l/+xwJQCf34Z+SuvvEKTJk3o168fK1asYM2aNQGorHRYd/kfC0yATJ06ldTUVObMmVOmAmPd5UIWmFLma8DrAw88wA8//EDPnj15+eWXmTZtGi+//DIJCQm5P5ChyrrLheyT/lK0dOlSvvvuO9atW4eq0rNnT1auXMm0adP473//y/Lly6lZsyYnTpwgOjqaUaNGBbvkQll3uZibQ2HrishyEdnuHQr7Bx/rdBaREyLypfcyzte+yorCBryWRdZdLuZmh8kCHlbVTSISA2wUkQ9VdVu+9Vapag8X6wgYLWDAa1lk3cU31zqMqv6oqpu819OA7XhmWoatlJQUZsyYkTvuYf/+/Rw6dOii9WJiYkhLSwt0eY5Yd/EtIMcwIlIfz6yY/ENhAdqJyGbgAJ6Rfl/72D7vjEtWrFjhWq1Opaenk52dzYoVK6hUqVLuAT9AVFQUjz76KHXq1CEzM5PVq1dTvXp1atWqxdSpU5kzZw4jR47MXb80ayrJa5Sens7EiRNp1qxZqbzWJa0npBR31p+/FyAazyTlO3wsqwZEe693B74ran8247JoJa0Jz3j40ilGQ+81ogQzLl09rSwikcA7wBxV/bePsJ5U1XTv9VQgUkRqulmTKZwduxTOzbNkgmeG5XZVfamAda7wroeItPHWc8TXuiYw7NilcG4ew7QH7gG2isiX3vseBeoBqOo0PJOTh4lIFpAB9PG2TBME1l2K5uZQ2E+BQn+bm6pOAaa4VYNxxrpL0eyrMQaw7uIvC4wBrLv4ywJjrLs4YIEx1l0csMCUc9ZdnLHAlHPWXZyxwJRj1l2cs8CUY9ZdnLPAlFPWXYrHAlNOWXcpHgtMOWTdpfgsMOWQdZfis8CUM9ZdSsYCU85YdykZC0w5Yt2l5Cww5Yh1l5KzwJQT1l1KhwWmnLDuUjosMOWAdZfSY4EpB6y7lB4LTJiz7lK6LDBhzrpL6bLAhDHrLqXPAhPGrLuUPgtMmLLu4g4LTJiy7uIOC0wYOj/QybpL6Qv2jEsRkVdEZKeIbBGRRLfqKU927NgBWHdxQ7BnXN4MNPRergde8/5pismOXdxVZIcRkREiEud0x+rfjMvbgDe9g6E+A2JFpLbTxzL/Y8cu7vKnw1wBrBeRTcAM4AOnM1wKmXFZB9ib5/Y+730/5ts+pGdchko952dTNmzYMGRqgtB6jUrMn7l+eOa8pABzgZ3As8DVfm5b2IzL94EOeW4vA5IK25/NuCwY3tmUoVSTami9RqoBmHHpfZCD3ksWEAcsFJEXCtuuqBmXeDpK3Ty3f4FnmrJxyI5dAsOfY5iRIrIReAFYDTRX1WFAEtCrkO2KnHEJLALu9Z4tawucUNUfC1jXFMKOXQLDn2OYmnjeTu3Je6eq5ohIj0K282fGZSqeceM7gdPAIGflG7DuEkhFBkZVxxWyrMC/IfVvxqUCvyuqBlM46y6BY5/0l3HWXQLLAlPGWXcJLAtMGWbdJfAsMGWYdZfAs8CUUdZdgsMCU0ZZdwkOC0wZZN0leCwwZZB1l+CxwJQx1l2CywJTxlh3CS4LTBli3SX4LDBliHWX4LPAlBHWXUKDBaaMsO4SGiwwZYB1l9BhgSkDrLuEDgtMiLPuElosMCHOuktoscCEMOsuoccCE8Ksu4QeC0yIsu4SmiwwIcq6S2iywIQg6y6hywITgspDd+nevTvHjx8vdJ3OnTuzYcOGi+7/8ssvSU1Ndau0QllgQkx56C6qyuLFi4mNjS3W9hYYkytcu8vu3btp0qQJw4cPJzExkYiICA4fPgzAM888w7XXXku3bt3o27cvEydOzN1uwYIFtGnThkaNGrFq1SrOnj3LuHHjmDdvHgkJCcybNy+gz8PNCWTGoXDvLjt27OCNN95g6tSp1K9fH4ANGzbwzjvv8MUXX5CVlUViYiJJSUm522RlZbFu3TpSU1N56qmn+Oijj3j66afZsGEDU6ZMCfhzcHPG5QwROSQiXxWwvLOInBCRL72XAn+Hc3kRrt3lvPj4eNq2bXvBfZ9++im33XYbUVFRxMTEcOutt16w/I477gAgKSmJ3bt3B6rUArnZYWYCU4A3C1lnlaoWNgGg3Aj37gJQtWrVi+7TIobZVa5cGYCIiAiysrJcqcsJ1zqMqq4Ejrq1/3AT7t2lIB06dOA///kPmZmZpKen8/777xe5TUxMDGlpaQGo7mLBPoZpJyKb8UwdG6WqX/taKdxnXJ6fTdmsWbNSeW6hNlMyPT2dzz77jFOnTuXWlZmZyerVq6levTotWrSgUaNGXH755dStW5eDBw+yYsUKjh8/zsaNG0lPT+fEiRNkZmayYsUKIiMj+fzzz7nmmmu4++67uemmmwL3ZIo768+fC1Af+KqAZdWAaO/17sB3/uwzHGdc4p1NWVpCbaZkUfWkpaWpquqpU6c0KSlJN27c6Go9uD3j0g2qelJV073XU4FIEakZrHqCpTwcuxRl6NChJCQkkJiYSK9evUhMTAx2SQUK2lsyEbkC+ElVVUTa4DmeOhKseoKlvB675PX2228HuwS/uRYYEfkX0BmoKSL7gCeASMidb3knMExEsoAMoI+3XZYb1l3KHtcCo6p9i1g+Bc9p53LLukvZY1+NCRLrLmWTBSZIrLuUTRaYILDuUnZZYILAukvZZYEJMOsuZZsFJsCsu5RtFpgAsu5S9llgAsi6S9lngQkQ6y7hwQITINZdwoMFJgCsu4QPC0wAWHcJHxYYl1l3CS8WGJdZdwkvFhgXWXcJPxYYF1l3CT8WGJdYdwlPFhiXWHcJTxYYF1h3CV8WGBdYdwlfFphSZt0lvFlgSpl1l/BmgSlF1l3CnwWmFIVCd5k0aRKZmZmOt5s5cyYHDhxwoSLPDJht27a5su9As8CUEn+7y+nTp8nJyXGtjkmTJnHmzBlH22RnZ7sWmKysrLAKjKu/vd+NS6j+9n6K+A3869at0wEDBmjlypV12bJlJX7c9PR07d69u7Zo0UKbNWumc+fO1cmTJ2tkZKQ2aNBAO3furKqqDzzwgCYlJWnTpk113LhxudvHx8frU089pe3bt9e33npLq1atqo0aNdKWLVvq6dOnc9fbtm2btm7dOvf2rl27tHnz5qqqumHDBu3YsaMmJiZqcnKyHjhwQFVVO3XqpGPHjtWOHTvq+PHjNSYmRuvXr68tW7bUnTt36s6dOzUlJUUTExO1Q4cOun37dlVV7dmzp86aNUtVVadNm6Z33313iV8nXyjBb+8PegCcXkIxMCtXrlQg9y/+vIyMDJ01a5a2adNG4+Pj9fnnn9fmzZvrqlWrSvy4Cxcu1MGDB+fePn78uKp6gvDee+/l3n/kyBFVVc3KytJOnTrp5s2bc9d7/vnnc9fr1KmTrl+/3udjtWzZUr///ntVVf3rX/+qzzzzjJ49e1bbtWunhw4dUlXVuXPn6qBBg3L3NWzYsNztU1JSdMGCBbm3b7rpJv32229VVfWzzz7TLl26qKrqwYMH9eqrr9aVK1dqw4YNc2svbSUJjJu/jHwG0AM4pKrX+VguwGQ8s2FOAwNVdZNb9bgp/7HLnj17mDZtGjNmzKBVq1b8+c9/pnv37kRERDBz5sxij9vOq3nz5owaNYoxY8bQo0cPbrzxRp/rzZ8/n+nTp5OVlcWPP/7Itm3baNGiBQC9e/f267F+85vfMH/+fB555BHmzZvHvHnz2LFjB1999RXdunUDPG/rateunbtNQftOT09nzZo13HXXXbn3nX8Lefnll/P000/TpUsX3n33XWrUqOFXfYEUzBmXNwMNvZfrgde8f5YJ732xnwkf7KD35YcAmLzgYz788ENeffVVVq1axb333suqVato1KjRBdsdO3YM8Ly3r1ix+C9/o0aN2LhxI6mpqYwdO5bk5GTGjbtwru6uXbuYOHEi69evJy4ujoEDB15wQsDXzElfevfuzV133cUdd9yBiNCwYUO2bt1Ks2bNWLt2rc9tCtp3Tk4OsbGxfPnllz6Xb926lUsvvdS1ExAl5eZv718pIvULWeU24E1vi/xMRGJFpLaq/uhWTaXlvS/2M/bfW8k4l83JrJ0AjL7/Hq68tBpjRz3InDlzCvyBadiwIR07duTkyZNccskl1KhRg7i4OL8vtWrVonr16hw4cIAaNWrQv39/oqOjmTlzJuCZ/3j69GkATp48SdWqValevTo//fQTS5YsoXPnzj7rKmxu5NVXX01ERATPPPNMbudo3LgxP//8M2vXrqVdu3acO3eOb7/9lmbNml20/SWXXJK772rVqtGgQQMWLFjAXXfdhaqyZcsWWrZsybp161iyZAlffPEFnTp1Ijk5mQYNGvj99xIIwZxxWQfYm+f2Pu99IR+YCR/sIONcNjk52UycOJEKUdWISxlB7eatGTq0a6Hbrly5EvD8S3vy5EmOHTtW4GXPnj0X3bd3715OnDjB1q1bGT16NBUqVCAyMpLXXnsN8EzzGjNmDNOnT2f58uW0atWKZs2acdVVV9G+ffsC6xo4cCAPPPAAUVFRrF27lqioqAuW9+7dm9GjR7Nr1y4AKlWqxMKFCxk5ciQnTpwgKyuLBx980GdgunTpwoQJE3jllVdYuHAhc+bMYdiwYYwfP55z587Rp08frr32WoYMGcIbb7zBlVdeyYsvvsh9993Hxx9/jOfde2gQdXGGkbfDLC7gGOZ94DlV/dR7exnwJ1Xd6GPdvENhk+bPn+9azf7Yuv9E7vXTP/7A9H/8g7NnznDngCHc1qVtIVuWTHZ2NsnJyXz44YdUqFDwJwLp6elER0e7VodToVZPly5dNqrqL4uzbTA7zD6gbp7bv8AzTfkiqjodmA7QuHFjLehtRaA89teP2X88A4CHm19F1u0vkLl9JVMnv8TP3yfz/PPPU6dOnYu2U1X27t1LdHQ01atXJyIiwtHjHj16lGrVqhU5NXjFihUFvvUKhlCrpySCGZhFwAgRmYvnYP9EWTh+ARid0jj3GAZARKjZ8iZeGTOYr1Jn0bJlS0aNGsVDDz1E5cqVc7dbv349HTp0yH1PHxMT4+gYJi0tLSTPHJUnwZxxmYrnlPJOPKeVB7lVS2m7vZWne0z4YAeQRp3YKEanNPbc3/4v3Hffffzxj3/k9ddfZ9KkSdxyyy0A/PTTTyQnJ7N48WKys7M5ceJEgccvR48e5fvvv7/o/qSkpCA+cxP0DyKdXkLxg8uCLFmyRBs3bqw333yz7tixQ2fOnKn9+/cPak3BEGr1UIIPLu27ZC769a9/zZYtW+jatSs33HADkydPJi4uLthlmRKwwLisUqVKPPzww3z11VckJibStq17Z9GM+4J50F+uXHHFFfzzn/8MdhmmhKzDGOOABcYYBywwxjhggTHGAQuMMQ5YYIxxwAJjjAMWGGMcsMAY44AFxhgHLDDGOGCBMcYBC4wxDlhgjHHAAmOMAxYYYxywwBjjgAXGGAcsMMY4YIExxgELjDEOWGCMccACY4wDFhhjHLDAGOOAq4ERkV+LyA4R2Skij/hYPlBEfhaRL72XwW7WY0xJuTnuIgJ4FeiGZ3jSehFZpKrb8q06T1VHuFWHMaXJzQ7TBtipqj+o6llgLp5BsMaUWW7+MnJfQ199jRXvJSIdgW+Bh1R1b/4V8s64BM6IyFelXWwJ1AQOB7uIfEKtplCrp3FxN3QzML5G3+afQPsf4F+qekZEHgBmARcNcNQ8My5FZIMWc6CnG0KtHgi9mkKxnuJu6+ZbsiKHvqrqEVU94735D8Dm0ZmQ5mZg1gMNRaSBiFQC+uAZBJtLRGrnudkT2O5iPcaUmGtvyVQ1S0RGAB8AEcAMVf1aRJ7GM2NwETBSRHoCWcBRYKAfu57uVs3FFGr1QOjVFDb1iGdGpjHGH/ZJvzEOWGCMcSBkAxNqX6sRkRkicqigz4DE4xVvvVtEJDHI9XQWkfGH6/4AAAKDSURBVBN5Xp9xLtdTV0SWi8h2EflaRP7gY52AvUZ+1uP8NVLVkLvgOUnwPXAVUAnYDDTNt85AYEoAa+oIJAJfFbC8O7AEz+dPbYHPg1xPZ2BxAF+f2kCi93oMng+i8/+dBew18rMex69RqHaYkPtajaquxHMmryC3AW+qx2dAbL7T5oGuJ6BU9UdV3eS9nobnI4I6+VYL2GvkZz2OhWpgfH2txteT7eVt7QtFpK6P5YHkb82B1E5ENovIEhFpFqgHFZH6QCvg83yLgvIaFVIPOHyNQjUw/n6tpr6qtgA+wvO1mmDyp+ZA2gTEq2pL4G/Ae4F4UBGJBt4BHlTVk/kX+9jE1deoiHocv0ahGpiy+LWaImsOJFU9qarp3uupQKSI1HTzMUUkEs8P5xxV/bePVQL6GhVVT3Feo1ANTFn8Ws0i4F7vmaC2wAlV/TFYxYjIFSIi3utt8PxdH3Hx8QR4Hdiuqi8VsFrAXiN/6inOa+Tmt5WLTd37Wk2xici/8JxVqSki+4AngEhvvdOAVDxngXYCp4FBQa7nTmCYiGQBGUAf9Z4ackl74B5gq4h86b3vUaBenpoC+Rr5U4/j18i+GmOMA6H6lsyYkGSBMcYBC4wxDlhgjHHAAmOMAxYYYxywwBjjgAUmTIhIa+8XUauISFXv/wG5Lth1hRv74DKMiMh4oAoQBexT1eeCXFLYscCEEe/37tYDmcANqpod5JLCjr0lCy81gGg8/8OwSpBrCUvWYcKIiCzC879TGwC11aYilLqQ/LaycU5E7gWyVPVt8YwaWSMiN6nqx8GuLZxYhzHGATuGMcYBC4wxDlhgjHHAAmOMAxYYYxywwBjjgAXGGAf+Py8UyPYamCfUAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system(1,dx=(0.5,2.5),dy=(0.5,3.5))\n",
"edge.draw(ax,0.3)\n",
"ax.scatter([p1[0],p2[0]],[p1[1],p2[1]])\n",
"ax.annotate('left',(1,2.5))\n",
"ax.annotate('right',(2,1.5))\n",
"ax.annotate('start vertex',p1, xytext = (30,0),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.annotate('end vertex',p2, xytext = (-85,0),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.set_title('Polygon Edge')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Polygon Class\n",
"\n",
"To work with a closed loop of oriented polygon edges we define a class\n",
"representing a set of connected edges.\n",
"\n",
"$$\n",
"PG_m(P_n) = \\left\\{ e_j(\\vec{a_j},\\vec{b_j}) \\;\\big|\\;\n",
"j = 1 \\dots m\n",
"\\wedge\n",
"\\vec{b_{j}} = \\vec{a_{j+1}}\n",
"\\wedge\n",
"\\vec{b_{m}} = \\vec{a_{1}}\n",
"\\right\\}\n",
"$$\n",
"\n",
"where $PG_m(P_n)$ represents a polygon with $m$ edges defined on points of the point cloud $P_n$.\n",
"\n",
"We will use this class to:\n",
"* create a polygon from an ordered set of points.\n",
"* Iterate over the vertices or edges of the polygon\n",
"* draw the polygon"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"class Polygon:\n",
" def __init__(self, points: Iterable[np.ndarray] = None):\n",
" '''Construct a polygon from an ordered lis of points.'''\n",
" if not points: return\n",
"\n",
" point_itr = iter(points)\n",
"\n",
" start_pt = next(point_itr)\n",
" end_pt = next(point_itr)\n",
" loop_start = PolygonEdge(start_pt, end_pt)\n",
" start_pt = end_pt\n",
" previous = loop_start\n",
" # process remaining points\n",
" for p in point_itr:\n",
" previous = PolygonEdge(start_pt, p, next = loop_start, previous = previous)\n",
" start_pt = p\n",
"\n",
" PolygonEdge(start_pt,loop_start.start,next = loop_start,previous=previous)\n",
" self.loop_start = loop_start\n",
"\n",
" @property\n",
" def vertices(self) -> Iterable[np.ndarray]:\n",
" '''Generator to iterate over all vertices of the polygon.'''\n",
" for edge in self.edges:\n",
" yield edge.start\n",
"\n",
" @property\n",
" def edges(self) -> Iterable[PolygonEdge]:\n",
" '''Generator to iterate over all edges of the polygon.'''\n",
" edge = self.loop_start\n",
" loop_end = edge.previous\n",
" while not edge is loop_end:\n",
" yield edge\n",
" edge=edge.next\n",
" yield loop_end\n",
"\n",
" def draw(self,ax: matplotlib.axes.Axes, head_size : float = 0.1) -> None:\n",
" '''Draw the polygon to a subplot.'''\n",
" for edge in self.edges:\n",
" edge.draw(ax,head_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now create a polygon from a counter clockwise sequence of vertices like so:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"polygon = Polygon([np.array([1,1]), np.array([2.5,1.5]), np.array([3,2]),\n",
" np.array([2,2]), np.array([1.75,1.6]), np.array([1.25,1.8])])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This polygon can easily be drawn using its `draw` method:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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dm7j5z//OYmJidW2KSwsZMWKFRw7dqyZo20eds9E+LEkET68vOrp+jrW7wcu9+r7Te2WLVsGwI5XnqRVq1YnrZs7dy5XX30148aNo7i4mNLSUlq3bs3x48eZM2cOc+bMISEhgbfffpvBgwf7EX7Ibd++nZUrV9o9E2HEkkR4sTuzW6CKLqSqSaLCVVddxfbt2xk6dCiJiYnExMTwxRdfUFZWxnPPPUdJSQlDhgxBRPj3f//3iG9l2D0T4cWSRPixRNECHTp0iLvuuitomc6dO/POO+8wffp0fvnLX9K9e3diYmKYPHkyqsqePXsYNGgQs2bNIj4+nsTERDZs2NBMNQgdu2civFiSCE+WKFqYnJwcAH7729/WWTYmJoa7776bJ598stq6rl27kpOTE9GtDLtnIrxYkghflihamJtvvhmg8n/IpgrWymjbtm1lYgpHNs9E+LAkEd4sUbQwGzduZOzYsZ7sO7CVMW/ePI4cOcLgwYPDspXx+uuvU1paavNMhAFLEuHPEkUL8uWXXwIwc+ZMT78nJiaGm2++ucZWxqZNm3xpZRQWFpKVlUVpaandMxFGLElEBksULcgdd9wBQLdu3ZrtO6u2MsrLy31pZcyfP5+xY8dy7rnnctZZZwF2z4TfLElEDksULcirr77a6GcYNVVFK2PQoEG+jGVkZmZSVlbGzp072bVrF7169aK01B5W7BdLEpHFEkULkZ+fDzh/Wfutuccy9u/fzyeffHLSsi+//JKePXuyc+fOkH6XqZslichjiaKFeOihhwAYOHCgz5F8L9hYRihbGUuWLKn2yPSjR49y/PjxigdShtRFF13UqO3mzZvHwoXV5/navXs3ffpUewBzRLIkEZksUbQQTz75ZOXloOHIy1ZGZmYmR44cOWlZcnIyq1evJj09vamhV7N27dpGbTdlyhRuuOGGEEcTPixJRC5LFC3A8ePO7LILFizwOZK6hbqV8c0337Bly5bKzyLCKaecwtq1a+nXr1+owweonNMgOzub4cOHM3bsWM4++2zGjRtX2YK555576N27N3379q28S37GjBk88cQTAOTm5tKvXz8uvPBC5syZU7nvsrIypk6dyuDBg+nbty/PPfecJ3UINUsSkc0SRQtQcTK5+uqrfY6kYULRysjKyqp8plVMTAwdO3Zk3bp19O7d2+vwAdi0aROzZs3i448/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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system(1,dx=(0.5,3.5),dy=(0.75,2.2))\n",
"\n",
"ax.scatter([p[0] for p in polygon.vertices],[p[1] for p in polygon.vertices])\n",
"ax.annotate('inside',(2,1.6))\n",
"ax.annotate('outside',(2,1.2))\n",
"ax.set_title('Counter-Clockwise Polygon')\n",
"polygon.draw(ax)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2D Convex Hulls from a Point Cloud\n",
"A [convex hull](https://medium.com/@pascal.sommer.ch/a-gentle-introduction-to-the-convex-hull-problem-62dfcabee90c)\n",
"is the smallest convex polygon, that encloses all of the points in a point cloud.\n",
"We build a convex hull from an unordered point cloud by using a subdivision technique known as [QuickHull](https://en.wikipedia.org/wiki/Quickhull). The Quickhull performance characteristic is\n",
"known to be $O(n \\cdot log(n))$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## OuterSector Class\n",
"\n",
"Computation of a convex hull requires some means to determine whether points are outside or inside of a\n",
"convex hull. We already have a way to determine if point is left or right to the (unbounded) straight\n",
"line of a edge by calculating its signed distance. With this we can subdivide a point cloud using\n",
"the signed distance method of edges.\n",
"\n",
"We define a class which describes the subset of points $OS(e)$ with $OS(e) \\subset P_n$ where\n",
"all points of $OS(e)$ are to the right of the polygon edge $e(\\vec{a},\\vec{b},)$:\n",
"\n",
"$$\n",
"OS(e) = \\left\\{ \\vec{p} \\;\\big|\\;\n",
"\\vec{p} \\in P_n\n",
"\\wedge\n",
"dist(e,\\vec{p}) > 0\n",
"\\right\\}\n",
"$$\n",
"\n",
"where $e$ is a shorthand notation for the edge $e(\\vec{a},\\vec{b})$.\n",
"\n",
"With this, a convex hull $C_m(P_n)$ of the point cloud $P_n$ can be described as a\n",
"polygon $PG_m(P_n)$ where there are no points of $P_n$ outside (to the _right_) of any of its edges:\n",
"\n",
"$$\n",
"C_m(P_n) = PG_m(P_n) \\Longleftrightarrow \\forall e \\in PG_m(P_n) \\;\\big|\\; OS(e) = \\emptyset\n",
"$$\n",
"\n",
"We define a utility class (`OuterSector`) which represents the set $OS(e)$\n",
"and use this class to iteratively build a polygon until $OS(e) = \\emptyset$\n",
"for all edges of that polygon.\n",
"\n",
"The `OuterSector` implementation shall be able to:\n",
"* classify points in a point cloud as _right_ or _left_ relative to an oriented section line\n",
" (represented by a `PolygonEdge` instance).\n",
"* compute the outermost point (apogee) for _right_ (outside) points. An apogee, by definition,\n",
" is a point on the convex hull.\n",
"* subdivide its defining edge."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"class OuterSector:\n",
" def __init__(self,section_line: PolygonEdge):\n",
" '''Use a polygon edge to define a sector.'''\n",
" self.section_line = section_line\n",
" self.outer_points = cl.deque()\n",
" self.apogee = None\n",
" self._peak_distance = -1\n",
"\n",
" def add_points(self, points: Iterable[np.ndarray]) -> Iterable[np.ndarray]:\n",
" '''Add points to the sector. Outer points are recorded, Inner points are\n",
" returned as a point collection.'''\n",
" inner_points = cl.deque()\n",
" point_itr = iter(points)\n",
" for p in point_itr:\n",
" dist = self.section_line.distance(p)\n",
" if dist > 0:\n",
" if dist > self._peak_distance:\n",
" if not self.apogee is None:\n",
" # put ols apogee back into the pool\n",
" self.outer_points.append(self.apogee)\n",
" self.apogee = p\n",
" self._peak_distance = dist\n",
" else:\n",
" self.outer_points.append(p)\n",
" else:\n",
" # point on the section line or inside\n",
" # (left to the section line)\n",
" inner_points.append(p)\n",
" return inner_points\n",
"\n",
" def subdivide(self) -> Tuple['OuterSector','OuterSector']:\n",
" '''Subdivide into 2 section lines joined at the apogee'''\n",
" if not self.apogee is None:\n",
" section_line1 = PolygonEdge(self.section_line.start,self.apogee,\n",
" previous = self.section_line.previous)\n",
" sector1 = OuterSector(section_line1)\n",
" remaining_points = sector1.add_points(self.outer_points)\n",
" section_line2 = PolygonEdge(self.apogee,self.section_line.end,\n",
" next = self.section_line.next,\n",
" previous = section_line1)\n",
"\n",
" sector2 = OuterSector(section_line2)\n",
" sector2.add_points(remaining_points)\n",
" return (sector1, sector2)\n",
"\n",
" def draw(self,ax: matplotlib.axes.Axes, head_size : float = 0.1) -> None:\n",
" '''Draw the sector'''\n",
" ax.scatter([p[0] for p in self.outer_points],[p[1] for p in self.outer_points], color='blue')\n",
" # draw start and endpoints\n",
" start = self.section_line.start\n",
" end = self.section_line.end\n",
" ax.scatter([start[0],end[0]],[start[1],end[1]],color='blue')\n",
" if not self.apogee is None:\n",
" ax.scatter([self.apogee[0]],[self.apogee[1]], marker = 'X', s=120, c = 'red')\n",
" self.section_line.draw(ax,head_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's experiment with this class on a randomly created point cloud."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"n=33 # point cloud size\n",
"point_cloud=[np.random.random(2)*100 for _ in range(n)] # n random points"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we create an edge with can serve as a section line for now (thoug its vertices are not from the point\n",
"cloud $P_n$). We pick start and end vertex so that the\n",
"edge somewhat bisects the point cloud."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"section_line = PolygonEdge(np.array([0,0]),np.array([100,100]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally we can construct a sector from that section line and the sample point cloud."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"sector = OuterSector(section_line)\n",
"inner_points = sector.add_points(point_cloud)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the figure below we see how the `OuterSector` instance subdivided the point cloud.\n",
"\n",
"Points to the right of the section line are represented as blue dots. These points are _recorded_\n",
"by the `OuterSector` instance. They are also are used to compute the _apogee_.\n",
"\n",
"Green dots represent left points which are **not** recorded in this instance `OuterSector`.\n",
"\n",
"We note that:\n",
"* the apogee is also a vertex on the convex hull (if it exists).\n",
"* if an _OuterSector_ instance contains no _right_ points, it is an edge of the convex hull,\n",
" provided its end vertices are also vertices of the convex hull polygon. "
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system()\n",
"\n",
"ax.scatter([p[0] for p in inner_points],[p[1] for p in inner_points], color='green')\n",
"\n",
"sector.draw(ax,10)\n",
"\n",
"ax.annotate('apogee',sector.apogee,\n",
" xytext = (30,0),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.annotate('left (inside)',(0,105))\n",
"ax.annotate('right (outside)',(80,-5))\n",
"ax.set_title(\"Subdivided Point Cloud\")\n",
"\n",
"ax.annotate('section line', (70,70),\n",
" xytext = (30,0),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using this we can demonstrate the effect of a sector subdivision where this `OuterSector`\n",
"instance is subdivided at its apogee. For comparison we create a new sector with the same\n",
"specs as before which we then subdivide."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"sector2 = OuterSector(PolygonEdge(np.array([0,0]),np.array([100,100])))\n",
"inner_points2 = sector2.add_points(point_cloud)\n",
"subsectors = sector2.subdivide()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To illustrate the subdivision effect we draw the original sector and the subdivided sector\n",
"side-by-side:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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71694oorND09XdPT0/XDDz9UVdW8vDy97rrrND09Xfv06aM43TWe3/dru3luQKkhTn/ct0BXoD7wBdCzqmOqu8hVnQu6c2fnIu/cOUov8Crw+iYfSryuWyQLmGqM+fbrr6vu2lUmKeOdd5z0chw4cEAnT56sLVu21KZNm+oXX3wRFBO2bNmi8fHx+vnnn6uq6qhRo/S5555TVdVzzz1XN23apKqqK1eu1CFDhqiq6tChQ3XdunX6xhtvaHp6uk6bNk0PHz6sXbp0Oab8n376SQ8dOqQZGRm6adMm9f3GGRkZ2qBBA/3mm2+0qKhIzz//fH3xxRd1586d2rFjR927d68WFhbqkCFD9NVXX9WdO3dq586d9ccff9SCggIdOHBgqYBdddVVunz5clVV3bZtm3bv3l1VVadMmVJal5ycHMWZAa6RRsC9vzZbxARxqGqRiNwM/AcnjH6Oqq6vbbm+hRqXLnWmdTKMWCGmfLui4J969eCCC45JbtKkCdOnT+f666/nkUceISUlJWhmdO3alT59+gCQlpbG1q1bycvLY8WKFYwaNao035EjRwAYNGgQy5YtY8uWLUyZMoV//vOfnHPOOZxxxhnHlF1YWMjNN9/MRx99RNOmTdm0aVPpvn79+pXW46qrruLDDz+kXr16DB48mFatWgHObCPLli0D4JxzzimNshw1alRpWe+99x5ffXU07u3AgQMcPHiQd955h9dff50HH3zQt0uATsCGWn9pHhIxAgagqm8Db3tth2EYkU+3bt1Kp5IKFg0aNCh9Hx8fz6FDhygpKaF58+asXbv2mPyDBg1i5syZ7Nq1i3vuuYe///3vLF26lLPPPvuYvA8//DBt2rRh9uzZnH322SQmJpbuKx90IyK+lqljqCwdoKSkhI8//viYZWJUlZdffplTTz3VV36mqka1eEEdn8zXMAyjOpo2bUrXrl158cUXAUcMvvjiCwD69+/PihUriIuLIzExkT59+vD0008zaNCgY8rJzc2lbdu2xMXF8dxzz1HsN0Pwp59+ypYtWygpKeGFF15g4MCB9O/fnw8++IB9+/ZRXFzM888/zznnnEO/fv344IMPyMnJoaioiJdffrm0nAsvvLCMqPtEd+jQoTz22GP+4hcTK3WagBmGYVTDggULeOaZZ+jduzc9e/bktddeA5wnto4dOzJgwADAeSI7ePAgP/vZz44p46abbmLevHncdNNNbNq0iUaNGpXuO/PMM5k8eTKpqal07dqVyy+/nLZt2zJ9+nSGDBlC79696du3L8OHD6d9+/ZMnTqV/v37c/7559OjRw+aNWsGOCtJr1q1il69etGjRw9mzpwJwF/+8hcKCwvp1asXqampAO1D+42FCa874WqzBdLR7cPrgIBQEav1UvW+bkR4EIcPr7+nUFFX6pWRkaGXXnppjco4ePCgqqoWFhbqsGHD9JVXXqnR8V76djA3ewIzDMOIMu6++2769OlT+sQ2YsQIr03yhIgK4jAMw6hrDB48mMGDB9foGL9owjpNxEwldTyIyA9ABdPbVsgJwL4QmuMVsVov8L5unVW1lRcnNt8GrF6hxDPfDiZRLWA1QURWaSzM/VWOWK0XxHbdgkmsfk9WL6M6rA/MMAzDiEpMwAzDMIyopC4J2CyvDQgRsVoviO26BZNY/Z6sXkaV1Jk+MMMwDCO2qEtPYIZhGEYMYQJmGIZhRCUxL2AicpGIfC0iWSIy2Wt7aouIbBWRTBFZKyKr3LQWIvKuiGx2X5O9trM6RGSOiOwVkXV+aRXWQxwedX/DL0Wkr3eWRw6x5Nux4tdgvh1OYlrARCQeeAK4GOgBXCUiPby1KigMUdU+fmNJJgPvq2o34H33c6TzLHBRubTK6nEx0M3dJgBPhcnGiCVGfTsW/BrMt8NGTAsY0A9n6exvVbUAWAQM99imUDAcmOe+nwdE/MRoqroMyC6XXFk9hgP/585DuhJoLiJtw2NpxFIXfDvq/BrMt8NJrAtYe2C73+cdRP8yAgq8IyKrRWSCm9ZGVXcDuK+tPbOudlRWj1j8HWtLrH0nsezXYL4dEmJ9Ml+pIC3axw2cpaq7RKQ18K6IbPTaoDAQi79jbYm176Qu+jXE3u8YVmL9CWwH0NHvcwdgl0e2BAVV3eW+7gVexWlK+t7X7OC+7vXOwlpRWT1i7ncMAjH1ncS4X4P5dkiIdQH7DOgmIl1FpD4wGnjdY5uOGxFpJCJNfO+BC4F1OHW61s12LfCaNxbWmsrq8TpwjRuxNQDI9TXH1GFixrfrgF+D+XZIiOkmRFUtEpGbgf8A8cAcVV3vsVm1oQ3wqoiA89stVNUlIvIZ8C8RGQd8B4zy0MaAEJHngcHACSKyA7gLuJ+K6/E2cAmQBeQD14Xd4Agjxnw7ZvwazLfDiU0lZRiGYUQlsd6EaBiGYcQoJmCGYRhGVGICZhiGYUQlJmCGYRhGVGICZhiGYUQlJmCGYRhGVGICZhiGYUQlJmB1ABE5w11rKNGd9WC9iKR6bZdh1Abza8MGMtcRRGQakAg0BHao6nSPTTKMWmN+XbcxAasjuPPlfQYcBn6uqsUem2QYtcb8um5jTYh1hxZAY6AJzj9Ww4gFzK/rMPYEVkcQkddxVu3tCrRV1Zs9Nskwao35dd0mpmejNxxE5BqgSFUXikg8sEJEzlXV/3ptm2EcL+bXhj2BGYZhGFGJ9YEZhmEYUYkJmGEYhhGVmIAZhmEYUYkJmGEYhhGVmIAZhmEYUYkJmGEYhhGVmIAZhmEYUcn/B4h2r/JZUsEWAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 432x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system(2, dx=(-10,130), dy=(-10,110)) # make 2 subplots\n",
"\n",
"# draw the original sector\n",
"sector.draw(ax[0],10)\n",
"ax[0].set_title('Outer Sector')\n",
"ax[0].annotate('apogee',sector.apogee,\n",
" xytext = (20,10),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"\n",
"# draw the subdivision result\n",
"subsectors[0].draw(ax[1],10)\n",
"subsectors[1].draw(ax[1],10)\n",
"\n",
"if not subsectors[0].apogee is None:\n",
" ax[1].annotate('new apogee',subsectors[0].apogee,\n",
" xytext = (30,-10),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"if not subsectors[1].apogee is None:\n",
" ax[1].annotate('new apogee',subsectors[1].apogee,\n",
" xytext = (30,0),\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"# draw the original section line\n",
"sector.section_line.draw(ax[1],10,color='lightgray')\n",
"ax[1].set_title('Subdivided Outer Sector')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We see that the subdivision created 2 new edges which _enclose_ more points of the point cloud.\n",
"\n",
"We also note that if we create an intial set of `OuterSector`\n",
"instances so that:\n",
"* the section lines represent a convex, counter-clockwise polygon\n",
"* the end-points of all section lines are points of the convex\n",
"\n",
"then we can then keep subdividing the sectors until all vertices of the convex hull are found.\n",
"Since there is only a finite number of vertices in the convex hull, therefore\n",
"the subdivision process will eventually end.\n",
"\n",
"Once subdivision has ended the underlying polygon is the convex hull."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Finding an Intial Convex Polygon\n",
"\n",
"For the iterative sector subdivision to yield the convex hull we must seed it with a convex polygon\n",
"whose end vertices are already vertices of the convex hull. Fortunately, we can compute a good initial\n",
"section line by searching for two distinct points which the smallest/highest x or y coordinates."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"def compute_section_line(point_cloud : Iterable[np.array]) -> PolygonEdge:\n",
" '''Find a good section line for a point clpud.'''\n",
" point_itr = iter(point_cloud)\n",
" x_min_pt = x_max_pt = y_min_pt = y_max_pt = next(point_itr)\n",
" x_min = x_max = x_min_pt[0]\n",
" y_min = y_max = y_min_pt[1]\n",
"\n",
" for p in point_itr:\n",
" x,y = p\n",
" if x < x_min:\n",
" x_min_pt = p\n",
" x_min = x\n",
" elif x > x_max:\n",
" x_max_pt = p\n",
" x_max = x\n",
"\n",
" if y < y_min:\n",
" y_min_pt = p\n",
" y_min = y\n",
" elif y > y_max:\n",
" y_max_pt = p\n",
" y_max = y\n",
"\n",
" if (x_max - x_min) > (y_max - y_min):\n",
" return PolygonEdge(x_min_pt,x_max_pt)\n",
" else:\n",
" return PolygonEdge(y_min_pt,y_max_pt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's visualize this with the sample point cloud we have been using so far:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"good_section_line = compute_section_line(point_cloud)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"this looks like so:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.collections.PathCollection at 0x1a2b337bc50>"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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ACxfvryA6f5glzFPlxezZs2aua5L9aScqylUYLGkmInIFhFpJiKRItJLRH4TkZMi0k5E6jn/9fsJvApzxupac8OHhjrOlXTq1KmQKT0z5ik1NZWuXbsSFRVF48aNXcU2JSWFNm3a0LRpUzp27MjRo0cB2Lt3L+3btycqKorbb7+dffv2MWbMGJKTk2nSpAmTJk0iKSnJNcPuqVOn6NWrF5GRkbRs2dJ1FcQLL7zA4MGDiYmJoXbt2tdV/HI6oXP6cHr37k3Dhg3p378/OSfErpZb2ZdeAeBFhblZxfXMDZ/TKvvtt98KldMTY56++OILqlevztatW9m+fTudOnUiIyODv/71ryQmJpKSksLgwYMZN24cAP379+fxxx9n69atrFu3jmrVqjF+/Hiio6PZsmULTz755CXbf/7557ntttv4/vvveeWVV3j11Vddz/34448sX76cb7/9lhdffJGMjIzrzr1582beeustduzYwf79+1m7dm2euZV96bWZXpRTeAp6xupaHcQdOnQA4P777+e///1vgXNWDw3hsJvClZ8xT7feeitPPfUUzzzzDN26dSM6Oprt27ezfft27rnnHgCysrKoVq0aZ8+e5fDhw/z5z38GoFSpUtfc/po1a/jkk08Ax4SVv//+O2fOnAGga9eulCxZkpIlS3LjjTdy7NgxwsLCrit3ixYtXK9t0qQJBw4cIDQ01G1uZW9azLzM22esBg8ezAcffFCobYzu2OCSs66Q/9ud1a9fn5SUFJYuXcrYsWPp0KEDf/7zn2nUqBHr16+/5LW///57vjO6Gw9pjAG4ZHhKcHAwmZmZ171dd+uKiNvcyt70MNNP5Zx9XFHBcbOTES9OKvC2PHG7syNHjlC6dGkeeOABnnrqKb777jsaNGjA8ePHXUUhIyODH374gXLlyhEWFsann34KQHp6OmlpaZQtW5azZ8+63X7r1q2ZPXs24OjrKl++vOuGL552tdzK3rRl5ocuGb9WrDimWAmmvDCKtj3uL3Ar8HpbkBkZGaxatYqEhASSk5NZt24dlStXZtu2bYwePZqgoCCKFy/O1KlTKVGiBImJiYwYMYIzZ86QmZnJE088QaNGjZg1axbDhg3jueeeo3jx4syfP5/IyEiKFStGVFQUgwYN4rbbbnO97wsvvMBDDz1EZGQkpUuXZswY7126m1duZWMi4rc/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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system()\n",
"\n",
"textoffset = np.array([good_section_line.direction[1],-good_section_line.direction[0]])*30\n",
"ax.annotate('section line', (good_section_line.start + good_section_line.end)/2,\n",
" xytext = textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.annotate('convex hull vertex',good_section_line.start,\n",
" xytext = -textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.annotate('convex hull vertex',good_section_line.end,\n",
" xytext = textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"good_section_line.draw(ax,10)\n",
"ax.scatter([p[0] for p in point_cloud],[p[1] for p in point_cloud])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Assembling the QuickHull Algorithm\n",
"\n",
"Finally we have all the pieces to (slowly) walk through the [QuickHull](https://en.wikipedia.org/wiki/Quickhull) subdivision algorithm.\n",
"\n",
"Using the sample point cloud from above and the `good_section_line` we have already calculated we\n",
"define an initial convex polygon by adding a _reverse_ edge to create a _closed_ edge loop. This\n",
"produces a degenerate but convex polygon."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"seed_polygon = Polygon([good_section_line.start, good_section_line.end, good_section_line.start])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This seed polygon looks like so:"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system()\n",
"\n",
"ax.scatter([p[0] for p in point_cloud],[p[1] for p in point_cloud])\n",
"textoffset = np.array([section_line.direction[1],-section_line.direction[0]])*30\n",
"seed_polygon.draw(ax,10)\n",
"ax.annotate('Seed Polygon', (seed_polygon.loop_start.start + seed_polygon.loop_start.end)/2,\n",
" xytext = textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.annotate('convex hull vertex',seed_polygon.loop_start.start,\n",
" xytext = -textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.annotate('convex hull vertex',seed_polygon.loop_start.end,\n",
" xytext = textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"ax.set_title(\"The Degenerate Seed Polygon\")\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To prepare for iterative subdivision of the convex seed polygon we set up a stack of\n",
"`OuterSector` instances for keeping track of the sectors we still need to work on."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"subdivision_stack = cl.deque()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we create a sector to the first edge of the seed polygon and push it onto the stack. "
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"sector1 = OuterSector(seed_polygon.loop_start)\n",
"remaining_points = sector1.add_points(point_cloud)\n",
"subdivision_stack.append(sector1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With the points which were _rejected_ by the first sector's `add_points` and remembered in `remaining_points`\n",
"we construct the second sector and push it onto the stack. `remaining_points` contains the point which\n",
"are left (inside) the first sector. This set of points is ideally significantly smaller than the original\n",
"point cloud."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"sector2 = OuterSector(seed_polygon.loop_start.next)\n",
"sector2.add_points(remaining_points)\n",
"subdivision_stack.append(sector2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the subdivision process can start. Ideally we do this until the stack is empty.\n",
"However, for illustration purposes we do this only $m$ times."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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40lt1xuA3e/cTw2KuOf8thGOTFG84/JWkhyNcuH+7Rz55g31VrE8LndxDj+r1WMYrW57hX+8/Qm33347GRkZpKen+zxGx44dyc/P5+uvv2bSpEkMHz6cJk2aICIVXt26dePWW29l6tSprF27lpKSknC8TSUKuef8tiQnOFKmlJO6U3fALbT567uMe/o/DB48mOTkZGrWrOnzGI0aNWLnzp3Mnj2bsWPH0q9fP9LT0yt4m56ezrnnnsv999/PRx99xJ49e8LxFmOSmB4O4Ro/+ddVp7BpzQKKj+znxLXTGdZ1aIQjCw1aUSA6cP3Oxm1dxj6AkiKO/TSX1L03E61zzKi70YHrd3bJU4kUFxfSxP13eek5PP3008yfP58pU6bw2WefkZCQUKanrFmzZqxevbrCcY0xbNmyhUWLFrF48WIWL17MokWLWLFiBStWrOCFF17wGlOrVq3o2bMnQ4cOZenSpWRlZZGUlBSU96veRg/DujbhyKGDXP0YJCSnlf193n4Vhw4d4t133+X5559n3bp1lJSUUFBQULr//fffz4QJEyoct7CwkFWrVpXxdt26dXz11Vd89dVXPmM67bTTuP3225k2bRo9e/bkpJNOQsTTtAuVJ1bcjekkGBxip99xERd9/hJ5eXl8//kcZs6cySWXXBLp0IKKVhSILoZ1bULKyB5c8Ibj54L841x22WWsXbuWhg0bRja4IKPuRhfDujYhIy2F/fm5fDe2f5l18fHxDBgwgAEDBnDkyBFmzpzJ888/z5o1aygqKqJx48YejykitGjRghYtWjBixAiv5963bx9LliwpTTYWL15cWoWia9euXHbZZR73y8zMpGfPnvTs2ZPTTz+dHj16+O2xVm+jjw7p+QD8PvHCCutq167NjTfeyI033simTZt4/fXXeemllzh27Bj5+fk0a9bM4zETExPp1q0b3bp1Y/To0R63McawcePGUmcXLVrEsmXLWLJkCXv27OFvf/ub15jbtWtX6m3Pnj3p3Lmz36FHseRuzCfBAK1bty69esrLy+Paa6/l1FNPpUWLFgHtb4crJveKAi60ooC9cfVWJSUlUVBQwLFjxxg+fDjffvstcXH+RzrZwVtQd6ORQHpa09PTufbaa7n22mvZsmULU6dOpWvXrkDV3a1fvz6DBg1i0KBBFdYtXLiwtCLF8uXLy/TMbd++nTlz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\n",
"text/plain": [
"<Figure size 864x144 with 4 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"m=3\n",
"fig,ax = make_coordinate_system(m+1)\n",
"fig.set_size_inches(12, 2, forward=True)\n",
"\n",
"ax[0].scatter([p[0] for p in point_cloud],[p[1] for p in point_cloud])\n",
"ax[0].set_title('Seed Polygon')\n",
"seed_polygon.draw(ax[0],15)\n",
"\n",
"subdivision_count = 1\n",
"while len(subdivision_stack)> 0 and subdivision_count < (m+1):\n",
" sector = subdivision_stack.pop()\n",
" if sector.apogee is None:\n",
" seed_polygon.loop_start = sector.section_line\n",
" else:\n",
" sector1,sector2 = sector.subdivide()\n",
" seed_polygon.loop_start = sector1.section_line\n",
"\n",
" subdivision_stack.appendleft(sector1)\n",
" subdivision_stack.appendleft(sector2)\n",
"\n",
" ax[subdivision_count].scatter([p[0] for p in point_cloud],[p[1] for p in point_cloud])\n",
" ax[subdivision_count].set_title( f'Subdivision: %d' % subdivision_count)\n",
"\n",
" seed_polygon.draw(ax[subdivision_count],15)\n",
" subdivision_count += 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## ConvexHull Class\n",
"\n",
"Finally we can wrap the bits and pieces of the [QuickHull](https://en.wikipedia.org/wiki/Quickhull)\n",
"algorithm into the `ConvexHull` class.\n",
"\n",
"This class now just needs to _orchestrate_ the steps developed earlier."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
"class ConvexHull (Polygon):\n",
"\n",
" def __init__(self):\n",
" '''Construct an empty polygon'''\n",
" self.loop_start = None\n",
"\n",
" def add_points(self, points : Iterable[np.ndarray]) -> None:\n",
" '''Expand the concex hull based on the given points.'''\n",
" # if we already have a convex hull add its\n",
" # vertices to the given point cloud\n",
" if self.loop_start:\n",
" point_cloud = cl.deque(self.vertices)\n",
" point_cloud.extend(points)\n",
" else:\n",
" point_cloud = points\n",
"\n",
" # Create a processing stack for sectors to subdivide.\n",
" subdivision_stack = cl.deque()\n",
"\n",
" # Find a good initial section line.\n",
" section_line = compute_section_line(point_cloud)\n",
" self.loop_start = section_line\n",
"\n",
" # create a closed, degenerate convex polygon\n",
" reverse_section_line = PolygonEdge(section_line.end,section_line.start,\n",
" next = section_line, previous = section_line)\n",
"\n",
" # Enroll sectors of the seed polygon for processing\n",
" for edge in self.edges:\n",
" sector = OuterSector(edge)\n",
" point_cloud = sector.add_points(point_cloud)\n",
" subdivision_stack.append(sector)\n",
"\n",
" # run the subdivision process until we have a convex hull\n",
" while len(subdivision_stack) > 0:\n",
" sector = subdivision_stack.pop()\n",
" if sector.apogee is None:\n",
" self.loop_start = sector.section_line\n",
" else:\n",
" sector1,sector2 = sector.subdivide()\n",
" subdivision_stack.appendleft(sector1)\n",
" subdivision_stack.appendleft(sector2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For experimentation we set up two disjoint point clouds with $n$ points each."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [],
"source": [
"n=99\n",
"point_cloud1 = [np.random.random(2)*50 for _ in range(n)]\n",
"point_cloud2 = [np.random.random(2)*50 + 50 for _ in range(n)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we add both point clouds to an empty convex hull. We also reset the counter for the `distance`\n",
"method of the `PolygonEdge`. Knowing the number of point-to-edge distance calculations should provide some\n",
"rough metric of the performance of this algorithm."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"hull = ConvexHull() # empty hull\n",
"\n",
"PolygonEdge.distance.callcount=0 # reset performace counter\n",
"hull.add_points(point_cloud1)\n",
"hull.add_points(point_cloud2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's visualize the result and print out the performance metric. Note that the two point clouds\n",
"are drawn with different colors."
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"198 points required 816 distance calculations\n"
]
},
{
"data": {
"image/png": "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ZUuef/55Bg4cyP3330/Lli0rxLd69Wqeeuopli9fztq1a/nuu+/CcFZEIpsSMhGRCDNr1iz69u1L06ZNAWjcuDEAc+fO5eqrrwbgmmuu4dtvvy15zmWXXUZcXBwnnHACO3bsAKBPnz7MmjWL3NxcPv/8c3r27ElycjIzZsxgypQpdO3aldNOO409e/awatUqAJ599lkefvhh6taty1VXXeUzvuOPP562bdsSFxdH165dWb9+fahOhUjUiOg+ZCIischaizGmyseVfkzdunXLPB8gKSmJXr168eWXXzJ16tSSBMtay7PPPssFF1xQYZ9btmwhLi6OHTt2UFRURFxcxf/XJyQklPweHx9PQUEBAHXq1KGoqAiAnJycQA5VJGZohExEJML07t2badOmsWfPHgD27t0LwBlnnMHbb78NwBtvvMEf/vCHKvfVv39//vOf//C///2vJAG74IILeOGFF8jPzwdg5cqVZGVlUVBQwA033MCbb75Jp06dePLJJ6sVd1paGgsWLADg3XffrdZzRaKdEjIRkQjTuXNn7rvvPs466yxOOukkBg8eDMAzzzzDf/7zH9LT03nttddKassqc/755/PNN99w7rnnkpiYCMBNN93ECSecwCmnnEKXLl3429/+RkFBAePGjePMM8/kzDPP5Mknn2Ty5Mn8+uuvAcc9evRo7rzzTs4880zi4+NrdvAiUcoUD11Hom7dulktnRS9dP5qTueudnT+akfnr3Z0/mrO7efOGLPAWtvN130aIRMRERFxmBIyEREREYfpKksRkQjw6KOP8tlnn5GYmEjdunVJSkrikUce4eijj3Y6NBEJAiVkIiIRYNasWVx44YWcfPLJ5OXlMXLkSFauXKmETCRKaMpSRCQC5OXlceqpp3LBBRdw8cUX07hx4zK9xUQksikhExGJAHl5eWUSsLy8vJI2FSIS+ZSQiYhEgPIJWG5urhIykSiihExEJAKUT8DKj5iJSGRTQiYiEgHKj5BpylIkuighExGJAOVHxDRlKRJdlJCJiEQATVmKRDclZCIiEUBTliLRTQmZiEgEUNsLkeimhExEJAKUn7JUDZlIdFFCJiISAXxNWaqGTCR6KCETEXG5wsJCjDHEx8eX3AZKbotI5NPi4iIiLnTqqady4MABEhMTSUhIIDk5ueS+/Px8rLUMGDCAxMREEhMTOfroo7n7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\n",
"text/plain": [
"<Figure size 720x720 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system()\n",
"fig.set_size_inches(10, 10, forward=True)\n",
"\n",
"ax.scatter([p[0] for p in point_cloud1],[p[1] for p in point_cloud1])\n",
"ax.scatter([p[0] for p in point_cloud2],[p[1] for p in point_cloud2])\n",
"ax.set_title('Convex Hull of Two Point Clouds')\n",
"hull.draw(ax,4)\n",
"\n",
"textoffset = np.array([hull.loop_start.direction[1],-hull.loop_start.direction[0]])*30\n",
"ax.annotate('convex hull', (hull.loop_start.start + hull.loop_start.end)/2,\n",
" xytext = textoffset,\n",
" arrowprops=dict( fc = 'None', ec='black', shrink=1.5),\n",
" textcoords ='offset points')\n",
"\n",
"print (len(point_cloud1) + len(point_cloud2),\n",
" 'points required', PolygonEdge.distance.callcount, 'distance calculations')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Adding Points to a Convex Hull One by One\n",
"\n",
"The implementation of the `ConvexHull` class already allows for adding more than one point cloud\n",
"to an existing convex hull. However, if points need to be added to an existing point cloud\n",
"**individually**, the [QuickHull](https://en.wikipedia.org/wiki/Quickhull) subdivision approach\n",
"is a big hammer for a small job. For single points we choose marching algorithm to add\n",
"points one by one which has $O(m)$ complexity with $m$ being the number of edges in the convex hull).\n",
"\n",
"To illustrate how the marching algoritm works we use the small cloud of randon point\n",
"we create earlier and create a convex hull for it."
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [],
"source": [
"hull = ConvexHull()\n",
"hull.add_points(point_cloud)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We also need a point that we can add to the convex hull. We choose a point which is outside\n",
"the convex hulls so that we get some action. Attempting to add a point which is **inside** the\n",
"convex hull does not change the hull."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
"p = np.array([110,50]) # make sure it is outside the convex hull"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we start _marching_ around the polygon to determine the sequence of edges where the point is\n",
"classified as _outside_ (distance is positive)."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [],
"source": [
"polyline_start = polyline_end = None\n",
"\n",
"for i,edge in enumerate(hull.edges):\n",
" if edge.distance(p) > 0:\n",
" if polyline_end:\n",
" polyline_end = edge\n",
" else: # first edge whre point is outside\n",
" polyline_start = polyline_end = edge\n",
" if i == 0:\n",
" # expand polyline backwards\n",
" probe = edge.previous\n",
" while not probe is edge:\n",
" if probe.distance(p) > 0:\n",
" polyline_start = probe\n",
" probe = probe.previous\n",
" else:\n",
" break\n",
" elif polyline_end: # point is outside the convex hull\n",
" # we can stop here because we have a contiguous\n",
" # set of edges (polyline) starting at\n",
" # 'polyline_start' and ending at 'polyline_end'\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The sequence of edges we found (if any) indicate the place where the convex hull needs\n",
"to be extended to include the given point."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"if polyline_end:\n",
" edge1 = PolygonEdge(polyline_start.start,p,\n",
" previous = polyline_start.previous)\n",
" PolygonEdge(p,polyline_end.end,\n",
" next = polyline_end.next, previous = edge1)\n",
" hull.loop_start = edge1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's draw this the 2 stages of adding the point."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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J598kjFjxpCSksLbb7/NH/7wB/r160dKSkpN0k5NTeX000/nySefZNOmTc5duEgceKWgVs72rgZz9lkDuOXX/4+XX36ZDRs2cOutt7L//vvXmQEqPT2dqqqqmpy9fft2ZsyYwbhx4+jYsSOff/45d999N8OHDyctLa1Osf3zn/+ce++9l+XLl6uBJIHU5SMGNm7cCLDPlkG3KSpuuGvAvraLu+QNytnnrd5+/frx1FNP8Ze//IVp06ZRVlZGamoqhx9+eJ39DjzwQK688kquvPLKOtuttRQUFDB37lxee+013nrrLV599VVeffXVeudq06YNo0ePZvTo0Zx88sl07Nix0bh1y1rcxisFtXK2tzWWsw844AD+9Kc/cfPNN/P6669TWFhIeno63bp1q9MtLzs7m3POOYdzzjmn3mts2bKFefPm8dprrzF37lw++OADPvjggwZnp/rlL39Zk7cPOeSQRuNWzg6PNypAl/PafKZdszNr+nTtvV2SQ6tWrbjssstYtGgRH3/8Mb179w7pOGMMhx56KIceeii///3v6zxXXV1Nfn4+c+fOZe7cuXzyySdMnz6d6dOn13udLl26cOqppzJ69GhOOukkFn61I6Spo0QSyStTkilnJ7/U1FRGjx7NokWLWLt2bVgty507d+bSSy/l0ksvrffcunXragrt119/nTfeeIM33niD8ePH19mvRYsWNTn71FNP5eMt1crZYVJBHQNeK6gnjOpb5xcFIDM9lQmjtGRrMhoyZEhMXiclJYUhQ4YwZMgQbr/99jrPVVRUsHjx4prEvWrVKp5++ul6iyi07H4E+597BykpqTW3rJWcxSleaaFWzm5eevToEbPX6tmzJ9deey3XXnttne3WWlauXFnTQPL+++/z0ksv8dJLL9XZz2Rk0XncX8jYr5tydhPUhzoGvFZQN9Sn694zc/VLIhFLT0/n+OOP57777mPlypV1+mvv2rWLA86eRGavoyjb8DnVvp01x+mWtTihpKQE8C/O4QXK2RJrxhhyc3O5+eabee+99+rk7MrKSjpfNJV2w84jJbMt3714F7aqAlDOboxaqGPAawU1NN6nSySWsrKy6DXkOJZ87F95zJbthFbZgG5ZizO++eYbgHorkbqZcrYkSmpqKj0PG0xhTj+sMexYPJ1tC/9Jh19epZzdCLVQx4AXC2qRRLqgbxrl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\n",
"text/plain": [
"<Figure size 864x360 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system(2, dx=(-5,120), dy=(-5,110))\n",
"fig.set_size_inches(12, 5, forward=True)\n",
"\n",
"# draw original convex hull\n",
"hull_orig = ConvexHull()\n",
"hull_orig.add_points(point_cloud)\n",
"hull_orig.draw(ax[0],5)\n",
"\n",
"ax[0].scatter([p[0] for p in point_cloud],[p[1] for p in point_cloud])\n",
"ax[0].scatter([p[0]],[p[1]])\n",
"ax[0].annotate('$\\\\vec{p}$', xy=p, xytext=(15,1), ha='right', textcoords='offset points')\n",
"ax[0].set_title('Original Convex Hull')\n",
"\n",
"# draw extend hull\n",
"ax[1].scatter([p[0] for p in point_cloud],[p[1] for p in point_cloud])\n",
"ax[1].scatter([p[0]],[p[1]])\n",
"hull.draw(ax[1],5)\n",
"ax[1].annotate('$\\\\vec{p}$', xy=p, xytext=(15,1), ha='right', textcoords='offset points')\n",
"ax[1].set_title('Extended Convex Hull')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## class ConvexHullEx - Extended Convex Hull\n",
"\n",
"Having the marching algorithm is layed out as well, we can wrap everthing up\n",
"into a multi-purpose convex hull class."
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
"class ConvexHullEx (ConvexHull):\n",
" def __init__(self):\n",
" super().__init__()\n",
"\n",
" def add_point(self, point : np.array) -> None:\n",
" if self.loop_start:\n",
" if self.loop_start.next is self.loop_start:\n",
" # got a nucleaus\n",
" self.loop_start = PolygonEdge(self.loop_start.start,point)\n",
" else: # got a convex hull\n",
" # march arround the existing convex hull until we find an\n",
" # edge to which the point has a positive distance\n",
" # (meaning the point is outside the convex hull).\n",
" # If we find such an edge, we expand the convex hull to\n",
" # include the point.\n",
" #\n",
" # If the point-to-edge distance of the point is negative for\n",
" # all edges of the convex hull, the point is inside the\n",
" # convex hull and can be discarded.\n",
"\n",
" polyline_start = polyline_end = None\n",
"\n",
" for i,edge in enumerate(self.edges):\n",
" if edge.distance(point) > 0:\n",
" if polyline_end:\n",
" polyline_end = edge # just extend the polyline\n",
" else: # first edge where point is outside\n",
" polyline_start = polyline_end = edge\n",
" if i == 0:\n",
" # expand polyline backwards\n",
" probe = edge.previous\n",
" while not probe is edge:\n",
" if probe.distance(point) > 0:\n",
" polyline_start = probe\n",
" probe = probe.previous\n",
" else:\n",
" break\n",
" elif polyline_end: # point is outside the convex hull\n",
" # we can stop here because we have a contiguous\n",
" # set of edges (polyline) starting at\n",
" # 'polyline_start' and ending at 'polyline_end'\n",
" break\n",
" # extend the convex hull\n",
" if polyline_end:\n",
" edge1 = PolygonEdge(polyline_start.start,point,\n",
" previous = polyline_start.previous)\n",
" PolygonEdge(point, polyline_end.end,\n",
" next = polyline_end.next, previous = edge1)\n",
" self.loop_start = edge1\n",
" else:\n",
" # add a nucleus\n",
" self.loop_start=PolygonEdge(point,point)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Performance Comparison `add_points` vs `add_point`\n",
"\n",
"To assess the performance characteristics of adding points in bulk (`add_points`) versus adding points one-by-one, (`add_point`) we set up two sets of points:\n",
"* a point cloud from which we take slices of varying sizes\n",
"* a set of points from which we take slices of varying sizes which we are going to add in bulk and\n",
" one-by-one.\n",
" \n",
"The make the test scenario more realistic we make it so that both point clouds overlap, so that\n",
"some of the points to add will be inside the convex hull of the point cloud."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"point_cloud=[np.random.random(2)*75 for _ in range(5000)]\n",
"extra_points=[np.random.random(2)*50+50 for _ in range(100)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The figure below demonstrate the layout of the overlapping point sets."
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Text(0.5, 1.0, 'Sample Point Sets')"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 864x360 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"fig,ax = make_coordinate_system(1)\n",
"fig.set_size_inches(12, 5, forward=True)\n",
"ax.scatter([p[0] for p in point_cloud[0:50]], [p[1] for p in point_cloud[0:50]],label='Point Cloud')\n",
"ax.scatter([p[0] for p in extra_points[0:50]], [p[1] for p in extra_points[0:50]],label='Points To Add')\n",
"ax.legend()\n",
"ax.set_title('Sample Point Sets')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the intactive graph below the cost (in terms of number of distance calculations) adding points to\n",
"an existing convex hull in bulk (`add_points`) versus one-by-one (`add_point`) can be explored."
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "35d964494d8e440ca04c6dee66080843",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"interactive(children=(IntSlider(value=500, continuous_update=False, description='Point Cloud Size', layout=Lay…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<function __main__.draw_costs(cloudsize, addsize)>"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from ipywidgets import interact\n",
"from ipywidgets import interact_manual\n",
"import ipywidgets as ipw\n",
"\n",
"def quickhull_costs(cloud,extra_points):\n",
" for n in range(1,len(extra_points)):\n",
" hull = ConvexHullEx()\n",
" hull.add_points(cloud)\n",
"\n",
" PolygonEdge.distance.callcount=0\n",
" hull.add_points(extra_points[0:n])\n",
" yield PolygonEdge.distance.callcount\n",
"\n",
"def marching_costs(cloud,extra_points):\n",
" hull = ConvexHullEx()\n",
" hull.add_points(cloud)\n",
" PolygonEdge.distance.callcount=0\n",
"\n",
" for p in extra_points:\n",
" hull.add_point(p)\n",
" yield PolygonEdge.distance.callcount\n",
"\n",
"def draw_costs(cloudsize,addsize):\n",
" fig,ax = plt.subplots(1,1)\n",
" fig.set_size_inches(11,5)\n",
" ax.plot(list(marching_costs(point_cloud[0:cloudsize],extra_points[0:addsize])), label='Marching')\n",
" ax.plot(list(quickhull_costs(point_cloud[0:cloudsize],extra_points[0:addsize])), label='Quickhull')\n",
" ax.legend()\n",
" ax.set_xlabel('# Points Added')\n",
" ax.set_ylabel('# Distance Calulations')\n",
" ax.grid(True)\n",
" ax.xaxis.set_major_locator(plt.MultipleLocator(5))\n",
" ax.set_title('Cloud Size: %d' % cloudsize)\n",
"interact(draw_costs,\n",
" cloudsize=ipw.IntSlider(min = 1,\n",
" max = len(point_cloud),\n",
" step = 5,\n",
" value = 500,\n",
" continuous_update=False,\n",
" description = 'Point Cloud Size',\n",
" layout = ipw.Layout(width='80%'),\n",
" style = {'description_width' :'initial'}\n",
" ),\n",
" addsize=ipw.IntSlider(min = 1,\n",
" max = len(extra_points),\n",
" step = 1,\n",
" value = 20,\n",
" continuous_update=False,\n",
" description = 'Points to Add',\n",
" layout = ipw.Layout(width='80%'),\n",
" style = {'description_width' :'initial'}\n",
" )\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Summary\n",
"\n",
"Experimentation with the sample data shows that the break-even point between adding points in bulk or\n",
"one-by-one in terms of number of distance calculations is typically somewhere between 5 and 20 points.\n",
"The exact position depends on the distribution of the points in space. For small point sets (< 5) it is most likely\n",
"advantageous to add point one-by-one (`add_point`) to an existing convex hull.\n",
"Larger point sets should be added in bulk (`add_points`). "
]
}
],
"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.1"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": false,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": false,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment