Skip to content

Commit 7f0e780

Browse files
committed
Added Jarvi's convex hull in Lua
1 parent cac39db commit 7f0e780

2 files changed

Lines changed: 101 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
-- Convex hull algorithms implementation
2+
-- See : http://en.wikipedia.org/wiki/Convex_hull
3+
4+
-- Checks if points p, q, r are oriented counter-clockwise
5+
local function isCCW(p, q, r)
6+
local cross = (q.y - p.y) * (r.x - q.x)
7+
- (q.x - p.x) * (r.y - q.y)
8+
return cross < 0
9+
end
10+
11+
-- Returns the convex hull using Jarvi's Gift wrapping algorithm).
12+
-- It expects an array of points as input. Each point is defined
13+
-- as : {x = <value>, y = <value>}.
14+
-- See : http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
15+
-- points : an array of points
16+
-- returns : the convex hull as an array of points
17+
local function jarvi_convex_hull(points)
18+
-- We need at least 3 points
19+
local numPoints = #points
20+
if numPoints < 3 then return end
21+
22+
-- Find the left-most point
23+
local leftMostPointIndex = 1
24+
for i = 1, numPoints do
25+
if points[i].x < points[leftMostPointIndex].x then
26+
leftMostPointIndex = i
27+
end
28+
end
29+
30+
local p = leftMostPointIndex
31+
local hull = {} -- The convex hull to be returned
32+
33+
-- Process CCW from the left-most point to the start point
34+
repeat
35+
-- Find the next point q such that (p, i, q) is CCW for all i
36+
q = points[p + 1] and p + 1 or 1
37+
for i = 1, numPoints, 1 do
38+
if isCCW(points[p], points[i], points[q]) then q = i end
39+
end
40+
41+
table.insert(hull, points[q]) -- Save q to the hull
42+
p = q -- p is now q for the next iteration
43+
until (p == leftMostPointIndex)
44+
45+
return hull
46+
end
47+
48+
return {
49+
jarvi = jarvi_convex_hull
50+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-- Tests for convex_hull.lua
2+
local hull = require 'convex_hull'
3+
4+
local total, pass = 0, 0
5+
6+
local function dec(str, len)
7+
return #str < len
8+
and str .. (('.'):rep(len-#str))
9+
or str:sub(1,len)
10+
end
11+
12+
local function run(message, f)
13+
total = total + 1
14+
local ok, err = pcall(f)
15+
if ok then pass = pass + 1 end
16+
local status = ok and 'PASSED' or 'FAILED'
17+
print(('%02d. %68s: %s'):format(total, dec(message,68), status))
18+
end
19+
20+
-- Compare points
21+
local function same(p1,p2)
22+
return p1.x == p2.x and p1.y == p2.y
23+
end
24+
25+
run('Returns nil when given less than three points', function()
26+
local points = {{x = 0, y = 3}, {x = 2, y = 2}}
27+
assert(hull.jarvi(points) == nil)
28+
end)
29+
30+
run('Returns the convex hull', function()
31+
local points = {
32+
{x = 0, y = 3},
33+
{x = 2, y = 2},
34+
{x = 1, y = 1},
35+
{x = 2, y = 1},
36+
{x = 3, y = 0},
37+
{x = 0, y = 0},
38+
{x = 3, y = 3}
39+
}
40+
local chull = hull.jarvi(points)
41+
assert(#chull == 4)
42+
assert(same(chull[1], {x = 0, y = 0}))
43+
assert(same(chull[2], {x = 3, y = 0}))
44+
assert(same(chull[3], {x = 3, y = 3}))
45+
assert(same(chull[4], {x = 0, y = 3}))
46+
end)
47+
48+
49+
print(('-'):rep(80))
50+
print(('Total : %02d: Pass: %02d - Failed : %02d - Success: %.2f %%')
51+
:format(total, pass, total-pass, (pass*100/total)))

0 commit comments

Comments
 (0)