Quick Hull
Intro
Algorithm
C++ implementation
Discussion
Complexity
Half Space Partition in 2D
Half Space Partition in 3D
Problem in 3D
References
It's a fast way to compute the convex hull of a set of points on the plane.
The partitioning step does all the work. The basic idea is as follows:
Dimension N space.
Let bd be the list of points on the boundary.
The aim is to process a list of points and find
which of them is in bd.
Let a half space container be a data structure with
a half space and a list of points. These points are
inside or on the half space partition. Access the list
of points by .L member. Access the half space by .H member.
Let cs be a stack of half space containers.
function HCpartition
(
half space container HC,
points list L
)
Copy any points in L that are inside HC.H or
on the boundary of HC.H to HC.L .
function createNewHC
(
half space container HC
)
if HC.L is empty
return.
Find the point p furthest from the halfspace HC.L.
Push p to bd.
Create N new half space containers Hi
with p and N-1 points from HC.H. Each of these
should not see other points in HC.H hence they all
face outwards of the convex hull created.
For each Hi
Call function HCpartition(Hi,HC.L)
To start the algorithm find N points on the boundary/hull. Push these into bd. For example in 2D search for the furthest left and right points on the x axis. O(n) in time.
Use these initial points on the boundary to create two half space
containers H1 and H2 that have their
half spaces intersect each other
at their boundaries and together partion the N dimensional
space.
Let L be the list of points.
Call function HCpartition(H1,L)
Call function HCpartition(H2,L)
Push H1 and H2 onto cs.
while cs is not empty do
pop cs to HC.
If HC.L empty
continue.
Call createNewHC(HC)
Consider the case in 2D where there are three or more colinear points at the top and the left most and right most of these are also the extremes in the set of points.
It is possible without tackling non-uniqueness that a point on the hull could be forgotten because it is colinear with two other points on the hull.
To handle this the algorithm uses copying of points (or realistically references to the points) rather than moving the points.
Better logic could improve this by moving rather than copying the points. This is better for the algorithm implementation as if we can determine uniquely which container to move which point then no automatic memory management is needed (eg copying to STL data structures).
I believe the handeling of non-unique points has lead to a robust algorithm.
The worst case is O(n^2). The real case (when implemented) is O(nlogn). The optimal case is O(nlogn). When modified by randomizing the input of points then the expected complexity is O(nlogn) so the quick hull algorithm can be made robust.
A lot of people require guarantees on algorithms so they may reject quick hull on the ground of worst case. The worst case (which can be avoided with randomized input) is a partition where only one point is partitioned and the others are above the newly formed half spaces. This is going to happen only for a very unusual data set. For example a data set where pretty much all the data points are on or near the hull. If this is not the case then you are going to get the optimum complexity O(nlogn).
See [1] for an analysis.
In 2D the hull is always convex as the algorithm continues. In three dimensions and higher this is not always the case.
The problem occures when we construct two neighboring half spaces which can see each others furthest point. Then they will share points during processing and it will take longer to converge, plus have inner points identified as boundary points.
To stop this happening the mesh needs to be re tessellated so that it is again a convex hull. In 3D this means making the v intersection be filled in.
This requires a half space to have knowledge of its neighboring half space. One possible solution is to have each newely created half space have its edge information between its parent and itself stored in a data structure. eg ordered points of the edge so edges (3,5) and (5,3) would be represented as an ordered pair 3,5 ording from lowest to highest.
When a half space is constructed it tests if this ordered pair already exists. If it does the convexity test (test if the two half spaces form a v) is done and the edge ordering removed from the edge table. If not the edge is added to the edge table with a reference to the current halfspace, and processing of it is suspended until the other half space is encountered.
The linked simplexes data structure could be used for this algorithm too where after a partition is constructed, the neighboring faces are tested for convexity and if this fails the mesh is re balanced. Since the faces are half space containers the points of both the original half spaces are fed to the two newely formed half spaces.