Skip to content

Instantly share code, notes, and snippets.

@WetHat
Last active July 27, 2022 20:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save WetHat/192eb4f42a5140ab6834cdbc0b750d78 to your computer and use it in GitHub Desktop.
Save WetHat/192eb4f42a5140ab6834cdbc0b750d78 to your computer and use it in GitHub Desktop.
A Partially Balanced Bounding Box Tree Implementation
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# A Simple Bounding Box Tree\n",
"\n",
"A [bounding box tree](https://en.wikipedia.org/wiki/Bounding_volume_hierarchy) is a hierarchical structure on a set of geometric objects. Nodes are recursively grouped into smaller sets and enclosed within larger bounding boxes. All geometric objects are wrapped in bounding volumes (boxes) that form the leaf nodes of the tree.\n",
"\n",
"![Box Tree](https://upload.wikimedia.org/wikipedia/commons/2/2a/Example_of_bounding_volume_hierarchy.svg)\n",
"\n",
"In CAD sytems bounding box trees are used to support several operations on sets of geometric objects efficiently, such as:\n",
"* Object collision detection. Typical objects whose boxes are used for collision detection are:\n",
" * bodies (parts)\n",
" * faces\n",
" * edges\n",
" Frequently uses a quick filter to avoid unnecessary, yet expensive computations such as intersections or point in face tests.\n",
"* Ray tracing\n",
"\n",
"Usually [R-Trees](https://en.wikipedia.org/wiki/R-tree) are used to build bounding box hierarchies by partitioning space by hyperplanes. However, in this _Gist_ we make an attempt to demonstrate the _from-scratch_ implementation of a k-dimensional bounding box tree based on space partitioning by k-dimensional sectors.\n",
"\n",
"Typical examples of that type of trees are [quadtrees](https://en.wikipedia.org/wiki/Quadtree) for two dimensions (k = 2) and [octrees](https://en.wikipedia.org/wiki/Octree) for three dimensions (k=3)\n",
"\n",
"Quadtrees based box hierarchies recursively partition space into 4 quadrants:\n",
"\n",
"~~~bob\n",
" +y\n",
" ^\n",
" │\n",
" ┌─────────┬────┬────┐\n",
" │ │ │ │\n",
" │ ├────┼────┤\n",
" │ │ │ │\n",
"-x <-├─────────┼────┴────┤-> +x\n",
" │ │ │\n",
" │ │ │\n",
" │ │ │\n",
" └─────────┴─────────┘\n",
" │\n",
" v\n",
" -y\n",
"~~~\n",
"\n",
"Octree based box hierarchies recursively partition space into 8 octants:\n",
"\n",
"~~~bob\n",
" +Y\n",
" +z ^\n",
" ^ /\n",
" +──── │ ──+─────────+\n",
" ╱ │ ╱ ╱│\n",
" ╱ │ ╱ ╱ │\n",
" ╱ ╱ ╱ │\n",
" +─────────+─────────+ +\n",
" ╱ ╱ ╱│ ╱│\n",
" ╱ ╱ ╱ │ ╱ │\n",
" ╱ ╱ ╱ │╱ │\n",
"-x <-- +─────────+─────────+ + -----> +x\n",
" │ │ │ ╱│ ╱\n",
" │ │ │ + + ╱\n",
" │ │ │╱│╱│╱\n",
" +─────────┼────┬────+ + +\n",
" │ │ │ │╱│╱\n",
" │ / ├────┼────+ +\n",
" │ / │ │ │╱\n",
" └──── / ──┴────┴────+\n",
" v │\n",
" -y v\n",
" -z\n",
"~~~\n",
"\n",
"These kind of trees are significantly easier to implement than R-trees, but also slightly less efficient. However, we will demonstrate that the tree construction complexity is still $O \\left( n \\cdot log(n) \\right)$. Depending on the spacial distribution of the boxes some of the sectors in the boxtree will be sparsely populated rendering the tree only partially balanced. For example if boxes line up in a straight line the tree will degenerate to a binary tree."
]
},
{
"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",
"* [matplotlib](https://matplotlib.org/) - Python plotting library.\n",
"* [NumPy](https://numpy.org/) - Numerical Linear Algebra\n",
"* [SciPy](https://www.scipy.org/) - Open-source software for mathematics, science, and engineering.\n",
"\n",
"This notebook makes extensive use of the [@agoose77/jupyterlab-markup](https://github.com/agoose77/jupyterlab-markup) extension for JupyterLab which renders simple ASCII-art drawings in [fenced code blocks](https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks) marked with the language identifier _bob_ as images. _bob_ stands for the _Svgbob_ ascii to svg converter. _GitHub_ currently does not render _bob_ blocks, however, even the raw ASCII-art drawings look somewhat acceptable. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Technical Approach\n",
"\n",
"We will focus on 2-dimensional box trees in this _Gist_, but design the datastructure and algorithms in a way that they can be applied to general k-dimensions. 2-dimensional boxes are easier to visualize.\n",
"\n",
"The data structure consists of two types of nodes:\n",
"* Sector Nodes (interior nodes)\n",
"* Box collection nodes (leaf nodes) which can contain up to $k$ boxes.\n",
"\n",
"For $k = 1$ such a tree looks like\n",
"\n",
"~~~ bob\n",
" ╭───────╮ \n",
" │ s0 │ <- sector node (root)\n",
" ╰───┬───╯ \n",
" │ \n",
" ╭──────────┴──────────╮ \n",
" │ │ \n",
" v v \n",
" ╭───────╮ ╭───────╮ \n",
" │ s1.1 │ │ s1.2 │ <- level 1 sector nodes (interior)\n",
" ╰───┬───╯ ╰───┬───╯ \n",
" │ │ \n",
" ╭────┴────╮ ╭────┴────╮ \n",
" │ │ │ │ \n",
" v v v v \n",
"╭───────╮ ╭───────╮ ╭───────╮ ╭───────╮ \n",
"│ bx1 │ │ bx2 │ │ bx3 │ │ bx4 │ <- box nodes (leaf)\n",
"╰───────╯ ╰───────╯ ╰───────╯ ╰───────╯ \n",
"~~~\n",
"\n",
"The the box tree will be reasonably (partially) balanced if most of the boxes are added during tree construction.\n",
"That is, the boxtree is essentially an [off-line](https://en.wikipedia.org/wiki/Online_algorithm) algorithm.\n",
"\n",
"Adding/removing boxes later can be supported, but no re-balancing is performed and the tree may get out-of balance so much that search performance is negatively affected. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Setting up the Environment\n",
"\n",
"In this section we prepare the environment for developing the box tree data structure by:\n",
"* importing the necessary external Python packages\n",
"* creating utility functions and classes\n",
"Importing the necessary packages to prepare the development environment:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python Package Imports"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"init_cell": true,
"tags": [
"CodeExport"
]
},
"outputs": [],
"source": [
"import numpy as np\n",
"import math\n",
"import time\n",
"from typing import TypeVar,Iterable,Generic,Callable,Any,Tuple,List\n",
"import sys\n",
"from functools import reduce, wraps\n",
"from collections import Counter\n",
"from scipy.optimize import curve_fit\n",
"import operator"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"init_cell": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)]\n"
]
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"print(sys.version)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Performance Measurement Instrumentation"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def callcounted(fnc : Callable) -> Callable:\n",
" '''Decorator to put on a method to count the number of calls.'''\n",
" @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": [
"## Intervals and Boxes\n",
"\n",
"A simple representation of $k$-dimensional bounding boxes based on $k$ one dimensional intervals.\n",
"\n",
"For example a 3-dimensional bounding box is represented by 3 intervals like so:\n",
"\n",
"~~~ bob \n",
" ■───────■\n",
" ╱. ╱│ ^\n",
" ╱ . ╱ │ │ z-interval\n",
"■───────■ │ v\n",
"│ □. . │. ■ \n",
"│ . │ ╱ ^\n",
"│. │╱ ╱ y-interval\n",
"■───────■ v\n",
"<------->\n",
"x-interval\n",
"~~~"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Class Interval\n",
"\n",
"Representation of a closed interval $I_{a,b}$ of numbers defined as:\n",
"$$\n",
" I_{l,r} := \\left\\{ x,l,r \\in \\mathbb{R} \\; \\big| \\; l \\leq x \\wedge x \\leq r \\right\\}\n",
"$$\n",
"\n",
"~~~ bob\n",
" l r\n",
"<- - - - -[────────────]- - - - ->\n",
"~~~\n",
"\n",
"`Interval(l : float, r : float) -> Interval`\n",
">\n",
"> `l` : float\n",
"> : The left bound of the interval.\n",
">\n",
"> `r` : float\n",
"> : The right bound of the interval.\n",
"\n",
"#### Properties\n",
"\n",
"> `center` : dp.ndarray\n",
"> : The $k$-dimensional center point of the box\n",
">\n",
"> `valid` : bool\n",
"> : `True` if the interval $I_{l,r}$ satisfies $l \\leq r$.\n",
">\n",
"> `size` : float\n",
"> : The size of the interval\n",
"> ~~~ bob\n",
"> l r\n",
"> <- - - - -[────────────]- - - - ->\n",
"> <---------->\n",
"> size\n",
"> ~~~\n",
"\n",
"#### Operators\n",
"\n",
"> `&`\n",
"> : Compute the the intersection of two intervals\n",
"> ~~~ bob\n",
"> <- - - - -[────────────]- - - - - - - ->\n",
"> <- - - - - - - -[────────────]- - - - ->\n",
"> |\n",
"> v Intersection\n",
"> <- - - - - - - -[──────]- - - - - - - ->\n",
"> ~~~\n",
"> \n",
"> `|`\n",
"> : Compute the union of two intervals.\n",
"> ~~~ bob\n",
"> <- - - - -[────────────]- - - - - - - - - - - - ->\n",
"> <- - - - - - - - - - - - -[────────────]- - - - ->\n",
"> | \n",
"> v Union\n",
"> <- - - - -[────────────────────────────]- - - - ->\n",
"> ~~~\n",
"\n",
"#### Methods\n",
"\n",
">`contains(x : float) -> bool`\n",
">> Check if the interval contains the number`x`.\n",
">>\n",
">> `x : float`\n",
">> : A number to check for containment\n",
">>\n",
">> **Returns**\n",
">> : `True` if its does, `False` otherwise."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"tags": [
"CodeExport"
]
},
"outputs": [],
"source": [
"class Interval:\n",
" def __init__ (self, l : float, r : float):\n",
" self._bounds = (l,r)\n",
"\n",
" def __repr__ (self):\n",
" return 'Interval(%r,%r)' % self._bounds\n",
"\n",
" @property\n",
" def center(self):\n",
" return sum(self._bounds)/2\n",
"\n",
" @property\n",
" def valid(self):\n",
" l,r = self._bounds\n",
" return l <= r\n",
"\n",
" def __and__ (self, other):\n",
" l,r = self._bounds\n",
" lo,ro = other._bounds\n",
" return Interval(max(l,lo),min(r,ro))\n",
"\n",
" def __or__(self,other):\n",
" l,r = self._bounds\n",
" lo,ro = other._bounds\n",
" return Interval(min(l,lo),max(r,ro))\n",
"\n",
" def contains(self,x : float) -> bool :\n",
" l,r = self._bounds\n",
" return l <= x and x <= r\n",
"\n",
" @property\n",
" def size(self):\n",
" l,r = self._bounds\n",
" return r - l"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Class kdBox\n",
"\n",
"A $k$-dimensional box defined by $k$ intervals.\n",
"\n",
"Example: 3d-box\n",
"~~~ bob \n",
" ■───────■\n",
" ╱. ╱│ ^\n",
" ╱ . ╱ │ │ z-interval\n",
"■───────■ │ v\n",
"│ □. . │. ■ \n",
"│ . │ ╱ ^\n",
"│. │╱ ╱ y-interval\n",
"■───────■ v\n",
"<------->\n",
"x-interval\n",
"~~~\n",
"\n",
"`kdBox(i1 : Interval,...) -> kdBox`\n",
">\n",
"> `i1,...` : Interval\n",
"> : $k$-intervals defining a $k$-dimensional box\n",
"\n",
"#### Properties\n",
"\n",
"> `k` : int\n",
"> : box dimensionality.\n",
">\n",
"> `center` : np.ndarray\n",
"> : The $k$-dimensional center point of the box.\n",
">\n",
"> `valid` : bool\n",
"> : `True` if all $k$ intervals of the box are valid-.\n",
">\n",
"> `volume` : float\n",
"> : The k dimensional volume of the box.\n",
"\n",
"#### Operators\n",
"\n",
"> `&`\n",
"> : Computes the intersection of two $k$-dimensional boxes.\n",
"> Example: 2d-Box intersection\n",
"> ~~~ bob\n",
"> ┌───────────┐\n",
"> │ │\n",
"> │ │\n",
"> │ ┌─────│─────┐ --> ┌─────┐\n",
"> │ │ │ │ │ │\n",
"> └─────│─────┘ │ └─────┘\n",
"> │ │\n",
"> └───────────┘\n",
"> ~~~\n",
"> \n",
"> `|`\n",
"> : Computes the union of two $k$-dimensional boxes.\n",
"> Example: 2d-box union\n",
"> ~~~ bob\n",
"> ┌───────────┐ ┌─────────────────┐\n",
"> │ │ │ │\n",
"> │ │ │ │\n",
"> │ ┌─────│─────┐ --> │ │\n",
"> │ │ │ │ │ │\n",
"> └─────│─────┘ │ │ │\n",
"> │ │ │ │\n",
"> └───────────┘ └─────────────────┘\n",
"> ~~~\n",
"\n",
"#### Methods\n",
"\n",
">`contains(point : np.ndarray) -> bool`\n",
">> Check if a k-dimensional point `point` is contained in the box.\n",
">>\n",
">> `point : np.ndarray`\n",
">> : k-dimensional point for containment testing\n",
">>\n",
">> **Returns**\n",
">> : `True` if it is; `False` otherwise."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"tags": [
"CodeExport"
]
},
"outputs": [],
"source": [
"class kdBox:\n",
" def __init__ (self,*intervals):\n",
" self.intervals = intervals\n",
" self.owner = None\n",
"\n",
" def __repr__(self):\n",
" return 'kdBox(%s)' % ','.join(['%s' % i for i in self.intervals])\n",
"\n",
" @property\n",
" def k(self):\n",
" return len(self.intervals)\n",
"\n",
" @callcounted\n",
" def __and__(self,other):\n",
" return kdBox(*[ (i2[0] & i2[1]) for i2 in zip(self.intervals, other.intervals)])\n",
"\n",
" @callcounted\n",
" def __or__(self,other):\n",
" return kdBox(*[ (i2[0] | i2[1]) for i2 in zip(self.intervals, other.intervals)])\n",
"\n",
" @property\n",
" def center(self) -> np.ndarray :\n",
" return np.array([i.center for i in self.intervals], dtype='float64')\n",
"\n",
" @property\n",
" def valid(self):\n",
" return all([i.valid for i in self.intervals])\n",
"\n",
" @property\n",
" def volume(self) -> float:\n",
" return reduce(operator.mul,[i.size for i in self.intervals])\n",
"\n",
" def contains(self,point : np.ndarray) -> bool:\n",
" for i,x in zip(self.intervals,point):\n",
" if not i.contains(x):\n",
" return False\n",
" return True\n",
" def plot(self,ax, *args, **kwargs):\n",
" 'Plot a box to matplotlib.'\n",
" if self.k == 2:\n",
" xil,xir = self.intervals[0]._bounds\n",
" yil,yir = self.intervals[1]._bounds\n",
" pts = [ [xil, yil],\n",
" [xil, yir],\n",
" [xir, yir],\n",
" [xir, yil],\n",
" [xil, yil] ]\n",
" return ax.plot([p[0] for p in pts],[p[1] for p in pts], *args, **kwargs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Building the Box Tree\n",
"\n",
"Using the preparatory work in the previous section we can now start to build up the box tree structure as described in the chapter _Technical Approach_.\n",
"\n",
"We build the structure bottom-up and start with `kdBoxCollection`:\n",
"~~~ bob\n",
" ╭───────╮ \n",
" │ s0 │ <- sector node (root)\n",
" ╰───┬───╯ \n",
" │ \n",
" ╭──────────┴──────────╮ \n",
" │ │ \n",
" v v \n",
" ╭───────╮ ╭───────╮ \n",
" │ s1.1 │ │ s1.2 │ <- level 1 sector nodes (interior)\n",
" ╰───┬───╯ ╰───┬───╯ \n",
" │ │ \n",
" ╭────┴────╮ ╭────┴────╮ \n",
" │ │ │ │ \n",
" v v v v \n",
"╭───────╮ ╭───────╮ ╭───────╮ ╭───────╮ \n",
"│ bx1 │ │ bx2 │ │ bx3 │ │ bx4 │ <- leaf nodes of type kdBoxCollection\n",
"╰───────╯ ╰───────╯ ╰───────╯ ╰───────╯ \n",
"~~~"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Class kdBoxCollection - Leaf Nodes of a Boxtree\n",
"\n",
"An iterable collection of k-dimensional boxes.\n",
"\n",
"Instances of this class\n",
"* constitute the leaf nodes of a boxtree.\n",
"* Are used for offline construction of boxtrees.\n",
"\n",
"`kdBoxCollection(k : int, boxes : Iterable[kdBox])`\n",
">\n",
"> `k` : int\n",
"> : The box dimensionality\n",
">\n",
"> `boxes` : Iterable[kdBox]\n",
"> : The boxes to add to this collection\n",
"\n",
"#### Properties\n",
"\n",
"> `center` : np.ndarray\n",
"> : The center of gravity computed over the center points of the boxes in this collection.\n",
">\n",
"> `count` : int\n",
"> : The number of boxes in the collection.\n",
">\n",
"> `superbox` : kdBox\n",
"> : The outer bounds of all boxes in the collection.\n",
"\n",
"#### Methods\n",
"\n",
"> `collisions(box : kdBox) -> Iterable[kdBox]`\n",
">> A generator to yield all intersecting boxes.\n",
">> This is an $O(n)$ algorithm.\n",
">>\n",
">> `box : kdBox`\n",
">> : Search box to intersect with all boxes in the collection.\n",
">>\n",
">> **Returns**\n",
">> : Each box in the collection which intersects with `box`.\n",
">\n",
"> `add_box(box : kdBox)`\n",
">> Add a box `box` to the collection and updates the collections `superbox` and `center` properties.\n",
">>\n",
">> `box : kdBox`\n",
">> : The box to add to the collection."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"tags": [
"CodeExport"
]
},
"outputs": [],
"source": [
"class kdBoxCollection:\n",
" def __init__(self,k : int ,boxes : Iterable[kdBox] = []):\n",
" self.superbox = kdBox(*([Interval(math.inf,-math.inf)]*k))\n",
" self.centersum = np.array([0.0] * k,dtype='float64')\n",
" self.boxes = []\n",
" self.k=k\n",
" for bx in boxes:\n",
" self.add_box(bx)\n",
"\n",
" def add_box(self,box : kdBox):\n",
" self.superbox |= box\n",
" self.boxes.append(box)\n",
" self.centersum += box.center\n",
"\n",
" def __iter__(self):\n",
" return iter(self.boxes)\n",
"\n",
" @property\n",
" def center(self) -> np.ndarray:\n",
" return self.centersum / len(self.boxes)\n",
"\n",
" def collisions(self,box : kdBox) -> Iterable[kdBox]:\n",
" for bx in self.boxes:\n",
" intersectionbox = bx & box\n",
" if intersectionbox.valid:\n",
" yield bx\n",
"\n",
" @property\n",
" def count(self) -> int:\n",
" return len(self.boxes)\n",
"\n",
" def plot(self,ax, legend=False):\n",
" for i,bx in enumerate(self.boxes):\n",
" b = bx.plot(ax)\n",
" if legend:\n",
" b[0].set_label('box %r' % i)\n",
"\n",
" b = self.superbox.plot(ax, linestyle='dashed', color='gray')\n",
" if legend:\n",
" b[0].set_label('Outer Bounds')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now put this to use by constructing a small collection of 2-d boxes we can then plot"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"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 1. A Collection of 2-d Boxes\n",
"bxs= kdBoxCollection(2) # boxes will have 2 dimensions\n",
"bxs.add_box(kdBox(Interval(0,5), Interval(7,9))) # Box 1\n",
"bxs.add_box(kdBox(Interval(3,9), Interval(10,11))) # Box 2\n",
"\n",
"## Set up plotting\n",
"fig,ax = plt.subplots()\n",
"ax.set_xlabel('x')\n",
"ax.set_ylabel('y')\n",
"ax.grid(True)\n",
"ax.set_title('Fig 1. A Collection of 2-d Boxes')\n",
"\n",
"bxs.plot(ax, legend=True) # plot the collection\n",
"\n",
"ax.legend()\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## kdSectorNode - Building the Boxtree Scaffolding\n",
"\n",
"Now we build the internal, recursive structure of a boxtree by introducing the `kdSectorNode` class:\n",
"\n",
"~~~ bob\n",
" ╭───────╮ \n",
" │ s0 │ <- sector node (root)\n",
" ╰───┬───╯ \n",
" │ \n",
" ╭──────────┴──────────╮ \n",
" │ │ \n",
" v v \n",
" ╭───────╮ ╭───────╮ \n",
" │ s1.1 │ │ s1.2 │ <- Structure Nodes (kdSectorNode)\n",
" ╰───┬───╯ ╰───┬───╯ \n",
" │ │ \n",
" ╭────┴────╮ ╭────┴────╮ \n",
" │ │ │ │ \n",
" v v v v \n",
"╭───────╮ ╭───────╮ ╭───────╮ ╭───────╮ \n",
"│ bx1 │ │ bx2 │ │ bx3 │ │ bx4 │ <- leaf nodes of type kdBoxCollection\n",
"╰───────╯ ╰───────╯ ╰───────╯ ╰───────╯ \n",
"~~~\n",
"\n",
"Instances of this class recursively subdivide the k-dimensional space into $2^k$ sectors. These sectors are aligned with a $k$ dimensional coordinate system centered at the center-of-gravity of all boxes contained in the leaf nodes of its sub-tree.\n",
"\n",
"Boxes are sorted into these sectors depending on where their centers lie.\n",
"\n",
"For $k=2$ a `kdSectorNode` with one box added looks like this:\n",
"\n",
"~~~bob\n",
" +y\n",
" ^\n",
" │ ┌───┐\n",
" │ │ o │\n",
" │ └───┘\n",
" │\n",
"-x <----------o----------> +x\n",
" │^\n",
" │ ╲\n",
" │ '- center of gravity \n",
" │\n",
" v\n",
" -y\n",
"~~~\n",
"\n",
"This implies that boxes may _bleed_ into adjacent sectors if they are close to sector boundaries:\n",
"\n",
"~~~ bob\n",
" +y\n",
" ^\n",
" ┌───┐ │ ┌───┐\n",
" │ │ │ | o |\n",
" │ │ │ └───┘\n",
" │ o │ │\n",
"-x <--│---│---o----------> +x\n",
" │ │ │^\n",
" └───┘ │ ╲\n",
" │ '- center of gravity \n",
" │\n",
" v\n",
" -y\n",
"~~~\n",
"\n",
"#### Constructor: kdSectorNode(boxes)\n",
"\n",
"> `boxes` : kdBoxCollection\n",
"> : the boxes assigned to this instance. The boxes will be recursively subdivided until there are less than $k$\n",
"> boxes left. If that is the case subdivision stops and the `kdBoxCollection` is added as leaf node.\n",
" \n",
"#### Properties\n",
"\n",
"> `superbox` : kdBox\n",
"> : The outer bounding box of all boxes in the subtree rooted at this intance.\n",
"> \n",
"> `center` : np.ndarray\n",
"> : The center of gravity of all boxes in the subtree rooted at this instance.\n",
"\n",
"#### Methods\n",
"\n",
"> `collisions(box)`\n",
"> : Generator to yield all boxes in the subtree rooted at this instance which intersect `box`.\n",
"\n",
"> `measure_depths(depth,depths)`\n",
"> : Recursively measure the depth of the subtree starting at this instance to the leaf nodes.\n",
" \n",
">> `depth` : int\n",
">> : is the depth of this instance relative to the boxtree root\n",
" \n",
">> `depths` : Counter\n",
">> : The frequency of depths in the boxtree."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"tags": [
"CodeExport"
]
},
"outputs": [],
"source": [
"class kdSectorNode():\n",
" def __init__(self, boxes : kdBoxCollection):\n",
" self.superbox = boxes.superbox\n",
" center = boxes.center # center point of boxes\n",
" self.center = center\n",
"\n",
" k = center.shape[0] # dimensionality\n",
" # number of semi infinite sectors required to subdivide\n",
" # k-d space\n",
" sectorcount = k**2\n",
"\n",
" # create all variations of axis aligned semi infinite sectors\n",
" # anchored at the center-gravity\n",
" sectors = [None]*sectorcount\n",
" for bits in range(0,sectorcount):\n",
" intervals = [None]*k # k intervals needed for k-d box\n",
" for i in range(0,k):\n",
" mask = 1 << i\n",
" if bits & mask:\n",
" intervals[i] = Interval(center[i],math.inf)\n",
" else:\n",
" intervals[i] = Interval(-math.inf,center[i])\n",
"\n",
" sectors[bits] = kdBox(*intervals)\n",
" # make a box collection for each semi infinite sector\n",
" # to hold the assigned boxes\n",
" sector_boxes = [kdBoxCollection(k) for _ in range(0,sectorcount)]\n",
" for box in boxes:\n",
" # find sector where the box center is located\n",
" for i,sector in enumerate(sectors):\n",
" if sector.contains(box.center):\n",
" sector_boxes[i].add_box(box)\n",
" break\n",
" # build the child nodes\n",
" self.sector_nodes = [None]*sectorcount\n",
" for i in range(0,sectorcount):\n",
" boxcount = sector_boxes[i].count\n",
" if boxcount > 0:\n",
" if boxcount <= sectorcount:\n",
" self.sector_nodes[i] = sector_boxes[i] # leaf node\n",
" else:\n",
" self.sector_nodes[i] = kdSectorNode(sector_boxes[i])\n",
"\n",
" def collisions(self,box):\n",
" intersectionbox = box & self.superbox\n",
" if intersectionbox.valid:\n",
" for nd in self.sector_nodes:\n",
" if nd:\n",
" yield from nd.collisions(box)\n",
"\n",
" def measure_depths(self,depth : int, depths : Counter):\n",
" depth += 1\n",
" for nd in self.sector_nodes:\n",
" if nd:\n",
" if type(nd) == kdBoxCollection :\n",
" depths.update({(depth+1):1})\n",
" else:\n",
" nd.measure_depths(depth,depths)\n",
" else:\n",
" depths.update({depth:1})\n",
"\n",
" def plot(self,ax):\n",
" ax.scatter([self.center[0]],[self.center[1]], marker='+', color='b', label='Sector Origin')\n",
" for nd in self.sector_nodes:\n",
" if nd:\n",
" nd.plot(ax)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"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 2. kdSectorNode Containing a Few 2-d Boxes\n",
"bxs= kdBoxCollection(2) # boxes will have 2 dimensions\n",
"\n",
"bxs.add_box(kdBox(Interval(0,2), Interval(0,4))) # Box 1\n",
"bxs.add_box(kdBox(Interval(1,3), Interval(1,5))) # Box 2\n",
"\n",
"bxs.add_box(kdBox(Interval(5,7), Interval(5,9))) # Box 3\n",
"bxs.add_box(kdBox(Interval(6,8), Interval(6,10))) # Box 4\n",
"\n",
"bxs.add_box(kdBox(Interval(1,3), Interval(6,7))) # Box 5\n",
"bxs.add_box(kdBox(Interval(1.5,2), Interval(8.5,9.5))) # Box 6\n",
"\n",
"\n",
"kds = kdSectorNode(bxs)\n",
"\n",
"## Set up plotting\n",
"fig,ax = plt.subplots()\n",
"ax.set_xlabel('x')\n",
"ax.set_ylabel('y')\n",
"ax.grid(True)\n",
"ax.set_title('kdSectorNode Containing a Few 2-d Boxes')\n",
"\n",
"kds.plot(ax)\n",
"ax.legend()\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Class kdBoxTree\n",
"\n",
"Finally we can wrap everything up by providing a container class which is to represent a facade to the boxtree structure offering simple construction, metrics and lookup methods.\n",
"\n",
"~~~ bob\n",
" ╭- - - - - - -╮ \n",
" : :\n",
" : ╭───────╮ : \n",
"kdBoxTree -> : │ s0 │ : <- Root node (kdSectorNode)\n",
" : ╰───┬───╯ :\n",
" : │ :\n",
" ╰- - - - - - -╯ \n",
" │ \n",
" ╭──────────┴──────────╮ \n",
" │ │ \n",
" v v \n",
" ╭───────╮ ╭───────╮ \n",
" │ s1.1 │ │ s1.2 │ <- Structure Nodes (kdSectorNode)\n",
" ╰───┬───╯ ╰───┬───╯ \n",
" │ │ \n",
" ╭────┴────╮ ╭────┴────╮ \n",
" │ │ │ │ \n",
" v v v v \n",
"╭───────╮ ╭───────╮ ╭───────╮ ╭───────╮ \n",
"│ bx1 │ │ bx2 │ │ bx3 │ │ bx4 │ <- leaf nodes of type kdBoxCollection\n",
"╰───────╯ ╰───────╯ ╰───────╯ ╰───────╯ \n",
"~~~\n",
"\n",
"`kdBoxTree(k : int , boxes : Iterable[kdBox])`\n",
"\n",
"> `k` : int\n",
"> : The dimensionality of the tree.\n",
">\n",
"> `boxes` : Iterable[kdBox]\n",
"> : The collection of boxes to build the boxtree with.\n",
"\n",
"#### Methods\n",
"\n",
"> `measure_depths() -> Counter`\n",
">> Create a statistics for tree depths measured from the root down to the leaf nodes.\n",
">>\n",
">> **Returns**\n",
">> : A `Counter` object whose keys are the depths found in the tree and the values are the frequency with which\n",
">> these depths occur. \n",
">\n",
"> `collisions(box : kdBox) -> Iterable[kdBox]`\n",
">> A generator function which yields all boxes which intersect with `box`.\n",
">>\n",
">> `box : kdBox`\n",
">> : The search box.\n",
">>\n",
">> **Returns**\n",
">> : Each box that intersects with the search box."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"tags": [
"CodeExport"
]
},
"outputs": [],
"source": [
"class kdBoxTree:\n",
" def __init__(self,k : int, boxes : Iterable[kdBox]):\n",
" bxs = kdBoxCollection(k,boxes=boxes)\n",
" self.root = kdSectorNode(bxs)\n",
"\n",
" def measure_depths(self) -> Counter:\n",
" c = Counter()\n",
" if self.root:\n",
" self.root.measure_depths(0,c)\n",
" return c\n",
"\n",
" def collisions(self,box : kdBox) -> Iterable[kdBox]:\n",
" if self.root:\n",
" yield from self.root.collisions(box)\n",
"\n",
" def plot(self,ax):\n",
" if self.root:\n",
" self.root.plot(ax)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With this we can illustrate the basic operations of a boxtree: creation and collision detection"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Fig 3. Basic Boxtree operations\n",
"sbx = kdBox(Interval(-10,-0.1),Interval(-10,7)) # Search box for collision detection\n",
"# a bunch of sample boxes\n",
"bxs = [ kdBox(Interval(-10.0,-8.0),Interval(-10.0,-8.0)),\n",
" kdBox(Interval(-10.0,-8.0),Interval(8.0,10.0)),\n",
" kdBox(Interval(-8.0,-6.0),Interval(-8.0,-6.0)),\n",
" kdBox(Interval(-8.0,-6.0),Interval(6.0,8.0)),\n",
" kdBox(Interval(-6.0,-4.0),Interval(-6.0,-4.0)),\n",
" kdBox(Interval(-6.0,-4.0),Interval(4.0,6.0)),\n",
" kdBox(Interval(-4.0,-2.0),Interval(-4.0,-2.0)),\n",
" kdBox(Interval(-4.0,-2.0),Interval(2.0,4.0)),\n",
" kdBox(Interval(-2.0,0.0),Interval(-2.0,0.0)),\n",
" kdBox(Interval(-2.0,0.0),Interval(-0.0,2.0)),\n",
" kdBox(Interval(0.0,2.0),Interval(0.0,2.0)),\n",
" kdBox(Interval(0.0,2.0),Interval(-2.0,-0.0)),\n",
" kdBox(Interval(2.0,4.0),Interval(2.0,4.0)),\n",
" kdBox(Interval(2.0,4.0),Interval(-4.0,-2.0)),\n",
" kdBox(Interval(4.0,6.0),Interval(4.0,6.0)),\n",
" kdBox(Interval(4.0,6.0),Interval(-6.0,-4.0)),\n",
" kdBox(Interval(6.0,8.0),Interval(6.0,8.0)),\n",
" kdBox(Interval(6.0,8.0),Interval(-8.0,-6.0)),\n",
" kdBox(Interval(8.0,10.0),Interval(8.0,10.0)),\n",
" kdBox(Interval(8.0,10.0),Interval(-10.0,-8.0)) ]\n",
"kdt=kdBoxTree(2,bxs) # sample boxes\n",
"\n",
"fig,(ax1,ax2) = plt.subplots(1,2,sharey=True)\n",
"fig.set_size_inches(8, 4, forward=True)\n",
"ax1.set_xlabel('x')\n",
"ax1.set_ylabel('y')\n",
"\n",
"ax1.set_title('Boxtree Construction')\n",
"kdt.plot(ax1)\n",
"\n",
"# collision detections\n",
"ax2.set_title('Collisions With Search Box')\n",
"\n",
"# find all collisions with the search box\n",
"collisions = list(kdt.collisions(sbx))\n",
"\n",
"# add the found collisions to a collection for plotting\n",
"found = kdBoxCollection(2,boxes=collisions)\n",
"ax2.set_xlabel('x')\n",
"b = sbx.plot(ax2)\n",
"b[0].set_label('Search Box')\n",
"found.plot(ax2)\n",
"ax2.legend()\n",
"\n",
"fig.suptitle('Fig 3. Basic Boxtree operations')\n",
"\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Performance Measurements\n",
"\n",
"We make an attempt to measure the performance of the tree in a way that is independent of the language chosen to implement the structure. We use:\n",
"* The number of box intersections as a measure for the _effort_ spent to detect box collisions\n",
"* The number of box unions as a measure for the _effort_ spent to build a boxtree"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating the Test Data\n",
"\n",
"We create a variable number of boxes arranged like an X or a grid like so:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"def make_boxes_X(n = 10):\n",
" ll = None\n",
" for x in np.linspace(-10,10,n):\n",
" if not ll is None:\n",
" yield kdBox(Interval(ll,x),Interval(ll,x))\n",
" yield kdBox(Interval(ll,x),Interval(-x,-ll))\n",
" ll = x\n",
"\n",
"def make_boxes_grid(n = 10):\n",
" for i,x in enumerate(np.linspace(-10,10,n)):\n",
" if i % 2 == 0:\n",
" lx=x\n",
" else:\n",
" for j,y in enumerate(np.linspace(-10,10,n)):\n",
" if j % 2 == 0:\n",
" ly = y\n",
" else:\n",
" yield kdBox(Interval(lx,x),Interval(ly,y))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"text/plain": [
"Text(0.5, 0.98, 'Fig 4. Sample Box Collections')"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Fig 4. Sample Box Collections\n",
"\n",
"fig,(ax1,ax2) = plt.subplots(1,2, sharey=True)\n",
"fig.set_size_inches(8, 4, forward=True)\n",
"\n",
"# plot the first sample collection\n",
"ax1.set_xlabel('x')\n",
"ax1.set_ylabel('y')\n",
"bxs1 = kdBoxCollection(2,make_boxes_grid(7))\n",
"bxs1.plot(ax1)\n",
"ax1.set_title('Grid Layout')\n",
"\n",
"# plot the second sample collection\n",
"ax2.set_xlabel('x')\n",
"bxs2 = kdBoxCollection(2,make_boxes_X(7))\n",
"bxs2.plot(ax2)\n",
"ax2.set_title('X Layout')\n",
"\n",
"fig.suptitle('Fig 4. Sample Box Collections')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Boxtree Construction Cost\n",
"\n",
"Here we measure the cost of creating a boxtree from $n$ boxes in terms of the number of box unions required."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Fig.5 Boxtree Construction Cost\n",
"def boxtree_construction_cost_grid(n : int) -> Tuple[int,int]:\n",
" 'Create 2*n boxes and measure the boxtree construction cost'\n",
" bxs = kdBoxCollection(2,make_boxes_grid(n))\n",
" kdBox.__or__.callcount = 0\n",
" kdBoxTree(2,bxs)\n",
"\n",
" return (bxs.count,kdBox.__or__.callcount)\n",
"\n",
"def boxtree_construction_cost_X(n : int) -> Tuple[int,int]:\n",
" 'Create 2*n boxes and measure the boxtree construction cost'\n",
" bxs = kdBoxCollection(2,make_boxes_X(n))\n",
" kdBox.__or__.callcount = 0\n",
" kdBoxTree(2,bxs)\n",
"\n",
" return (bxs.count,kdBox.__or__.callcount)\n",
"\n",
"fig,(ax1,ax2) = plt.subplots(1,2,sharey=True)\n",
"fig.set_size_inches(8, 4, forward=True)\n",
"\n",
"# plot the construction cost for the X-shaped collection\n",
"ax1.set_title('X-Shaped Collection')\n",
"ax1.set_xlabel('# Boxes')\n",
"ax1.set_ylabel('Construction Cost (# Unions)')\n",
"ax1.grid()\n",
"# Sample the construction cost for box collections of increasing size\n",
"samples= [boxtree_construction_cost_X(i) for i in range(2,600,10) ]\n",
"ax1.plot([sample[0] for sample in samples], [sample[1] for sample in samples], label='measured')\n",
"\n",
"# fit a*n*log(n) to verify theoretical big O complexity prediction.\n",
"\n",
"def bigO(n : int, a : float) -> float:\n",
" return a*n*np.log(n)\n",
"\n",
"popt, pcov = curve_fit(bigO, [sample[0] for sample in samples], [sample[1] for sample in samples])\n",
"ax1.plot([sample[0] for sample in samples], [ bigO(sample[0],popt[0]) for sample in samples],\n",
" label='$%.3f*n*log(n)$' % popt[0])\n",
"\n",
"ax1.legend()\n",
"\n",
"# plot the construction cost for the X-shaped collection\n",
"\n",
"ax2.set_title('Grid-Shaped Collection')\n",
"ax2.set_xlabel('# Boxes')\n",
"ax2.grid()\n",
"# Sample the construction cost for box collections of increasing size\n",
"samples2= [boxtree_construction_cost_grid(i) for i in range(2,90,10) ]\n",
"ax2.plot([sample[0] for sample in samples2], [sample[1] for sample in samples2], label='measured')\n",
"\n",
"# fit a*n*log(n) to verify theoretical big O complexity prediction.\n",
"\n",
"popt, pcov = curve_fit(bigO, [sample[0] for sample in samples2], [sample[1] for sample in samples2])\n",
"ax2.plot([sample[0] for sample in samples2], [ bigO(sample[0],popt[0]) for sample in samples2],\n",
" label='$%.3f*n*log(n)$' % popt[0])\n",
"\n",
"ax2.legend()\n",
"\n",
"fig.suptitle('Fig 5. Boxtree Construction Cost')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Boxtree Balance\n",
"\n",
"The tree branch depth distribution, i.e. the number of occurrences of tree branches of a certain depth is a good measure for the cost of collision tests. Ideally, all branches are short and have the same length. In reality the tree depth distribution depends on the geometric configuration of the boxes.\n",
"\n",
"In the section below we measure the distribution of branch length for our X-shaped box collection. Due to the X-shape configuration we expect a tree balance which resembles the balance of two binary trees. The X-shaped box collection which we will use for testing has 1022 boxes, therefore we expect a tree depth $d$ to be so that $2 \\cdot 2^d = 1022$. Solving this for $d$ gives the expected depth as: $d \\approx 9$.\n",
"\n",
"We also test the grid-shaped box collection which should give a perfectly balanced tree of depth 5 ($4^5=1024$)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 576x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Fig 6. Branch Depth Distribution\n",
"fig,(ax1,ax2) = plt.subplots(1,2)\n",
"fig.set_size_inches(8, 4, forward=True)\n",
"\n",
"# Branch depth for the x-shaped collection\n",
"ax1.set_xlabel('Depth')\n",
"ax1.set_ylabel('Frequency')\n",
"\n",
"bxs = kdBoxCollection(2,make_boxes_X(512)) # make 1022 boxes\n",
"bxt = kdBoxTree(2,bxs) # and build a tree for that\n",
"\n",
"depths = bxt.measure_depths()\n",
"keys=list(depths.keys())\n",
"keys.sort()\n",
"ax1.bar(keys, [depths[k] for k in keys])\n",
"ax1.set_title('X-Shaped Collection')\n",
"ax1.grid()\n",
"\n",
"# Branch depth for the x-shaped collection\n",
"ax2.set_xlabel('Depth')\n",
"\n",
"bxs = kdBoxCollection(2,make_boxes_grid(65)) # make 1024 boxes\n",
"bxt2 = kdBoxTree(2,bxs) # and build a tree for that\n",
"\n",
"depths2 = bxt2.measure_depths()\n",
"keys2=list(depths.keys())\n",
"keys2.sort()\n",
"ax2.bar(keys2, [depths2[k] for k in keys2])\n",
"ax2.set_title('Grid-Shaped Collection')\n",
"ax2.grid()\n",
"\n",
"plt.suptitle('Fig 6.Branch Depth Distribution')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The tree depth distribution above shows that the tree made from the X-shaped collection is reasonably well balanced. The majority of boxes are in branches which are close to the optimal depth. The boxes in the shorter branches have smaller collision costs. Overall collision cost is expected slightly better than that of a double binary tree lookup.\n",
"\n",
"The tree built from the grid shaped collection is perfectly balanced."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Collision Cost\n",
"\n",
"The cost of a collision depends on several factors:\n",
"* The spatial distribution of the bounding boxes.\n",
"* the topology of the boxtree.\n",
"* the search box\n",
"\n",
"This makes it difficult to create a simple metric for assessing the performance of collision detection. To get some idea of the performance advantage of a boxtree collision test relative to a _brute-force_ collision test we set up a test case like so:\n",
"* generate box collections using `make_boxes_X` (X-shaped collection) and `make_boxes_grid` (grid-shaped box collection).\n",
"* generate a sequence of search boxes with volumes covering between 0 and 100% of the boxtree. The search boxes start at the center of the bottom of the box collection's superbox and increase in volume until the last search box covers the entire volume of the box collection. "
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [
{
"data": {
"image/png": "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uRV/vrB/lcccOXKEIUOGkJ+fT2pqKgsWLABg9uzZ/PDDD6xbt4709HROO+00Ro0axRNPPMFpp53G2WefzR133MFnn33GiSeeyEUXXcS4ceP41a9+dfTcmZmZLF68mKKiIkaPHs3cuXPp0KEDM2fO5KGHHuKNN97g1ltv5eWXX6Z3794sX76c3/72t0djqMrq1at58803Wb58OarK6aefzujRoyktLa1we1kC37JlC5MmTeLNN99kyJAhVZaxYsUKNm7cSPfu3TnvvPOYPXs2l19+ebWxmcD19/lb+DxxH9ec3o2OraKObo8ID+PCwZ2Ia9vcj9EFL8/8BfWTwxo6f11wwQWce+65R6e5sPxlTMVCtqLmL56XDpYuXcp1111HQkIC3333HVdddRXh4eHExsYyevRoVq5cyUUXXcSrr77KqFGjePbZZznxxBMrPfeVV14JOMklISGB8ePHA04S79SpE7m5uSxZsoSJEycefU5BQYFXcX/33XdceumltGjRAoBf/vKXfPvtt6hqhdsvuugi0tLSuPjii/nggw/o37/qPwAAw4cPp1evXgBcddVVfPfdd5bogtjH61J4efGPXHN6Nx6/dKC/wzH1wPJX5Sx/GV8J2Yqa5y9Hf026eOaZZ5Kenk5aWhqqWulxGzZsICYmhpSUlCrPV5ZsVJX+/fuzdOnSY/ZnZ2fTpk2bo4m2JiqLr6q4W7duTdeuXfn++++PJrqIiIijlwRUlcLCwqPHlx++bsPZg9fGlGz+MGs9w7q3rbaVxtRc+ffUHznM8pflL9MwrI+aH23evJmSkhJiYmIYNWoUM2fOpKSkhLS0NL755huGDx/Orl27+Mc//sHatWuZN28ey5cvByA6Oprc3NwKz9unTx/S0tKOJrqioiISExNp1aoVPXv25P333wecRLNu3TqvYh01ahRz5szh8OHD5OXl8eGHH3L22WdXuh2gSZMmzJkzh7feeot3330XgB49ehxNtHPnzqWoqOhoGStWrGDnzp2UlpYyc+ZMRo4cWYt31fjbobxCfv32Klo1i+Df155KkwhLM41RXfNXTk5OhecN9Py1evVqwPKXaTgh26LmL2V9PMBJNNOnTyc8PJxLL72UpUuXMnjwYESEp556itjYWMaPH8/TTz9N586def3115k8eTIrV65k0qRJ3HTTTbzyyivHdG4FJ8HMmjWL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\n",
"text/plain": [
"<Figure size 720x288 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Fig 7. Collision Cost\n",
"def boxtree_collision_cost(bxt : kdBoxTree, box : kdBox) -> int:\n",
" kdBox.__and__.callcount = 0\n",
" list(bxt.collisions(box))\n",
" return (box.volume/bxt.root.superbox.volume ,kdBox.__and__.callcount)\n",
"\n",
"# X shaped box collection\n",
"X_bxs = kdBoxCollection(2,make_boxes_X(512)) # make 1022 boxes\n",
"X_bxcount = X_bxs.count\n",
"\n",
"X_bxt = kdBoxTree(2,X_bxs) # and build a tree for that\n",
"\n",
"# measure the number of collisions for a sequence of boxes\n",
"box_sizes = np.linspace(0,10,100)\n",
"X_collision_costs = [ boxtree_collision_cost(X_bxt,kdBox(Interval(-sz,sz), Interval(-10,2*sz-10))) for sz in box_sizes]\n",
"\n",
"# Grid shaped box collection\n",
"Grid_bxs = kdBoxCollection(2,make_boxes_grid(64)) # make 1024 boxes\n",
"Grid_bxcount = Grid_bxs.count\n",
"\n",
"Grid_bxt = kdBoxTree(2,Grid_bxs) # and build a tree for that\n",
"Grid_collision_costs = [ boxtree_collision_cost(Grid_bxt,kdBox(Interval(-sz,sz), Interval(-10,2*sz-10))) for sz in box_sizes]\n",
"\n",
"# Plot the Results\n",
"fig,(ax1,ax2) = plt.subplots(1,2,sharey=True)\n",
"fig.set_size_inches(10, 4, forward=True)\n",
"\n",
"# X-shaped box collection result\n",
"ax1.set_xlabel('Search Box Coverage (%)')\n",
"ax1.set_ylabel('Box Intersections')\n",
"ax1.set_title('X-shaped Box Collection')\n",
"ax1.grid()\n",
"ax1.plot([c[0]*100 for c in X_collision_costs],[c[1] for c in X_collision_costs],label=\"Boxtree Lookup\")\n",
"ax1.plot([0,100],[X_bxcount,X_bxcount], label='Brute Force')\n",
"ax1.legend()\n",
"\n",
"# Grid shaped box collection result\n",
"ax2.set_xlabel('Search Box Coverage (%)')\n",
"ax2.set_title('Grid-shaped Box Collection')\n",
"ax2.grid()\n",
"ax2.plot([c[0]*100 for c in Grid_collision_costs],[c[1] for c in Grid_collision_costs],label=\"Boxtree Lookup\")\n",
"ax2.plot([0,100],[Grid_bxcount,Grid_bxcount], label='Brute Force')\n",
"ax2.legend()\n",
"\n",
"plt.suptitle('Fig 7. Collision Cost')\n",
"\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discussion of the Collision Test Result\n",
"\n",
"* The boxtree lookup graph show logarithmic behavior.\n",
"* For _small_ search boxes boxtree lookups are considerably faster than _brute-force_ lookups.\n",
"* Search boxes which cover more that 70% of the boxtree volume become *more expensive* then _brute force_ lookups\n",
"\n"
]
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment