Files
aoi/grid.go
2024-03-09 10:29:06 +08:00

145 lines
3.7 KiB
Go

package aoi
import "sync"
var (
// Define direction vectors for the eight directions by populating dx and dy arrays.
dx = []int{-1, -1, -1, 0, 0, 1, 1, 1}
dy = []int{-1, 0, 1, -1, 1, -1, 0, 1}
)
// Grid represents a grid with a unique identifier and entities within it.
type Grid struct {
GID int // Grid ID
Entities sync.Map // Entities within the current grid
}
// GridManager implements AOI (Area of Interest) using a rectangular grid.
type GridManager struct {
StartX int // X-coordinate of the left boundary of the AOI
StartY int // Y-coordinate of the upper boundary of the AOI
AreaWidth int // Width of each grid (assuming square grids)
GridCount int // Number of grids in each row/column
grids map[int]*Grid
pool sync.Pool
}
// NewGrid creates a new grid with the specified ID.
func NewGrid(gid int) *Grid {
return &Grid{
GID: gid,
}
}
// NewGridManager initializes a new GridManager with the specified parameters.
func NewGridManager(startX, startY, areaWidth, gridCount int) AOI {
manager := &GridManager{
StartX: startX,
StartY: startY,
AreaWidth: areaWidth,
GridCount: gridCount,
grids: make(map[int]*Grid),
}
manager.pool.New = func() interface{} {
return make([]*Grid, 0, 9)
}
// Initialize grids with unique IDs
for y := 0; y < gridCount; y++ {
for x := 0; x < gridCount; x++ {
// Grid ID calculation: ID = IDy * nx + IDx (using grid coordinates to obtain grid ID)
gID := y*gridCount + x
manager.grids[gID] = NewGrid(gID)
}
}
return manager
}
// gridWidth calculates the width of each grid.
func (g *GridManager) gridWidth() int {
return g.AreaWidth / g.GridCount
}
// getGIDByPos calculates the grid ID based on the given coordinates.
func (g *GridManager) getGIDByPos(x, y float64) int {
gx := (int(x) - g.StartX) / g.gridWidth()
gy := (int(y) - g.StartY) / g.gridWidth()
return gy*g.GridCount + gx
}
// getSurroundGrids retrieves information about the surrounding nine grids based on the given grid ID.
func (g *GridManager) getSurroundGrids(gID int) []*Grid {
grids := g.pool.Get().([]*Grid)
defer func() {
grids = grids[:0]
g.pool.Put(grids)
}()
if _, ok := g.grids[gID]; !ok {
return grids
}
grids = append(grids, g.grids[gID])
// Calculate the coordinates of the grid based on the grid ID
x, y := gID%g.GridCount, gID/g.GridCount
// Add information about the eight neighboring grids
for i := 0; i < 8; i++ {
newX := x + dx[i]
newY := y + dy[i]
if newX >= 0 && newX < g.GridCount && newY >= 0 && newY < g.GridCount {
grids = append(grids, g.grids[newY*g.GridCount+newX])
}
}
return grids
}
// Add adds an entity to the appropriate grid based on its coordinates.
func (g *GridManager) Add(x, y float64, key string) {
entity := entityPool.Get().(*Entity)
entity.X = x
entity.Y = y
entity.Key = key
ID := g.getGIDByPos(x, y)
grid := g.grids[ID]
grid.Entities.Store(key, entity)
}
// Delete removes an entity from the grid based on its coordinates.
func (g *GridManager) Delete(x, y float64, key string) {
ID := g.getGIDByPos(x, y)
grid := g.grids[ID]
if entity, ok := grid.Entities.Load(key); ok {
grid.Entities.Delete(key)
entityPool.Put(entity)
}
}
// Search retrieves a list of entity keys within the specified coordinates' range.
func (g *GridManager) Search(x, y float64) []string {
result := resultPool.Get().([]string)
defer func() {
result = result[:0]
resultPool.Put(result)
}()
ID := g.getGIDByPos(x, y)
grids := g.getSurroundGrids(ID)
// Collect entity keys from the surrounding grids
for _, grid := range grids {
grid.Entities.Range(func(_, value interface{}) bool {
result = append(result, value.(*Entity).Key)
return true
})
}
return result
}