-
Notifications
You must be signed in to change notification settings - Fork 16
/
hull.go
62 lines (54 loc) · 1.23 KB
/
hull.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package delaunay
import "sort"
func cross2D(p, a, b Point) float64 {
return (a.X-p.X)*(b.Y-p.Y) - (a.Y-p.Y)*(b.X-p.X)
}
// ConvexHull returns the convex hull of the provided points.
func ConvexHull(points []Point) []Point {
// copy points
pointsCopy := make([]Point, len(points))
copy(pointsCopy, points)
points = pointsCopy
// sort points
sort.Slice(points, func(i, j int) bool {
a := points[i]
b := points[j]
if a.X != b.X {
return a.X < b.X
}
return a.Y < b.Y
})
// filter nearly-duplicate points
distinctPoints := points[:0]
for i, p := range points {
if i > 0 && p.squaredDistance(points[i-1]) < eps {
continue
}
distinctPoints = append(distinctPoints, p)
}
points = distinctPoints
// find upper and lower portions
var U, L []Point
for _, p := range points {
for len(U) > 1 && cross2D(U[len(U)-2], U[len(U)-1], p) > 0 {
U = U[:len(U)-1]
}
for len(L) > 1 && cross2D(L[len(L)-2], L[len(L)-1], p) < 0 {
L = L[:len(L)-1]
}
U = append(U, p)
L = append(L, p)
}
// reverse upper portion
for i, j := 0, len(U)-1; i < j; i, j = i+1, j-1 {
U[i], U[j] = U[j], U[i]
}
// construct complete hull
if len(U) > 0 {
U = U[:len(U)-1]
}
if len(L) > 0 {
L = L[:len(L)-1]
}
return append(L, U...)
}