package world import ( "fmt" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" ) type gridCell struct { Row int Col int } func newTestWorld(wReal, hReal int) *World { return NewWorld(wReal, hReal) } func countObjectInGrid(g *World, id uuid.UUID) int { count := 0 for row := range g.grid { for col := range g.grid[row] { for _, item := range g.grid[row][col] { if item.ID() == id { count++ } } } } return count } func hasObjectInCell(g *World, row, col int, id uuid.UUID) bool { for _, item := range g.grid[row][col] { if item.ID() == id { return true } } return false } func TestViewportPxToWorldFixed(t *testing.T) { tests := []struct { name string viewportWidthPx int viewportHeightPx int cameraZoom int wantWidth int wantHeight int }{ { name: "zoom 1.0", viewportWidthPx: 500, viewportHeightPx: 400, cameraZoom: SCALE, wantWidth: 500 * SCALE, wantHeight: 400 * SCALE, }, { name: "zoom 2.0", viewportWidthPx: 500, viewportHeightPx: 400, cameraZoom: 2 * SCALE, wantWidth: 250 * SCALE, wantHeight: 200 * SCALE, }, { name: "zoom below 1.0", viewportWidthPx: 550, viewportHeightPx: 550, cameraZoom: 917, wantWidth: 599781, wantHeight: 599781, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotW, gotH := viewportPxToWorldFixed(tt.viewportWidthPx, tt.viewportHeightPx, tt.cameraZoom) require.Equal(t, tt.wantWidth, gotW) require.Equal(t, tt.wantHeight, gotH) }) } } func TestSplitByWrap_ZeroOrNegativeSizeReturnsNil(t *testing.T) { tests := []struct { name string minX, maxX int minY, maxY int }{ { name: "zero width", minX: 100, maxX: 100, minY: 50, maxY: 100, }, { name: "zero height", minX: 100, maxX: 200, minY: 50, maxY: 50, }, { name: "negative width", minX: 200, maxX: 100, minY: 50, maxY: 100, }, { name: "negative height", minX: 100, maxX: 200, minY: 100, maxY: 50, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rects := splitByWrap(600, 400, tt.minX, tt.maxX, tt.minY, tt.maxY) require.Nil(t, rects) }) } } func TestSplitByWrap_XWrapUsesWorldWidth(t *testing.T) { rects := splitByWrap( 600, 400, 500, 650, 50, 100, ) require.Len(t, rects, 2) require.Equal(t, Rect{minX: 500, maxX: 600, minY: 50, maxY: 100}, rects[0]) require.Equal(t, Rect{minX: 0, maxX: 50, minY: 50, maxY: 100}, rects[1]) } func TestSplitByWrap_YWrapUsesWorldHeight(t *testing.T) { rects := splitByWrap( 600, 400, 50, 100, 350, 450, ) require.Len(t, rects, 2) require.Equal(t, Rect{minX: 50, maxX: 100, minY: 350, maxY: 400}, rects[0]) require.Equal(t, Rect{minX: 50, maxX: 100, minY: 0, maxY: 50}, rects[1]) } func TestSplitByWrap_XAndYWrap(t *testing.T) { rects := splitByWrap( 600, 400, 550, 650, 350, 450, ) require.Len(t, rects, 4) require.ElementsMatch(t, []Rect{ {minX: 550, maxX: 600, minY: 350, maxY: 400}, {minX: 550, maxX: 600, minY: 0, maxY: 50}, {minX: 0, maxX: 50, minY: 350, maxY: 400}, {minX: 0, maxX: 50, minY: 0, maxY: 50}, }, rects) } func TestSplitByWrap_NoWrapInsideWorld(t *testing.T) { rects := splitByWrap( 600, 400, 100, 200, 50, 100, ) require.Len(t, rects, 1) require.Equal(t, Rect{minX: 100, maxX: 200, minY: 50, maxY: 100}, rects[0]) } func TestSplitByWrap_FullWorldCoverageOnEqualWidth(t *testing.T) { rects := splitByWrap( 600, 400, 0, 600, 50, 100, ) require.Len(t, rects, 1) require.Equal(t, Rect{minX: 0, maxX: 600, minY: 50, maxY: 100}, rects[0]) } func TestSplitByWrap_FullWorldCoverageOnEqualHeight(t *testing.T) { rects := splitByWrap( 600, 400, 50, 100, 0, 400, ) require.Len(t, rects, 1) require.Equal(t, Rect{minX: 50, maxX: 100, minY: 0, maxY: 400}, rects[0]) } func TestSplitByWrap_FullWorldCoverageOnBothAxes(t *testing.T) { rects := splitByWrap( 600, 400, 0, 600, 0, 400, ) require.Len(t, rects, 1) require.Equal(t, Rect{minX: 0, maxX: 600, minY: 0, maxY: 400}, rects[0]) } func TestWorldToCell(t *testing.T) { tests := []struct { name string value int worldSize int cells int cellSize int want int }{ { name: "simple inside world", value: 150, worldSize: 600, cells: 6, cellSize: 100, want: 1, }, { name: "negative wraps to last cell", value: -1, worldSize: 600, cells: 6, cellSize: 100, want: 5, }, { name: "exact world size wraps to zero", value: 600, worldSize: 600, cells: 6, cellSize: 100, want: 0, }, { name: "large positive wraps correctly", value: 650, worldSize: 600, cells: 6, cellSize: 100, want: 0, }, { name: "last in-range value lands in last cell", value: 599, worldSize: 600, cells: 6, cellSize: 100, want: 5, }, {name: "first cell", value: 0, worldSize: 10000, cells: 5, cellSize: 2000, want: 0}, {name: "middle cell", value: 2500, worldSize: 10000, cells: 5, cellSize: 2000, want: 1}, {name: "last exact world point wraps to zero", value: 10000, worldSize: 10000, cells: 5, cellSize: 2000, want: 0}, {name: "negative wraps to last", value: -1, worldSize: 10000, cells: 5, cellSize: 2000, want: 4}, {name: "partial last cell is clamped", value: 9999, worldSize: 10000, cells: 4, cellSize: 3000, want: 3}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := worldToCell(tt.value, tt.worldSize, tt.cells, tt.cellSize) require.Equal(t, tt.want, got) }) } } func TestResetGrid_UsesWidthForColsAndHeightForRows(t *testing.T) { g := newTestWorld(600, 400) g.resetGrid(100 * SCALE) require.Equal(t, 6, g.cols) require.Equal(t, 4, g.rows) require.Len(t, g.grid, 4) require.Len(t, g.grid[0], 6) } func TestIndexPoint(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() p := Point{ Id: id, X: 150 * SCALE, Y: 250 * SCALE, } g.indexObject(p) require.True(t, hasObjectInCell(g, 2, 1, id)) require.Equal(t, 1, countObjectInGrid(g, id)) } func TestIndexPoint_WrapsNegativeCoordinates(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() p := Point{ Id: id, X: -1, Y: -1, } g.indexObject(p) require.True(t, hasObjectInCell(g, 5, 5, id)) require.Equal(t, 1, countObjectInGrid(g, id)) } func TestIndexCircle_WrapsAcrossLeftAndTopEdges(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() c := Circle{ Id: id, X: 50 * SCALE, Y: 50 * SCALE, Radius: 75 * SCALE, } g.indexObject(c) // The circle spans [-25..125] on both axes. // It must appear both near zero and near the wrapped end. require.True(t, hasObjectInCell(g, 0, 0, id)) require.True(t, hasObjectInCell(g, 0, 5, id)) require.True(t, hasObjectInCell(g, 5, 0, id)) require.True(t, hasObjectInCell(g, 5, 5, id)) // It also extends into the next cells near the origin. require.True(t, hasObjectInCell(g, 0, 1, id)) require.True(t, hasObjectInCell(g, 1, 0, id)) require.True(t, hasObjectInCell(g, 1, 1, id)) } func TestIndexCircle_NoWrap(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() c := Circle{ Id: id, X: 300 * SCALE, Y: 300 * SCALE, Radius: 50 * SCALE, } g.indexObject(c) require.True(t, hasObjectInCell(g, 2, 2, id)) require.True(t, hasObjectInCell(g, 2, 3, id)) require.True(t, hasObjectInCell(g, 3, 2, id)) require.True(t, hasObjectInCell(g, 3, 3, id)) } func TestIndexCircle_CoversWholeWorldWhenLargerThanWorld(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() c := Circle{ Id: id, X: 300 * SCALE, Y: 300 * SCALE, Radius: 400 * SCALE, } g.indexObject(c) for row := 0; row < g.rows; row++ { for col := 0; col < g.cols; col++ { require.Truef(t, hasObjectInCell(g, row, col, id), "missing object in row=%d col=%d", row, col) } } } func TestIndexLine_HorizontalWrap(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 590 * SCALE, Y1: 200 * SCALE, X2: 10 * SCALE, Y2: 200 * SCALE, } g.indexObject(l) // The shortest torus representation crosses the right/left border. require.True(t, hasObjectInCell(g, 2, 5, id)) require.True(t, hasObjectInCell(g, 2, 0, id)) } func TestIndexLine_VerticalWrap(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 200 * SCALE, Y1: 590 * SCALE, X2: 200 * SCALE, Y2: 10 * SCALE, } g.indexObject(l) require.True(t, hasObjectInCell(g, 5, 2, id)) require.True(t, hasObjectInCell(g, 0, 2, id)) } func TestIndexLine_DiagonalWrapBothAxes(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 590 * SCALE, Y1: 590 * SCALE, X2: 10 * SCALE, Y2: 10 * SCALE, } g.indexObject(l) require.True(t, hasObjectInCell(g, 5, 5, id)) require.True(t, hasObjectInCell(g, 0, 0, id)) } func TestIndexLine_HorizontalNoWrap_DegenerateBBoxStillIndexes(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 100 * SCALE, Y1: 200 * SCALE, X2: 300 * SCALE, Y2: 200 * SCALE, } g.indexObject(l) // The indexed interval is half-open: [100,300). // Therefore it occupies columns 1 and 2, but not column 3. require.True(t, hasObjectInCell(g, 2, 1, id)) require.True(t, hasObjectInCell(g, 2, 2, id)) require.False(t, hasObjectInCell(g, 2, 3, id)) } func TestIndexLine_VerticalNoWrap_DegenerateBBoxStillIndexes(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 200 * SCALE, Y1: 100 * SCALE, X2: 200 * SCALE, Y2: 300 * SCALE, } g.indexObject(l) // The indexed interval is half-open: [100,300). // Therefore it occupies rows 1 and 2, but not row 3. require.True(t, hasObjectInCell(g, 1, 2, id)) require.True(t, hasObjectInCell(g, 2, 2, id)) require.False(t, hasObjectInCell(g, 3, 2, id)) } func TestIndexLine_ZeroLengthIndexesSingleCell(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 250 * SCALE, Y1: 350 * SCALE, X2: 250 * SCALE, Y2: 350 * SCALE, } g.indexObject(l) require.True(t, hasObjectInCell(g, 3, 2, id)) require.Equal(t, 1, countObjectInGrid(g, id)) } func TestIndexLine_ExactlyOnCellBoundaryUsesHalfOpenInterval(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() l := Line{ Id: id, X1: 200 * SCALE, Y1: 100 * SCALE, X2: 400 * SCALE, Y2: 100 * SCALE, } g.indexObject(l) // The indexed interval is [200,400), so it must occupy columns 2 and 3 only. require.True(t, hasObjectInCell(g, 1, 2, id)) require.True(t, hasObjectInCell(g, 1, 3, id)) require.False(t, hasObjectInCell(g, 1, 4, id)) } func collectOccupiedCells(g *World, id uuid.UUID) []gridCell { var cells []gridCell for row := range g.grid { for col := range g.grid[row] { for _, item := range g.grid[row][col] { if item.ID() == id { cells = append(cells, gridCell{Row: row, Col: col}) break } } } } return cells } func allGridCells(rows, cols int) []gridCell { cells := make([]gridCell, 0, rows*cols) for row := 0; row < rows; row++ { for col := 0; col < cols; col++ { cells = append(cells, gridCell{Row: row, Col: col}) } } return cells } func requireIndexedExactlyInCells(t *testing.T, g *World, id uuid.UUID, want []gridCell) { t.Helper() got := collectOccupiedCells(g, id) require.ElementsMatchf( t, want, got, "unexpected indexed cells for object %s", id.String(), ) } func TestIndexObject_Point_TableDriven(t *testing.T) { tests := []struct { name string worldW int worldH int cellSize int item Point wantCells []gridCell }{ { name: "point inside world", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Point{ Id: uuid.New(), X: 150 * SCALE, Y: 250 * SCALE, }, wantCells: []gridCell{ {Row: 2, Col: 1}, }, }, { name: "point wraps from negative coordinates to last cell", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Point{ Id: uuid.New(), X: -1, Y: -1, }, wantCells: []gridCell{ {Row: 5, Col: 5}, }, }, { name: "point exactly at world boundary wraps to zero cell", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Point{ Id: uuid.New(), X: 600 * SCALE, Y: 600 * SCALE, }, wantCells: []gridCell{ {Row: 0, Col: 0}, }, }, { name: "point on cell boundary belongs to that cell", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Point{ Id: uuid.New(), X: 200 * SCALE, Y: 300 * SCALE, }, wantCells: []gridCell{ {Row: 3, Col: 2}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := newTestWorld(tt.worldW, tt.worldH) g.resetGrid(tt.cellSize) g.indexObject(tt.item) requireIndexedExactlyInCells(t, g, tt.item.Id, tt.wantCells) }) } } func TestIndexObject_Circle_TableDriven(t *testing.T) { tests := []struct { name string worldW int worldH int cellSize int item Circle wantCells []gridCell }{ { name: "circle without wrap", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Circle{ Id: uuid.New(), X: 300 * SCALE, Y: 300 * SCALE, Radius: 50 * SCALE, }, wantCells: []gridCell{ {Row: 2, Col: 2}, {Row: 2, Col: 3}, {Row: 3, Col: 2}, {Row: 3, Col: 3}, }, }, { name: "circle wraps across left and top edges", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Circle{ Id: uuid.New(), X: 50 * SCALE, Y: 50 * SCALE, Radius: 75 * SCALE, }, wantCells: []gridCell{ {Row: 5, Col: 5}, {Row: 5, Col: 0}, {Row: 5, Col: 1}, {Row: 0, Col: 5}, {Row: 0, Col: 0}, {Row: 0, Col: 1}, {Row: 1, Col: 5}, {Row: 1, Col: 0}, {Row: 1, Col: 1}, }, }, { name: "circle wraps across right edge only", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Circle{ Id: uuid.New(), X: 575 * SCALE, Y: 300 * SCALE, Radius: 50 * SCALE, }, wantCells: []gridCell{ {Row: 2, Col: 5}, {Row: 2, Col: 0}, {Row: 3, Col: 5}, {Row: 3, Col: 0}, }, }, { name: "circle wraps across bottom edge only", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Circle{ Id: uuid.New(), X: 300 * SCALE, Y: 575 * SCALE, Radius: 50 * SCALE, }, wantCells: []gridCell{ {Row: 5, Col: 2}, {Row: 5, Col: 3}, {Row: 0, Col: 2}, {Row: 0, Col: 3}, }, }, { name: "circle larger than world covers the whole grid", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Circle{ Id: uuid.New(), X: 300 * SCALE, Y: 300 * SCALE, Radius: 400 * SCALE, }, wantCells: allGridCells(6, 6), }, { name: "circle touching boundaries exactly uses half-open indexing", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Circle{ Id: uuid.New(), X: 300 * SCALE, Y: 300 * SCALE, Radius: 100 * SCALE, // bbox [200, 400) x [200, 400) }, wantCells: []gridCell{ {Row: 2, Col: 2}, {Row: 2, Col: 3}, {Row: 3, Col: 2}, {Row: 3, Col: 3}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := newTestWorld(tt.worldW, tt.worldH) g.resetGrid(tt.cellSize) g.indexObject(tt.item) requireIndexedExactlyInCells(t, g, tt.item.Id, tt.wantCells) }) } } func TestIndexObject_Line_TableDriven(t *testing.T) { tests := []struct { name string worldW int worldH int cellSize int item Line wantCells []gridCell }{ { name: "horizontal line without wrap", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 100 * SCALE, Y1: 200 * SCALE, X2: 300 * SCALE, Y2: 200 * SCALE, }, // Half-open interval [100,300), so only cols 1 and 2. wantCells: []gridCell{ {Row: 2, Col: 1}, {Row: 2, Col: 2}, }, }, { name: "vertical line without wrap", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 200 * SCALE, Y1: 100 * SCALE, X2: 200 * SCALE, Y2: 300 * SCALE, }, // Half-open interval [100,300), so only rows 1 and 2. wantCells: []gridCell{ {Row: 1, Col: 2}, {Row: 2, Col: 2}, }, }, { name: "horizontal line wraps across left right border", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 590 * SCALE, Y1: 200 * SCALE, X2: 10 * SCALE, Y2: 200 * SCALE, }, wantCells: []gridCell{ {Row: 2, Col: 5}, {Row: 2, Col: 0}, }, }, { name: "vertical line wraps across top bottom border", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 200 * SCALE, Y1: 590 * SCALE, X2: 200 * SCALE, Y2: 10 * SCALE, }, wantCells: []gridCell{ {Row: 5, Col: 2}, {Row: 0, Col: 2}, }, }, { name: "diagonal line wraps across both axes", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 590 * SCALE, Y1: 590 * SCALE, X2: 10 * SCALE, Y2: 10 * SCALE, }, wantCells: []gridCell{ {Row: 5, Col: 5}, {Row: 5, Col: 0}, {Row: 0, Col: 5}, {Row: 0, Col: 0}, }, }, { name: "zero length line indexes a single cell", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 250 * SCALE, Y1: 350 * SCALE, X2: 250 * SCALE, Y2: 350 * SCALE, }, wantCells: []gridCell{ {Row: 3, Col: 2}, }, }, { name: "line exactly on cell boundaries follows half-open interval", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 200 * SCALE, Y1: 100 * SCALE, X2: 400 * SCALE, Y2: 100 * SCALE, }, // [200,400) => cols 2 and 3 only. wantCells: []gridCell{ {Row: 1, Col: 2}, {Row: 1, Col: 3}, }, }, { name: "diagonal line without wrap indexes its full bbox footprint", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 100 * SCALE, Y1: 100 * SCALE, X2: 300 * SCALE, Y2: 300 * SCALE, }, // Indexing is bbox-based, not raster-based. // The bbox is [100,300) x [100,300), so four cells. wantCells: []gridCell{ {Row: 1, Col: 1}, {Row: 1, Col: 2}, {Row: 2, Col: 1}, {Row: 2, Col: 2}, }, }, { name: "horizontal wrap exactly on borders still indexes both edge cells", worldW: 600, worldH: 600, cellSize: 100 * SCALE, item: Line{ Id: uuid.New(), X1: 600 * SCALE, Y1: 100 * SCALE, X2: 0, Y2: 100 * SCALE, }, // After wrapping both endpoints are equivalent to zero-width on the edge. // The degenerate bbox expansion should still index the first cell only. wantCells: []gridCell{ {Row: 1, Col: 0}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := newTestWorld(tt.worldW, tt.worldH) g.resetGrid(tt.cellSize) g.indexObject(tt.item) requireIndexedExactlyInCells(t, g, tt.item.Id, tt.wantCells) }) } } func TestIndexOnViewportChange_RebuildsGridAndIndexesObjects(t *testing.T) { g := newTestWorld(600, 400) pID := uuid.New() cID := uuid.New() lID := uuid.New() g.objects[pID] = Point{ Id: pID, X: 50 * SCALE, Y: 50 * SCALE, } g.objects[cID] = Circle{ Id: cID, X: 300 * SCALE, Y: 200 * SCALE, Radius: 50 * SCALE, } g.objects[lID] = Line{ Id: lID, X1: 590 * SCALE, Y1: 100 * SCALE, X2: 10 * SCALE, Y2: 100 * SCALE, } g.IndexOnViewportChange(500, 300, 1.) require.Greater(t, g.cellSize, 0) require.Equal(t, ceilDiv(g.W, g.cellSize), g.cols) require.Equal(t, ceilDiv(g.H, g.cellSize), g.rows) require.Greaterf(t, countObjectInGrid(g, pID), 0, "point %s was not indexed", pID) require.Greaterf(t, countObjectInGrid(g, cID), 0, "circle %s was not indexed", cID) require.Greaterf(t, countObjectInGrid(g, lID), 0, "line %s was not indexed", lID) } func TestIndexOnViewportChange_RebuildsGridShapeForNonSquareWorld(t *testing.T) { g := newTestWorld(600, 400) g.IndexOnViewportChange(500, 300, 1.) require.Equal(t, ceilDiv(g.W, g.cellSize), g.cols) require.Equal(t, ceilDiv(g.H, g.cellSize), g.rows) require.Len(t, g.grid, g.rows) require.Len(t, g.grid[0], g.cols) } func TestIndexOnViewportChange_ReindexesAfterCellSizeChange(t *testing.T) { g := newTestWorld(600, 600) id := uuid.New() g.objects[id] = Circle{ Id: id, X: 300 * SCALE, Y: 300 * SCALE, Radius: 50 * SCALE, } g.IndexOnViewportChange(500, 500, 1.) firstCellSize := g.cellSize firstCount := countObjectInGrid(g, id) g.IndexOnViewportChange(200, 200, 1.) secondCellSize := g.cellSize secondCount := countObjectInGrid(g, id) require.NotEqual(t, firstCellSize, secondCellSize) require.Greater(t, firstCount, 0) require.Greater(t, secondCount, 0) if firstCellSize != secondCellSize && firstCount == secondCount { t.Logf( "cell size changed from %d to %d, but the indexed cell count happened to stay equal (%d)", firstCellSize, secondCellSize, firstCount, ) } } func TestPrimitiveIndexing_ErrorMessagesStayReadable(t *testing.T) { g := newTestWorld(600, 600) g.resetGrid(100 * SCALE) id := uuid.New() p := Point{ Id: id, X: 100 * SCALE, Y: 100 * SCALE, } g.indexObject(p) got := collectOccupiedCells(g, id) require.NotEmpty(t, got, fmt.Sprintf("object %s should occupy at least one cell", id.String())) }