diff --git a/hex.go b/hex.go index c09d1e0..c559d37 100644 --- a/hex.go +++ b/hex.go @@ -16,7 +16,7 @@ const ( directionS ) -var directions = []hex{ +var directions = []Hex{ NewHex(1, 0), NewHex(1, -1), NewHex(0, -1), @@ -44,20 +44,20 @@ var directions = []hex{ // \ _ _ / (0,1) \ _ _ / // \ +R / // \ _ _ / -type hex struct { +type Hex struct { q int // x axis r int // y axis s int // z axis } -func NewHex(q, r int) hex { +func NewHex(q, r int) Hex { - h := hex{q: q, r: r, s: -q - r} + h := Hex{q: q, r: r, s: -q - r} return h } -func (h hex) String() string { +func (h Hex) String() string { return fmt.Sprintf("(%d,%d)", h.q, h.r) } @@ -77,7 +77,7 @@ func NewFractionalHex(q, r float64) fractionalHex { } // Rounds a FractionalHex to a Regular Hex -func (h fractionalHex) Round() hex { +func (h fractionalHex) Round() Hex { roundToInt := func(a float64) int { if a < 0 { @@ -101,42 +101,42 @@ func (h fractionalHex) Round() hex { } else { s = -q - r } - return hex{q, r, s} + return Hex{q, r, s} } // Adds two hexagons -func HexAdd(a, b hex) hex { +func HexAdd(a, b Hex) Hex { return NewHex(a.q+b.q, a.r+b.r) } // Subtracts two hexagons -func HexSubtract(a, b hex) hex { +func HexSubtract(a, b Hex) Hex { return NewHex(a.q-b.q, a.r-b.r) } // Scales an hexagon by a k factor. If factor k is 1 there's no change -func HexScale(a hex, k int) hex { +func HexScale(a Hex, k int) Hex { return NewHex(a.q*k, a.r*k) } -func HexLength(hex hex) int { +func HexLength(hex Hex) int { return int((math.Abs(float64(hex.q)) + math.Abs(float64(hex.r)) + math.Abs(float64(hex.s))) / 2.) } -func HexDistance(a, b hex) int { +func HexDistance(a, b Hex) int { sub := HexSubtract(a, b) return HexLength(sub) } // Returns the neighbor hexagon at a certain direction -func HexNeighbor(h hex, direction direction) hex { +func HexNeighbor(h Hex, direction direction) Hex { directionOffset := directions[direction] return NewHex(h.q+directionOffset.q, h.r+directionOffset.r) } // Returns the slice of hexagons that exist on a line that goes from hexagon a to hexagon b -func HexLineDraw(a, b hex) []hex { +func HexLineDraw(a, b Hex) []Hex { hexLerp := func(a fractionalHex, b fractionalHex, t float64) fractionalHex { return NewFractionalHex(a.q*(1-t)+b.q*t, a.r*(1-t)+b.r*t) @@ -153,7 +153,7 @@ func HexLineDraw(a, b hex) []hex { a_nudge := NewFractionalHex(float64(a.q)+0.000001, float64(a.r)+0.000001) b_nudge := NewFractionalHex(float64(b.q)+0.000001, float64(b.r)+0.000001) - results := make([]hex, 0) + results := make([]Hex, 0) step := 1. / math.Max(float64(N), 1) for i := 0; i <= N; i++ { @@ -163,9 +163,9 @@ func HexLineDraw(a, b hex) []hex { } // Returns the set of hexagons around a certain center for a given radius -func HexRange(center hex, radius int) []hex { +func HexRange(center Hex, radius int) []Hex { - var results = make([]hex, 0) + var results = make([]Hex, 0) if radius >= 0 { for dx := -radius; dx <= radius; dx++ { @@ -181,9 +181,9 @@ func HexRange(center hex, radius int) []hex { } // Returns the set of hexagons that form a rectangle with the specified width and height -func HexRectangleGrid(width, height int) []hex { +func HexRectangleGrid(width, height int) []Hex { - results := make([]hex, 0) + results := make([]Hex, 0) for q := 0; q < width; q++ { qOffset := int(math.Floor(float64(q) / 2.)) @@ -198,9 +198,9 @@ func HexRectangleGrid(width, height int) []hex { } // Determines if a given hexagon is visible from another hexagon, taking into consideration a set of blocking hexagons -func HexHasLineOfSight(center hex, target hex, blocking []hex) bool { +func HexHasLineOfSight(center Hex, target Hex, blocking []Hex) bool { - contains := func(s []hex, e hex) bool { + contains := func(s []Hex, e Hex) bool { for _, a := range s { if a == e { return true @@ -220,9 +220,9 @@ func HexHasLineOfSight(center hex, target hex, blocking []hex) bool { } // Returns the list of hexagons that are visible from a given hexagon -func HexFieldOfView(source hex, candidates []hex, blocking []hex) []hex { +func HexFieldOfView(source Hex, candidates []Hex, blocking []Hex) []Hex { - results := make([]hex, 0) + results := make([]Hex, 0) for _, h := range candidates { diff --git a/hex_test.go b/hex_test.go index cc70be4..aad1f9d 100644 --- a/hex_test.go +++ b/hex_test.go @@ -8,9 +8,9 @@ import ( func TestHexAdd(t *testing.T) { var testCases = []struct { - hexA hex - hexB hex - expected hex + hexA Hex + hexB Hex + expected Hex }{ {NewHex(1, -3), NewHex(3, -7), NewHex(4, -10)}, } @@ -28,9 +28,9 @@ func TestHexAdd(t *testing.T) { func TestHexSubtract(t *testing.T) { var testCases = []struct { - hexA hex - hexB hex - expected hex + hexA Hex + hexB Hex + expected Hex }{ {NewHex(1, -3), NewHex(3, -7), NewHex(-2, 4)}, } @@ -48,9 +48,9 @@ func TestHexSubtract(t *testing.T) { func TestHexScale(t *testing.T) { var testCases = []struct { - hexA hex + hexA Hex factor int - expected hex + expected Hex }{ {NewHex(1, -3), 2, NewHex(2, -6)}, {NewHex(-2, 3), 2, NewHex(-4, 6)}, @@ -84,9 +84,9 @@ func TestHexScale(t *testing.T) { func TestHexNeighbor(t *testing.T) { var testCases = []struct { - origin hex + origin Hex direction direction - expected hex + expected Hex }{ {NewHex(0, -1), directionSE, NewHex(1, -1)}, @@ -130,8 +130,8 @@ func TestHexNeighbor(t *testing.T) { func TestHexDistance(t *testing.T) { var testCases = []struct { - origin hex - destination hex + origin Hex + destination Hex expected int }{ {NewHex(-1, -1), NewHex(1, -1), 2}, @@ -169,8 +169,8 @@ func TestHexDistance(t *testing.T) { func TestHexLineDraw(t *testing.T) { var testCases = []struct { - origin hex - destination hex + origin Hex + destination Hex expected string // the expected path serialized to string }{ {NewHex(-3, -1), NewHex(3, -3), "[(-3,-1) (-2,-1) (-1,-2) (0,-2) (1,-2) (2,-3) (3,-3)]"}, @@ -276,7 +276,7 @@ func TestHexRectangle(t *testing.T) { // The hexagons marked with an X are non-visible. The remaining 16 are visible. func TestHexFieldOfView(t *testing.T) { - universe := []hex{ + universe := []Hex{ NewHex(0, 0), NewHex(0, 1), NewHex(0, 2), @@ -303,7 +303,7 @@ func TestHexFieldOfView(t *testing.T) { NewHex(5, 1), } - losBlockers := []hex{NewHex(2, 0), NewHex(3, 0)} + losBlockers := []Hex{NewHex(2, 0), NewHex(3, 0)} actual := HexFieldOfView(NewHex(1, 1), universe, losBlockers) @@ -319,7 +319,7 @@ func TestHexFieldOfView(t *testing.T) { func BenchmarkHexDistance(b *testing.B) { var testCases = []struct { - destination hex + destination Hex }{ {NewHex(0, 0)}, {NewHex(100, 100)}, @@ -341,7 +341,7 @@ func BenchmarkHexDistance(b *testing.B) { func BenchmarkHexLineDraw(b *testing.B) { var testCases = []struct { - destination hex + destination Hex }{ {NewHex(0, 0)}, {NewHex(100, 100)}, @@ -385,6 +385,6 @@ func BenchmarkHexRange(b *testing.B) { func BenchmarkHexHasLineOfSight(b *testing.B) { for i := 0; i < b.N; i++ { - HexHasLineOfSight(NewHex(1, 1), NewHex(4, -1), []hex{NewHex(2, 0), NewHex(3, 0)}) + HexHasLineOfSight(NewHex(1, 1), NewHex(4, -1), []Hex{NewHex(2, 0), NewHex(3, 0)}) } } diff --git a/layout.go b/layout.go index a7612ca..6e6508f 100644 --- a/layout.go +++ b/layout.go @@ -2,66 +2,66 @@ package hexgrid import "math" -type point struct { - x float64 - y float64 +type Point struct { + X float64 + Y float64 } -type layout struct { - orientation orientation - size point // multiplication factor relative to the canonical hexagon, where the points are on a unit circle - origin point // center point for hexagon 0,0 +type Layout struct { + Orientation Orientation + Size Point // multiplication factor relative to the canonical hexagon, where the points are on a unit circle + Origin Point // center point for hexagon 0,0 } -type orientation struct { +type Orientation struct { f0, f1, f2, f3, b0, b1, b2, b3, startAngle float64 } -var orientationPointy orientation = orientation{math.Sqrt(3.), math.Sqrt(3.) / 2., 0., 3. / 2., math.Sqrt(3.) / 3., -1. / 3., 0., 2. / 3., 0.5} +var OrientationPointy Orientation = Orientation{math.Sqrt(3.), math.Sqrt(3.) / 2., 0., 3. / 2., math.Sqrt(3.) / 3., -1. / 3., 0., 2. / 3., 0.5} -var orientationFlat orientation = orientation{3. / 2., 0., math.Sqrt(3.) / 2., math.Sqrt(3.), 2. / 3., 0., -1. / 3., math.Sqrt(3.) / 3., 0.} +var OrientationFlat Orientation = Orientation{3. / 2., 0., math.Sqrt(3.) / 2., math.Sqrt(3.), 2. / 3., 0., -1. / 3., math.Sqrt(3.) / 3., 0.} // HexToPixel returns the center pixel for a given hexagon an a certain layout -func HexToPixel(l layout, h hex) point { - - M := l.orientation - size := l.size - origin := l.origin - x := (M.f0*float64(h.q) + M.f1*float64(h.r)) * size.x - y := (M.f2*float64(h.q) + M.f3*float64(h.r)) * size.y - return point{x + origin.x, y + origin.y} +func HexToPixel(l Layout, h Hex) Point { + + M := l.Orientation + size := l.Size + origin := l.Origin + x := (M.f0*float64(h.q) + M.f1*float64(h.r)) * size.X + y := (M.f2*float64(h.q) + M.f3*float64(h.r)) * size.Y + return Point{x + origin.X, y + origin.Y} } // PixelToHex returns the corresponding hexagon axial coordinates for a given pixel on a certain layout -func PixelToHex(l layout, p point) fractionalHex { +func PixelToHex(l Layout, p Point) fractionalHex { - M := l.orientation - size := l.size - origin := l.origin + M := l.Orientation + size := l.Size + origin := l.Origin - pt := point{(p.x - origin.x) / size.x, (p.y - origin.y) / size.y} - q := M.b0*pt.x + M.b1*pt.y - r := M.b2*pt.x + M.b3*pt.y + pt := Point{(p.X - origin.X) / size.X, (p.Y - origin.Y) / size.Y} + q := M.b0*pt.X + M.b1*pt.Y + r := M.b2*pt.X + M.b3*pt.Y return fractionalHex{q, r, -q - r} } -func HexCornerOffset(l layout, c int) point { +func HexCornerOffset(l Layout, c int) Point { - M := l.orientation - size := l.size + M := l.Orientation + size := l.Size angle := 2. * math.Pi * (M.startAngle - float64(c)) / 6. - return point{size.x * math.Cos(angle), size.y * math.Sin(angle)} + return Point{size.X * math.Cos(angle), size.Y * math.Sin(angle)} } // Gets the corners of the hexagon for the given layout, starting at the E vertex and proceeding in a CCW order -func HexagonCorners(l layout, h hex) []point { +func HexagonCorners(l Layout, h Hex) []Point { - corners := make([]point, 0) + corners := make([]Point, 0) center := HexToPixel(l, h) for i := 0; i < 6; i++ { offset := HexCornerOffset(l, i) - corners = append(corners, point{center.x + offset.x, center.y + offset.y}) + corners = append(corners, Point{center.X + offset.X, center.Y + offset.Y}) } return corners } diff --git a/layout_test.go b/layout_test.go index f99bd53..4f0aa55 100644 --- a/layout_test.go +++ b/layout_test.go @@ -26,7 +26,7 @@ import ( // * * // ********* // -var defaultLayout = layout{size: point{100, 100}, origin: point{0, 0}, orientation: orientationFlat} +var defaultLayout = Layout{Size: Point{100, 100}, Origin: Point{0, 0}, Orientation: OrientationFlat} // utility functions func round(num float64) int { @@ -41,7 +41,7 @@ func toFixed(num float64, precision int) float64 { func TestHexToPixel(t *testing.T) { var testCases = []struct { - hexA hex + hexA Hex expected string }{ {NewHex(0, 0), "0.0;0.0"}, @@ -54,7 +54,7 @@ func TestHexToPixel(t *testing.T) { pixel := HexToPixel(defaultLayout, tt.hexA) - actual := fmt.Sprintf("%.1f;%.1f", pixel.x, pixel.y) + actual := fmt.Sprintf("%.1f;%.1f", pixel.X, pixel.Y) if actual != tt.expected { t.Error("Expected:", tt.expected, "got:", actual) @@ -65,17 +65,17 @@ func TestHexToPixel(t *testing.T) { func TestPixelToHex(t *testing.T) { var testCases = []struct { - point point - expected hex + Point Point + expected Hex }{ - {point{0, 0}, NewHex(0, 0)}, - {point{150, 87}, NewHex(1, 0)}, - {point{300, 10}, NewHex(2, -1)}, + {Point{0, 0}, NewHex(0, 0)}, + {Point{150, 87}, NewHex(1, 0)}, + {Point{300, 10}, NewHex(2, -1)}, } for _, tt := range testCases { - actual := PixelToHex(defaultLayout, tt.point).Round() + actual := PixelToHex(defaultLayout, tt.Point).Round() if actual != tt.expected { t.Error("Expected:", tt.expected, "got:", actual) @@ -118,8 +118,8 @@ func TestHexagonCorners(t *testing.T) { for i := 0; i < len(corners); i++ { - actualX := toFixed(corners[i].x, 1) - actualY := toFixed(corners[i].y, 1) + actualX := toFixed(corners[i].X, 1) + actualY := toFixed(corners[i].Y, 1) expectedX := testCase[i].roundedX expectedY := testCase[i].roundedY