Quickhull (Javascript)

From LiteratePrograms

Jump to: navigation, search
Other implementations: Javascript | Python, arrays

Contents

Quickhull demonstration using javascript and canvas tag

Distant function

This code demonstrate the procedure to construct a convex hull using quickhull algorithm. When the main javascript code is triggered by a button, the intermediate base edges will be plotted. At the end, the convex hull is highlighted in red.

First of all, we need a function to calculate the distant from a point to a base edge and use it to find the point that are furtherest away from the base edge.

<<utility functions>>=
function getDistant(cpt, bl) {
    var Vy = bl[1][0] - bl[0][0];
    var Vx = bl[0][1] - bl[1][1];
    return (Vx * (cpt[0] - bl[0][0]) + Vy * (cpt[1] -bl[0][1]))
}
function findMostDistantPointFromBaseLine(baseLine, points) {
    var maxD = 0;
    var maxPt = new Array();
    var newPoints = new Array();
    for (var idx in points) {
        var pt = points[idx];
        var d = getDistant(pt, baseLine);
        if ( d > 0) {
            newPoints.push(pt);
        } else {
            continue;
        }
        if ( d > maxD ) {
            maxD = d;
            maxPt = pt;
        }
    } 
    return {'maxPoint':maxPt, 'newPoints':newPoints}
}

The recursive quickhull algorithm implementation

This code block recursively builds the base edge and return the convex hull edges. The global variable allBaseLines is only used for replaying the convex hull construction.

<<quickhull>>=
var allBaseLines = new Array();
function buildConvexHull(baseLine, points) {
    allBaseLines.push(baseLine)
    var convexHullBaseLines = new Array();
    var t = findMostDistantPointFromBaseLine(baseLine, points);
    if (t.maxPoint.length) { // if there is still a point "outside" the base line
        convexHullBaseLines = 
            convexHullBaseLines.concat( 
                buildConvexHull( [baseLine[0],t.maxPoint], t.newPoints) 
            );
        convexHullBaseLines = 
            convexHullBaseLines.concat( 
                buildConvexHull( [t.maxPoint,baseLine[1]], t.newPoints) 
            );
        return convexHullBaseLines;
    } else {  // if there is no more point "outside" the base line, the current base line is part of the convex hull
        return [baseLine];
    }    
}

Get it started

Find the extremes, and use them to construct the initial base edge and the conver hull. The buildConvexHull are called twice to build upper half and lower half convex hull.

<<start quickhull>>=
function getConvexHull(points) {
    //find first baseline
    var maxX, minX;
    var maxPt, minPt;
    for (var idx in points) {
        var pt = points[idx];
        if (pt[0] > maxX || !maxX) {
            maxPt = pt;
            maxX = pt[0];
        }
        if (pt[0] < minX || !minX) {
            minPt = pt;
            minX = pt[0];
        }
    }
    var ch = [].concat(buildConvexHull([minPt, maxPt], points),
                       buildConvexHull([maxPt, minPt], points))
    return ch;
}

Utilities for showing animation in a HTML canvas tag

This block contains some simple functions for constructing the animation.

<<utility functions for animation>>=
function getRandomPoints(numPoint, xMax, yMax) {
    var points = new Array();
    var phase = Math.random() * Math.PI * 2;
    for (var i = 0; i < numPoint/2; i++) {
        var r =  Math.random()*xMax/4;
        var theta = Math.random() * 1.5 * Math.PI + phase;
        points.push( [ xMax /4 + r * Math.cos(theta), yMax/2 + 2 * r * Math.sin(theta) ] )
    }
    var phase = Math.random() * Math.PI * 2;
    for (var i = 0; i < numPoint/2; i++) {
        var r =  Math.random()*xMax/4;
        var theta = Math.random() * 1.5 * Math.PI + phase;
        points.push( [ xMax /4 * 3 +  r * Math.cos(theta), yMax/2 +  r * Math.sin(theta) ] )
    }
    return points
}
function plotBaseLine(baseLine,color) {
    var ctx = document.getElementById('qh_demo').getContext('2d');
    var pt1 = baseLine[0]
    var pt2 = baseLine[1];
    ctx.save()
    ctx.strokeStyle = color;
    ctx.beginPath();
    ctx.moveTo(pt1[0],pt1[1]);
    ctx.lineTo(pt2[0],pt2[1]);
    ctx.stroke();
    ctx.restore();
}   
var pts;
function qhPlotPoints() {
    ctx = document.getElementById('qh_demo').getContext('2d');
    ctx.clearRect(0,0,200,200);
    ctx.fillStyle = 'rgb(0,0,0)';
    pts = getRandomPoints(250,200,200);
    for (var idx in pts) {
        var pt = pts[idx];
        ctx.fillRect(pt[0],pt[1],2,2);
    }
}
function qhPlotConvexHull() {
    var ch = getConvexHull(pts);
    var eBL = allBaseLines[0];
    function plotIntermediateBL() {
        var l = allBaseLines.shift();
        if (l) {
            plotBaseLine(l, 'rgb(180,180,180)');
            setTimeout(plotIntermediateBL, 250);
        } else {
            for (var idx in ch) {    
                var baseLine = ch[idx];
                plotBaseLine(baseLine, 'rgb(255,0,0)');
            }
            plotBaseLine(eBL,'rgb(0,255,0)');
        }
    }
    plotIntermediateBL();
}
<<qh.js>>=
utility functions
quickhull
start quickhull
utility functions for animation

The minimum HTML to run the code

To run the javascript code, we need this HTML file. It should work at least in Firefox 2.0 and Safari 2.0.

<<qh.html>>=
<html>
<head>
<script language="javascript" src="./qh.js"></script>
</head>
<body>
<button onclick='qhPlotPoints()'>Get Random Points</button> 
<button onclick='qhPlotConvexHull()'>Get convex hull</button><br>
<canvas id="qh_demo" width=200 height=200 style='margin:20pt;border:solid 1px #888;'></canvas>
</body>
</html>

Example

Online example

Download code
Views