79 lines
2.2 KiB
Go
79 lines
2.2 KiB
Go
package world
|
|
|
|
import (
|
|
"errors"
|
|
)
|
|
|
|
var (
|
|
errGridNotBuilt = errors.New("render: grid not built; call IndexOnViewportChange first")
|
|
)
|
|
|
|
// TileCandidates binds one torus tile to the list of unique grid candidates
|
|
// that intersect the tile rectangle.
|
|
//
|
|
// Items are not guaranteed to be truly visible; the grid is a coarse spatial index.
|
|
// Exact visibility tests are performed later in the renderer pipeline.
|
|
type TileCandidates struct {
|
|
Tile WorldTile
|
|
Items []MapItem
|
|
}
|
|
|
|
// collectCandidatesForTiles queries the world grid for each tile rectangle
|
|
// and returns per-tile unique candidate lists.
|
|
//
|
|
// Deduplication is performed per tile (by MapItem.ID()) to avoid duplicates caused by
|
|
// bbox indexing into multiple cells. Dedup across tiles is intentionally NOT performed.
|
|
func (w *World) collectCandidatesForTiles(tiles []WorldTile) ([]TileCandidates, error) {
|
|
if w.grid == nil || w.rows <= 0 || w.cols <= 0 || w.cellSize <= 0 {
|
|
return nil, errGridNotBuilt
|
|
}
|
|
|
|
out := make([]TileCandidates, 0, len(tiles))
|
|
for _, tile := range tiles {
|
|
items := w.collectCandidatesForTile(tile.Rect)
|
|
out = append(out, TileCandidates{
|
|
Tile: tile,
|
|
Items: items,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// collectCandidatesForTile returns a unique set of grid candidates for a single
|
|
// canonical-world tile rectangle [0..W) x [0..H).
|
|
//
|
|
// The rectangle must be half-open and expressed in fixed-point world coordinates.
|
|
func (w *World) collectCandidatesForTile(r Rect) []MapItem {
|
|
// Empty rect => no candidates.
|
|
if r.maxX <= r.minX || r.maxY <= r.minY {
|
|
return nil
|
|
}
|
|
|
|
// Map rect to cell ranges using the same half-open conventions as indexing:
|
|
// the last included cell is computed from (max-1).
|
|
colStart := w.worldToCellX(r.minX)
|
|
colEnd := w.worldToCellX(r.maxX - 1)
|
|
|
|
rowStart := w.worldToCellY(r.minY)
|
|
rowEnd := w.worldToCellY(r.maxY - 1)
|
|
|
|
seen := make(map[PrimitiveID]struct{})
|
|
result := make([]MapItem, 0)
|
|
|
|
for row := rowStart; row <= rowEnd; row++ {
|
|
for col := colStart; col <= colEnd; col++ {
|
|
cell := w.grid[row][col]
|
|
for _, item := range cell {
|
|
id := item.ID()
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
seen[id] = struct{}{}
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|