{ "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": [ "
" ] }, "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": "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\n", "text/plain": [ "
" ] }, "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": [ "
" ] }, "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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H3/8cbnXzps3j/vvvx+AmTNn8sILL9RI14ABAwIev/HGG5k7d26F995111188MEHNcrXMKKNqG4zCzdPPfUUb7/9NqeccgrTpk2jefPm5ZrNn//8Z9544w0Abr311hrnWZFhVsa4ceMYO3YsQ4YMqXEahhEtWMksAI888ghnnXUWPXv2ZOrUqYBjSF9//TWpqak8/vjjzJw5k8cff5ykpCSWLFlS4v6NGzfSuHFj2rRpA8C0adN49NFHARg0aBCTJk2ib9++nH766cX3fvnll/Tt25ekpCRuvvlmNm3aBBwrMaoqt99+O127duXSSy9l+/btxfllZ2dz/vnnk5KSwrBhw8jLc6LRJCQksGvXLr7/vjgghGHELFYyK8XChQvZtGkTy5cvR1VJTU3lww8/ZObMmbzzzjssXryYNm3aUFhYSPPmzbnrrrvKpLF06VKSk5PLzaOoqIjly5ezYMEC7rvvPt5//31mzpzJHXfcQVpaGu+99x4dO5YMwPDaa6+xYcMG1qxZww8//EDXrl256aabOHz4MOPGjeP111/nhBNOYM6cOUyZMoVnn30WgOTkZJYuXcqoUaOC+6AMw2OYmZVi4cKFLFy4kN69ewOwd+9eNm3axMCBA6ucRl5eHiecULbn2McVV1wBQEpKCps3bwagf//+TJ8+na1bt9KhQweaNm1a4p4PP/yQa6+9lvr163PSSScVVx03bNjA2rVrGTp0KABHjhyhfftjobXatm3Ld999V2XthhGtmJmVQlW5++67+dWvflXjNJo2bUphYWG55xs3dgJE1K9fn6KiIgCuu+46zj77bN566y1+97vflTAsHyISUG+3bt345JNPAuZ18ODBMsZoGLGItZmVYtiwYTz77LPs3bsXgG3btpVon/LRokUL9uzZEzCNM888k5ycnGrl+/XXX3Pqqacyfvx4BgwYwOrVq0ucHzhwIJmZmRw5coS8vDwWL3biIp5xxhns2LGj2MwOHz7Ml19+WXzfxo0b6d69O4YR65iZleKiiy7iuuuuo3///vTo0YMrr7wyoGkNHz6c1157LWAHwMCBA/n888/RaoRXmjNnDt27dycpKYlvv/2WG264ocT5kSNH0rlzZ3r06MFtt93G+eefD0CjRo2YO3cukyZNolevXiQlJRX3gB4+fJicnBz69OlT3cdgGNFHoJG00bJ5eQbA+PHj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\n", "text/plain": [ "
" ] }, "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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\n", "text/plain": [ "
" ] }, "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": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "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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ad68eZrNDenp2rUrX331lfMfAMDMmTNZsmQJISEhBAUF8d13qV+rffDBB1mwYIHzguD777/P5s2bCQ4OplatWkyZMgVjDL169eLtt9+mbNmyTJ8+nd69e6eo5etgS0rhvX7O7nh5up+zp6QVz8iRIw1g6tat69mAjGfP0YKtx02NV38wFYcudr5qvPpDin7lueV35i12i8cY7eec1ZfWnDNw4sQJXn/9da8/pfq7775j9OjRBAQEsG3bNq/GktNcnzAi6BNGVN6kFwTTER0dTdu2bUlKSiIiIoIFCxZwyy23eDyOffv20amTY6gR1zbdm1mneoGajFWepjXnNMTFxdGhQwdatmzJ77//TrVq1QgNDXV2kXM3Ywxz587lwoULKebHxMRQo0YNAOfFQ6XUzU+Tcxr69etH8eLFmTBhAr6+vnz00Ud07NiRpk2bcujQIbfvb8mSJTz++OPUr1/fmYSTkpIoUqQI4LjBJXkoUKXUzU+Tcxpat27NL7/8wq+//gqAiDBy5EheeeUV7r33Xre2+xpjGDRoEAkJCRw9epTdu3ezfft25yD569evT/UuQaXUzUuTcxoef/xxPv30Uzp06JBibIznnnvOefNI8i3M2TV//nznzRiJiYkkJCQQEhJCUlISkydPpkmTJm7Zj1Iq99DknI7777+fRYsW8fTTTzNjxgzn/Iceeoi5c+fSrVs35syZc912J0+eZOvWrZnaR2JiIi+//HKaD2DNziOmlFK5lzZiZqBx48asXr2atm3bEhUVxSuvvIKI0KJFC1asWEH79u2JiorihRdeAODcuXO0atWK+Ph49u/fn+IW8NTMnj3bOYpcal544QX++OMP3njjjQzLUkrdPLTmnAk1a9Zk/fr1fPHFF7z88svOsStCQkJYu3YtkyZNYsSIEcTGxvLAAw/Qvn17ChQowJo1a9ItNyEhgSFDhqRZawZHr5F3332Xl156ya3HpJSyN03OmVSuXDnWrl3Lr7/+yhNPPMHVq1cBqFy5MuvXr2f58uVUr16datWqMW7cOHr16sX06dPTLXPGjBlER0enu05ybbl27druORCVoYXbImk6dhWVh31P07GrbPFABZX3aHLOgltvvZUVK1YQExPDgw8+SExMDAAlS5Zk9erVDBs2jGnTpuHj48MTTzzBtwu+4+6Ri1L9I4+Pj2f48OHp1poLFSrE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\n", "text/plain": [ "
" ] }, "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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\n", "text/plain": [ "
" ] }, "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 840 distance calculations\n" ] }, { "data": { "image/png": "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gJSUFIwxPPjggxQUFIThhYlElgKZiLjOc889x+WXX86FF17IypUry98Z7nVUblw47zYH90KD8iExrcFhD5GamkqbNm2CetoBAwaQl5eHtZaPP/6YevXq8eijj1KrVi2MMdx7770HWpiIRBkFMhFxlXvuuYfbb7+dm2++mQ8++ODQB4R7HZUbF867iUt6ofXr1489e/ZgrWXevHk0bdqUp556itq1a2OM4fbbb2ffvn0RrUmkJhTIRMQ1rrzySsaPH89jjz3Giy++WPGDwr2OqvfoQ3duOr1w3k3c1gsN6N27N9u2bcNay5dffslRRx3Fc889R3p6OsYY/u///o+9e/c6Vp9IIBTIRMQVzjzzTKZMmcLEiRMZNWpU1Q8O5zqqjoMPrINyy8J5N3H5lO6ZZ57Jxo0bsdayaNEijj32WF5++WUyMjIwxnDdddeRnV3D65KKhIECmYg4rmXLlixYsIBZs2Zx/fXXO12Odw2UmxbOu0kUTel269aNdevWYa1l6dKltG/fnkmTJtGgQQN/K46dO3c6XaYIoEAmIg6y1mKMISsriwULFhzSAkFcyI290AJwyimn8MMPP2Ct5fvvv6dz58689dZbNGnSBGMMl1xyCdu2bXO6TIljCmQi4oji4mJvXzFg1apV3mtJivu5sRdakE488USWLVuGtZbVq1fTvXt33n33XY444giMMfTv359NmzY5XabEmSSnCxCR+JOXl+fv1r5hwwZatmzpcEVyiJXTvQv192R5pyN7jz4QujoOjqoAVpUTTjiBhQsXAvDzzz9z/fXXM3v2bI46yjsFe9555/Haa69x9NFHO1mmxAGNkIlIRO3atcsfxn7//XeFMTdySWuLSDv22GPJzMzEWsuvv/5Knz59+M9//kPr1q0xxnDWWWfx888/O12mxCgFMhGJmKysLBo1agTAvn37aNiwocMVSYVc2Noi0o4++mjmzp2LtZasrCz69+/Pl19+yXHHHYcxhtNPP53//e9/TpcpMUSBTEQi4scff/SPhhUVFflHycSFXN7aItJatGjBrFmzsNaydetWBg0axKJFi/jDH/6AMYbOLdJYdXNdd12IXqKOApmIhN0333xDhw4dAPB4PCQlafmqq0VRa4tIa9asGe+88w7WWnZkvsYVJ9fiv5vzOemlXMydP9Dh3MtZNu0Jp8uUKKRAJiJhNXv2bHr06MGRRx7pb3MhLhelrS0irfGyZ3lzQAp2TD1231uX6zol8+OOEroMuQ9jDMceeyzffvut02VKlFAgE5GwmTRpEv3796dHjx5qIxBNYqC1RUSUmcKtX8vwz4vSsGPqsWdkPf7v//6PX375he7du2OMoWXLlnz11VcOFitup0AmImHx+OOPc91113HZZZexYMECp8uRYIXz8lSxopIp3HpNW/KPf/wDay25ubncfvvtZGVl0bNnT4wxNGnShPnz55f/opXTvWvQxtaH7T9qLVocUiATkZAbNmwYo0aNYsSIEbz11ltOlyMSHgFM7dapU4dnnnkGay379+/nnnvuYefOnZx77rkYY8jIyOCTf9xXvs1ISWFctBmR8hTIRCSkBgwYwIsvvsiECRMYP3680+WIhE+QU7tpaWk8+eSTWGvJz89n9OjR7N27l/NveQLzwDaO+H855BRY74PjrM2IqFO/iIRQp06dWLFiBVOmTOHyyy8P75NV1UleJFKqedWC1NRUHnroIR76SweK3rmB8d8UMu6rAkbNz+cvHX0PitM2I/FKI2QiUmPWWjIyMlixYgVz586NTBiLw07ycaPseqpY7u3l+zlOTjTc0yOFEgsLs0oO3K82I3FFgUxEasTj8ZCQkMDevXtZvHgxffr0Cf+TqpN87IqnsF3m53jJ5hIM8P12D9ZatRmJQwpkIlJthYWFJCYmArBmzRq6du1aswMGOjISjZ3k42XUp6biKWyX+Xmdu66YIg+kJkJeEWozEocUyESkWnJyckhNTQVg8+bNtG3btmYHDGZkJNo6yR/utSmsHRCNYbu6yvy8fvBTMcUeKCyBnKJEhbE4pEAmIkHbvn079erVAyA7O5vmzZvX/KDBjIxEWyf5ql5bLE3RlQbLLf+tfrCMtrBdE76f432Flh92eAAoKIG9xdpvF48UyEQkKOvXr6dZs2YA5Ofnk5GREZoDBzMyUt1O8k6NRFX12mJliq5csKT6wTLawnZN+H6OF/zekFplMlhuXoF3HZnEFcVwEQnYf//7Xzp37gxAcXGxf/1YSGQcdeDN/ODbKxJsu4HSwFAafkoDQ+mxwqmq1xYrU3RVBctgzm/pY+OlpUnHwXxc62tyi/4OePw3r127lnbt2jlXl0ScRshEJCCZmZl07tyZlJQUPB5PaMMYBD4yUt1RLidHoqp6bbEyRRfKYBlnl22aPXs2Ho+n3G263Fj8USATkcN69913Ofvss2nXrh0FBQUYY0L/JIFMQ9ZkvZWTI1FVvbZYmaJze7B06caJHTt2kJVV/mfQ4/EwZ84chyoSp2jKUkSq9OKLLzJs2DD69u0b/jeJw01D1mRarLJpQ6z3Dbo602LBXC2gstcWK1N0vUeXnxIG9wRLJ6erD+Ozzz4jNTWVwsLCcrd/9dVXDlUkTtEImYhU6oEHHmDYsGHccMMN7viLvSajXBWNRPm/vhoL0EO5OzIWpujKjQIS+EaLSKjudHVlo2ohHG378MMPycnJOeT23bt3s3Xr1mofV6KPApmIVOjaa6/lscceY8yYMbz22mtOl+NVk2mxgwPDwYJdTxYruyNDqTRYNu/krmBZnSBfWeCefWdI25R8+umnFd6emprK119/Xa1jxprMzEwuuOCCoL5m8uTJDBs2DICxY8fy//7f/wtHaSGlQCYihzj33HOZPHkyL7/8MmPHjnW6nANqut6qNDBQyRq4YNaTxcruyHhQnSBfWeBeOjlkQfyXX34hNze3wvv27t3L/Pnzq/zaUaNGMXXq1KCfV9xJgUxEyjn22GOZP38+M2fO5MYbb3S6nPKq23/sYKFYgO72RexyQIXT1QbaVnHd1cqCtS2p+PZqBPH//Oc/VW6QmTdvXrnPi4qKmDlzJn379qVbt27Mnj2bpUuXBv28lXnjjTfo2LEjJ598MldddRUAv/32G71796Zjx4707t2bDRs2AHDNNdcwfPhwzjjjDI455hjeffddAC699FI+/vhj/zGvueYaZsyYQUlJCXfffTennnoqHTt25JVXXgHgvffe49xzz8Vay5YtW2jXrl2FU7W5ubkMGjSIE044gSuuuMLfp61169bs3LkTgCVLlnD77beH7HxEmgKZiABgrcUYwy+//EJmZiYXX3yx0yVVLBTrrUKxszFWdkdGk+qu3eo4GE6+nPIjoxZWvFX5MSoL1qaSdi/VCOIffPAB+/fvr/T+devWkZOTw2+//caDDz7I0UcfzbPPPsvQoUPZuHEjffv2pUmTJkE/b0V++OEHHnvsMT777DNWrFjBc889B8CwYcMYOnQoK1eu5IorrmD48OH+r9myZQsLFixg9uzZjBw5EoAhQ4Ywbdo0wHut2/nz53P++efzz3/+k4yMDBYvXszixYt57bXXWL9+PRdffDFHHHEEL774In/961956KGHOOKIIw6pb/ny5Tz77LP8+OOP/PLLLzE5natdliJCSUkJSUneXwcrVqygY8eODlcUZsHubKxqN2W0746MFhXtlJz5N9iwCC6YcPivX/spcFD3+6p26Fa2a/Tky71BLgS7SW+88UZatGhBcXExRUVFTJkyhRYtWpCRkUGfPn3Yv38/gwcPZvHixVxxxRXMmzeP9u3b+79+x44d5T6vic8++4xBgwbRuHFjABo2bAjAwoULmTlzJgBXXXUV99xzj/9rBgwYQEJCAu3bt2fbtm0A9OvXj+HDh1NQUMCcOXPo2bMnaWlpfPrpp6xcudI/krZnzx7Wrl1LmzZteOGFFzjxxBPp3r07l112WYX1nXbaaRx1lDf0durUiV9//ZU//vGPIXntbqFAJhLn8vPzSUvzjvSsX7+e1q1bO1tQsIJpPVFWoJ3+D9cyQQEsMipa04WFJf+CVt0P/30Ids1fVYG7VfeQBPGLLrqIiy66yP/5lClTGDVqFF999RU//PADrVq1YsiQIcyYMYPatWsf8vXZ2dm88sorfPPNNzRu3JgmTZrQpEmTQ/5dp06dw9ZSOkJ+OGUfk5qaWu7rAWrVqkWvXr2YO3cu06ZN8wcsay0vvPACffv2PeSYmzZtIiEhgW3btuHxeEhIOHTyruxzJSYmUlxcDEBSUpK/qW5+fv5h63czBTKROJadnU2DBg0A71/bpX8dR41I9JcK1SWBpGYqXaNla9aHrqqpxqp6x4Xpe3/HHXdw/vnn88knn3DSSSdV+dhnnnmG5cuXs3PnTnbs2MGGDRtYtmwZO3bsYMeOHf7bjTEVBrWmTZvyt7/9jUaNGtG7d28uvvhi7rjjDho1asSuXbto2LAhZ5xxBlOnTuWqq65iypQpAY1KDRkyhIkTJ7JkyRImT54MQN++fXnppZc455xzSE5OZs2aNbRo0YLU1FSuvfZa3nrrLd544w0mTJjAiBEjAj5frVu3ZunSpfTr148ZM2YE/HVupEAmEqc2b95MixYtAO+C2UD+inadSIQl7aZ0h0ob+xJ4Hzq3Nq4to3HjxuzcuZN69eod9rFt2rShTZs2VT7GWsu+ffv84axsUHv++efp0qULffr0oUOHDtx///2cddZZJCYm0rlzZyZPnszzzz/Pddddx/jx42nSpAmTJk06bF19+vRh6NChXHjhhaSkpABwww038Ouvv3LKKadgraVJkya8//77PP3005x55pmceeaZdOrUiVNPPZU///nP/OEPfwjofI0ZM4brr7+ecePG0a1bt4C+xq1MNF9RvmvXrnbJkiVOl1GpzMxMevXq5XQZUUvnr/oOd+5++uknTjjhBMC78DY5OTlClYXY2Pocsi4IAONd9F9N5c7fMydWMrLS0tdCQw4Wlv93V073rhmr6Psd6PeiutPbEWKMYe7cuUydOpVZs2Zx1113cdddd/lDTah16tSJSZMm0blz57Ac3wluf98wxiy11nat6D6NkInEme+++87/lyzSsAwAACAASURBVGRJSUmF6zWiRmWjJmkNfEEqBG+8UTKyEvM6DvYu4F/yL8qFsmD70LkogFWkbt26DB06lAceeIDbbruN119/nb///e+ce+655R5XVFTEoEGDSEpKOmQ68uDPy66/KmvHjh0h26UpNadAJhJH5syZQ79+/WjYsCG///670+XUXEVhKSEZCnMhb5f385quK9NuSve4YELIFtS7VWpqKgUFBRxzzDF8+OGHzJo1ixtuuIFu3boxYcIE/zKDbdu28c033/CPf/zDPw25bt06Fi5ceMj0ZK1atSpc9B+V60ZjmAKZSJx48803ueqqqzj11FP57rvvnC4nNCoKS4X7DoSxUjVdVxYFIytxw4nvRQSnOg8ezbrwwgs599xzGTduHCeffDKjRo3i1ltvZceOHRx55JFccsklVR7PWsvevXvLrR0r/fe4ceOoVatWWF6HBE+BTCQOPP3004wYMYJBgwbxzjvvOF1OaB38Bj22fsWP0yJ8qY5I7OQto6KAVLt2bR599FGGDh3KsGHDmDRpEn/5y18Cmm40xpCRkUFGRgbHHXdcyOuV0InixSMiEog777yTESNGcNttt8VeGKuILmkUX6rbvT9QEb6IfGXrvQDatWvH3LlzGTNmDBMnTqywo71EL42QicSwwYMH88477/Dkk0+W67Ad07QIP35EYvQqwm1Pqgpk4B3xGjRoEP369Yv6RqhSngKZSIzq3r073377La+//jpDhw51upzI0SL8+BGJPnTVaShbA4cLZKXq1KkTnb0DpVIKZCIxaMWKFXz77bd89NFHnH/++U6XE3lahB8fIjF6FeER10ADmcQerSETiSGl16MrLi5m0aJF8RnGJH5EYr1gx8HQ/3lv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\n", "text/plain": [ "
" ] }, "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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fhs3r20HJA1phw/xO9Xs+ES2cKuqrVPNTlPPP/88oP3c20oNdStUnOMzc24ZddY2OOal7YCkIe1BLX532GGHAbB9+3bHSfzJ61u4SWJZazn//PM57LDDOProo13H8RU11K2INNQbN25kyZIlmpzfRl7eDkgOtHbtWoYNG+Y6hki7ZWSEfq29+uqrrFixgsWLFztO5C+q2entm9/8JgDvvfee4yT+oznUzbjlllt48cUX2bBhAwBDhw4lMzOTHj16EAzq1FesQtv+7GzmuHiRdvgQP3rqqaeYPn06mzdvBuD888+ne/fu7N69m2AwSP/+/R0n9Ie83ED9FfsaH5fUtn79el588UXuu+8+Onfu7DqO72iEuhlvvvkmH374Yf2uB3v37mX37t31VwyS2EyZVECGMQ2OeWE7IGmeGmrxo1WrVlFWVsbnn39ef2znzp1kZGTQu3dvRnu2QQAAGeVJREFUh8n8xatbuEniDRo0CICbb77ZcRJ/UkPdjGuvvbbJa9YfccQRDtL4V1FhPvk9A/XbuHlhOyBp2p49ewCYOHGi4yQibTd58mQ6dTrwpGvfvn2bPC5Ni2zhlp2ZoZqdRp566ikA7WoWB1WZZpx77rlcffXVDY516tRJk/TbITeQxfypE1zHkFaUlJQANPmHpIjXHXrooRx66KGsXLnygOPSNkWF+ZRs/4jVMya4jiJJUFdXxyWXXEJhYSGHH3646zi+lfQRamPMIGPM68aYD4wxK40xPwgfP9gY84ox5qPwf3smO1u07t27c+qppzY41rVr1/oV5CKpRjt8iN9dc801dO3atcExDYKItOyUU04BYOHChY6T+JuLKR81wC3W2sOB44DvG2OOAKYCr1lrRwCvhb926uqrr6Z79+71X1trGTFihMNEIomjhlr87sILL6S2dv+Wb4FAgCOPPNJhIhFvW7NmDfPmzePBBx8kKyvLdRxfS3pDba3dYK1dEv58J/ABkA+cDTwevtnjQFGyszUW+astYs+ePTp9KCnro48+YuDAga5jiLRbr169OOGEE+q/zsrK0iCISAuGDh0KhM7uSHycLko0xgwBCoGFQD9r7QYINd1AX3fJQjp16sRFF11Uv69pjx49DjidKJJKtMOH+N21115bf2axpqZGDbVIMx599FEAPv74Y8dJUoOxja5il7QnNiYH+C/wC2vtbGNMhbU2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\n", "text/plain": [ "
" ] }, "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": [ "
" ] }, "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": "d10bb32c38e049ef8a17c116ccc5f76c", "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": [ "" ] }, "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.6.9" }, "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 }