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 }