@@ -2,10 +2,11 @@ package common
22
33import (
44 "math/rand"
5+ "sort"
56
6- quickhull "github.com/markus-wa/quickhull-go"
7-
7+ r2 "github.com/golang/geo/r2"
88 r3 "github.com/golang/geo/r3"
9+ quickhull "github.com/markus-wa/quickhull-go"
910)
1011
1112// Inferno is a list of Fires with helper functions.
@@ -51,31 +52,89 @@ func (inf Inferno) Active() Inferno {
5152 return res
5253}
5354
54- // ConvexHull2D returns the vertices making up the 2D convex hull of all the fires in the inferno.
55+ // ConvexHull2D returns clockwise sorted corner points making up the 2D convex hull of all the fires in the inferno.
5556// Useful for drawing on 2D maps.
56- func (inf Inferno ) ConvexHull2D () []r3.Vector {
57- pointCloud := make ([]r3.Vector , 0 , len (inf .Fires ))
57+ func (inf Inferno ) ConvexHull2D () []r2.Point {
58+ pointCloud := make ([]r3.Vector , len (inf .Fires ))
59+ for i , f := range inf .Fires {
60+ pointCloud [i ] = f .Vector
61+ pointCloud [i ].Z = 0
62+ }
5863
59- for _ , f := range inf .Fires {
60- pointCloud = append (pointCloud , r3.Vector {
61- X : f .Vector .X ,
62- Y : f .Vector .Y ,
63- Z : 0 ,
64- })
64+ vertices := convexHull (convexHull (pointCloud ).Vertices ).Vertices
65+ points := make ([]r2.Point , len (vertices ))
66+ for i , v := range vertices {
67+ points [i ] = r2.Point {X : v .X , Y : v .Y }
68+ }
69+ sortPointsClockwise (points )
70+
71+ return points
72+ }
73+
74+ // pointsClockwiseSorter implements the Sort interface for slices of Point
75+ // with a comparator for sorting points in clockwise order around their center.
76+ type pointsClockwiseSorter struct {
77+ center r2.Point
78+ points []r2.Point
79+ }
80+
81+ func (s pointsClockwiseSorter ) Len () int { return len (s .points ) }
82+
83+ func (s pointsClockwiseSorter ) Swap (i , j int ) { s .points [i ], s .points [j ] = s .points [j ], s .points [i ] }
84+
85+ func (s pointsClockwiseSorter ) Less (i , j int ) bool {
86+ a , b := s .points [i ], s .points [j ]
87+
88+ if a .X - s .center .X >= 0 && b .X - s .center .X < 0 {
89+ return true
90+ }
91+ if a .X - s .center .X < 0 && b .X - s .center .X >= 0 {
92+ return false
93+ }
94+ if a .X - s .center .X == 0 && b .X - s .center .X == 0 {
95+ if a .Y - s .center .Y >= 0 || b .Y - s .center .Y >= 0 {
96+ return a .Y > b .Y
97+ }
98+ return b .Y > a .Y
99+ }
100+
101+ // compute the cross product of vectors (s.center -> a) X (s.center -> b)
102+ det := (a .X - s .center .X )* (b .Y - s .center .Y ) - (b .X - s .center .X )* (a .Y - s .center .Y )
103+ if det < 0 {
104+ return true
105+ }
106+ if det > 0 {
107+ return false
65108 }
66109
67- return quickhull .ConvexHull (pointCloud )
110+ // points a and b are on the same line from the s.center
111+ // check which point is closer to the s.center
112+ d1 := (a .X - s .center .X )* (a .X - s .center .X ) + (a .Y - s .center .Y )* (a .Y - s .center .Y )
113+ d2 := (b .X - s .center .X )* (b .X - s .center .X ) + (b .Y - s .center .Y )* (b .Y - s .center .Y )
114+ return d1 > d2
68115}
69116
70- // ConvexHull3D returns the vertices making up the 3D convex hull of all the fires in the inferno.
71- func (inf Inferno ) ConvexHull3D () []r3.Vector {
117+ func sortPointsClockwise (points []r2.Point ) {
118+ sorter := pointsClockwiseSorter {
119+ center : r2 .RectFromPoints (points ... ).Center (),
120+ points : points ,
121+ }
122+ sort .Sort (sorter )
123+ }
124+
125+ // ConvexHull3D returns the 3D convex hull of all the fires in the inferno.
126+ func (inf Inferno ) ConvexHull3D () quickhull.ConvexHull {
72127 pointCloud := make ([]r3.Vector , len (inf .Fires ))
73128
74129 for i , f := range inf .Fires {
75130 pointCloud [i ] = f .Vector
76131 }
77132
78- return quickhull .ConvexHull (pointCloud )
133+ return convexHull (pointCloud )
134+ }
135+
136+ func convexHull (pointCloud []r3.Vector ) quickhull.ConvexHull {
137+ return new (quickhull.QuickHull ).ConvexHull (pointCloud , false , false , 0 )
79138}
80139
81140// NewInferno creates a inferno and sets the Unique-ID.
0 commit comments