ui: basic map scroller
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
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[uuid.UUID]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
|
||||
}
|
||||
Reference in New Issue
Block a user