-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrid_hex.go
More file actions
147 lines (127 loc) · 5.01 KB
/
Copy pathgrid_hex.go
File metadata and controls
147 lines (127 loc) · 5.01 KB
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package grid
import (
geom "github.com/gravitton/geometry"
"github.com/gravitton/geometry/types/floats"
"github.com/gravitton/geometry/types/ints"
hex "github.com/gravitton/hexagon"
"github.com/gravitton/x/slices"
)
// NewHexagonPointyTopGrid creates a new hexagon grid with a pointy-top layout.
//
// hexSize is the circumradius of each hex cell — the distance from the center
// to any vertex, which equals the edge length for a regular hexagon.
// Use equal W and H for an undistorted hex; different values skew the cell
// independently along each axis (e.g. Sz(1, 0.5) produces a hex that is
// compressed vertically). The pixel bounding box of each cell is
// hexSize.W*√3 wide and hexSize.H*2 tall.
//
// Grid cell (0, 0) is placed at the top-left: even rows are unshifted and
// odd rows are offset half a cell to the right, so no cell center has a
// negative x coordinate.
func NewHexagonPointyTopGrid[T any](size ints.Size, hexSize floats.Size, opts ...HexGridOption) *Grid[T] {
return newHexagonGrid[T](size, hexSize, HexPointyTop, hex.OffsetOddR, opts)
}
// NewHexagonFlatTopGrid creates a new hexagon grid with a flat-top layout.
//
// hexSize is the circumradius of each hex cell — the distance from the center
// to any vertex, which equals the edge length for a regular hexagon.
// Use equal W and H for an undistorted hex; different values skew the cell
// independently along each axis (e.g. Sz(0.5, 1) produces a hex that is
// compressed horizontally). The pixel bounding box of each cell is
// hexSize.W*2 wide and hexSize.H*√3 tall.
//
// Grid cell (0, 0) is placed at the top-left: even columns are unshifted and
// odd columns are offset half a cell downward, so no cell center has a
// negative y coordinate.
func NewHexagonFlatTopGrid[T any](size ints.Size, hexSize floats.Size, opts ...HexGridOption) *Grid[T] {
return newHexagonGrid[T](size, hexSize, HexFlatTop, hex.OffsetOddQ, opts)
}
// HexFlatTopCellSize returns the circumradius for NewHexagonFlatTopGrid where
// each tile is exactly width pixels wide and width·√3/2 pixels tall (2:√3 ratio).
func HexFlatTopCellSize(width float64) floats.Size {
return geom.SzU(width).Scale(0.5)
}
// HexPointyTopCellSize returns the circumradius for NewHexagonPointyTopGrid where
// each tile is exactly width pixels wide and width·2/√3 pixels tall (√3:2 ratio).
func HexPointyTopCellSize(width float64) floats.Size {
return geom.SzU(width * 2 / geom.Sqrt3).Scale(0.5)
}
// HexFlatTopIsometricPixelPerfectCellSize returns the circumradius for
// NewHexagonFlatTopGrid where each tile is exactly width pixels wide and
// width·3/4 pixels tall (4:3 ratio), suitable for pixel-perfect isometric rendering.
func HexFlatTopIsometricPixelPerfectCellSize(width float64) floats.Size {
return geom.Sz(width, width*geom.Sqrt3/2).Scale(0.5)
}
// HexPointyTopIsometricPixelPerfectCellSize returns the circumradius for
// NewHexagonPointyTopGrid where each tile is exactly width pixels wide and
// width pixels tall (1:1 ratio), suitable for pixel-perfect isometric rendering.
func HexPointyTopIsometricPixelPerfectCellSize(width float64) floats.Size {
return geom.Sz(width*2/geom.Sqrt3, width).Scale(0.5)
}
func newHexagonGrid[T any](size ints.Size, hexSize floats.Size, transform *Transform, system hex.CoordinateSystem, opts []HexGridOption) *Grid[T] {
o := applyHexGridOptions(opts)
if o.even {
switch system {
case hex.OffsetOddR:
system = hex.OffsetEvenR
case hex.OffsetOddQ:
system = hex.OffsetEvenQ
default:
// noop
}
}
layout := NewLayout(transform,
LayoutOpts.Size(size),
LayoutOpts.CellSize(hexSize),
LayoutOpts.ToPointMapper(func(index ints.Point) floats.Point {
return system.From(index).Point().Float()
}),
LayoutOpts.FromPointMapper(func(pixel floats.Point) ints.Point {
return system.To(hex.FracPt(pixel.XY()).Round())
}),
).AlignTopLeft()
return NewGrid[T](
layout,
func(from, to ints.Point) int {
return system.From(from).DistanceTo(system.From(to))
},
func(index ints.Point, n int, valid ValidIndexFunc) []ints.Point {
h := system.From(index)
candidates := h.Range(n)
var blocking []hex.Hex
for _, i := range candidates {
if !valid(system.To(i)) {
blocking = append(blocking, i)
}
}
return slices.Map(h.FieldOfView(candidates, blocking), func(h hex.Hex) ints.Point {
return system.To(h)
})
},
func(index ints.Point) []ints.Vector {
offsets := system.Offsets(index)
return offsets[:]
},
)
}
// HexGridOption configures a hexagonal grid constructor.
type HexGridOption func(*hexGridOptions)
type hexGridOptions struct {
even bool
}
// HexGridOpts is the namespace for hexagonal grid options.
var HexGridOpts hexGridOptions
// EvenSystem returns an option that switches the offset row/column convention
// from odd-offset (default) to even-offset.
func (o hexGridOptions) EvenSystem() HexGridOption {
return func(o *hexGridOptions) {
o.even = true
}
}
func applyHexGridOptions(opts []HexGridOption) *hexGridOptions {
o := &hexGridOptions{}
for _, opt := range opts {
opt(o)
}
return o
}