refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332

Cell logic storage was an array-of-UIGridPoint: 24 bytes/cell for 2 bytes of
real payload (walkable/transparent bools), the other 22 being redundant grid_x/
grid_y and a parent_grid back-pointer. Grids larger than 64 in either dimension
switched to non-contiguous 64x64 chunk storage, so no whole-grid contiguous
view was possible at any size.

Replaced with two dense row-major std::vector<uint8_t> planes on GridData
(walkable_plane, transparent_plane), indexed y*grid_w + x, with inline
is/setWalkable / is/setTransparent accessors. Chunking is dropped for LOGIC data
entirely (render caches still chunk independently in GridLayers) -- so cells are
contiguous at any size. Result: 12x smaller logic layer (2 vs 24 B/cell) and
cache-friendly iteration; a 1024x1024 grid's logic data is 2 MB instead of 24 MB.

The Python GridPoint wrapper already held (grid, x, y) and re-resolved per
access, so it just calls the new accessors -- low-invasiveness. GridData::at()
-> UIGridPoint& is removed; its callers convert to accessors:
- UIGridPoint get/set_bool_member + repr (the wrapper)
- UIGridPyMethods apply-threshold / apply-ranges heightmap writers
- syncTCODMap / syncTCODMapCell (read planes)
- EntityBehavior::isCellWalkable and PathProvider::cellWalkable (pathfinding --
  both were missed by the first scout; found via exhaustive grep)
- UITestScene

UIGridPoint's per-cell data members + ctor removed (class now only namespaces
the Python type's accessors). GridChunk.h/.cpp and ChunkManager deleted (dead
after logic-chunking removal; nothing else referenced them). No walkable/
transparent serialization existed, so no format impact.

Unblocks #333 (TCOD map dedup) and a future contiguous numpy view of
walkable/transparent.

Regression test issue_332_soa_storage_test.py: round-trips walkable/transparent
on a 200x160 grid at and across the old 64-cell chunk boundary, default values,
toggle-off, subscript vs at() agreement, grid_pos/entities, and a per-cell
footprint guard. Suite 311/311 (FOV + pathfinding + heightmap unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-07-11 01:43:30 -04:00
commit 2ceb18e673
13 changed files with 138 additions and 395 deletions

View file

@ -26,16 +26,14 @@ typedef struct {
int x, y; // Grid coordinates - compute data pointer on access
} PyUIGridPointObject;
// UIGridPoint - grid cell data for pathfinding and layer access
// #150 - Layer-related properties (color, tilesprite, etc.) removed; now handled by layers
// UIGridPoint - namespaces the Python GridPoint type's accessors.
// #150 - Layer-related properties (color, tilesprite, etc.) removed; now handled by layers.
// #332 - per-cell data (walkable/transparent) moved to GridData's dense uint8
// planes (SoA); this class no longer stores cell state. The Python wrapper
// (PyUIGridPointObject) holds (grid, x, y) and reads/writes via GridData accessors.
class UIGridPoint
{
public:
bool walkable, transparent; // Pathfinding/FOV properties
int grid_x, grid_y; // Position in parent grid
GridData* parent_grid; // Parent grid reference for TCOD sync (#252)
UIGridPoint();
// Built-in property accessors (walkable, transparent only)
static PyGetSetDef getsetters[];
static int set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure);