refactor(grid): GridData is a map, not a widget -- delete UIGrid's drawable half
closes #361 closes #370 closes #371 UIGrid inherited both UIDrawable and GridData, so every map carried a second camera and RenderTexture that nothing on screen corresponded to. Appending a _GridData to a scene drew the whole map again through that ghost, frozen at construction values. GridData is now pure data -- no position, no size, no render() -- and UIGridView is the only widget. UIGrid is deleted rather than demoted: shedding UIDrawable left an empty subclass whose only job was to be what an aliasing shared_ptr pointed into. GridData absorbs the texture (it defines the cell size, which every tile<->pixel conversion depends on) and its own PythonObjectCache serial; the binding layer becomes struct PyGridData. PyObjectsEnum::UIGRID and its 14 case arms go with it. * mcrfpy.GridData is public and standalone-constructible. A map with no view at all -- an offscreen level, still steppable and pathable -- was previously undefined behavior: GridData::markDirty did static_cast<UIGrid*>(this). * UIGridView::init constructs the GridData directly instead of building a throwaway Python _GridData, stealing its base subobject, and copying ~15 fields of rendering state out of the discarded wrapper. One init() serves both modes, so a second view is no longer a crippled kwarg subset (Grid(grid=other, z_index=5) used to raise TypeError). * entity.grid returns the GridData. Accepted break: an entity may be on a map with no view or with several, so "the grid an entity is in" is not a camera. Migrate player.grid.center_camera(p) -> view.center_camera(p). * tp_name is mcrfpy.GridView; mcrfpy.Grid is an alias to the same type object. Two latent bugs surfaced and are fixed here: #370 Grid.center_camera() and Grid.size = ... were SILENT NO-OPS -- neither was defined on the view, so both delegated to the ghost camera. On master, center_camera((5,5)) leaves center_x at 50.0. #371 The shader uniform binder strcmp'd tp_name == "mcrfpy.Grid" (the VIEW's name) then cast to PyUIGridObject. UB that only worked because both wrappers had identical layout and both classes put UIDrawable at offset 0. Suite 327/327. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3e18c1fbf6
commit
c696b4ef41
51 changed files with 1520 additions and 2633 deletions
|
|
@ -30,12 +30,13 @@ cameras over one dataset):
|
|||
dead property is gone, not silently still None.
|
||||
|
||||
Design note carried over from the fix: GridData::views is a *list*, not a
|
||||
single pointer, specifically because N views can share one GridData. Index 0
|
||||
is guaranteed (by construction: a fresh GridData is only ever produced by
|
||||
Grid()'s factory path, `init_with_data`; `init_explicit_view` can only ATTACH
|
||||
to an already-existing GridData) to be the view that created the data, so
|
||||
UIEntity.grid identity (test 2) stays deterministic even with secondary
|
||||
views registered -- not an arbitrary pick from an unordered set.
|
||||
single pointer, specifically because N views can share one GridData.
|
||||
|
||||
#361 update: entity.grid no longer returns a view at all -- it returns the
|
||||
GridData. The "which view is primary?" question that views.front() answered for
|
||||
identity purposes is gone, because the answer was always arbitrary dressed up as
|
||||
deterministic. The registry itself remains, and is still load-bearing for
|
||||
dirty-notification fan-out (test 3).
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
|
|
@ -95,18 +96,20 @@ def test_1_two_views_render_shared_mutations():
|
|||
|
||||
|
||||
def test_2_entity_grid_identity():
|
||||
"""entity.grid returns the SAME object as the Grid() that actually
|
||||
created the data (GridData::views.front()), not the secondary view, not
|
||||
None, and not a fresh throwaway wrapper -- even once a secondary view is
|
||||
registered on the same GridData."""
|
||||
"""entity.grid is the shared GridData -- one stable object, the same one both
|
||||
views are looking at, and NOT either view (#361)."""
|
||||
print("(2) entity.grid identity with a secondary view registered...")
|
||||
grid_a = mcrfpy.Grid(grid_size=(5, 5))
|
||||
grid_b = mcrfpy.Grid(grid=grid_a, pos=(0, 0), size=(80, 80)) # noqa: F841 (keep alive)
|
||||
grid_b = mcrfpy.Grid(grid=grid_a, pos=(0, 0), size=(80, 80))
|
||||
|
||||
e = mcrfpy.Entity((1, 1), grid=grid_a)
|
||||
|
||||
assert e.grid is grid_a, f"entity.grid identity broken: {e.grid!r} is not grid_a ({grid_a!r})"
|
||||
assert e.grid is not grid_b, "entity.grid returned the secondary view instead of the creator"
|
||||
data = grid_a.grid_data
|
||||
assert grid_b.grid_data is data, "the two views do not share one GridData"
|
||||
assert e.grid is data, f"entity.grid identity broken: {e.grid!r} is not {data!r}"
|
||||
assert e.grid is e.grid, "entity.grid is not stable across reads"
|
||||
assert e.grid is not grid_a and e.grid is not grid_b, \
|
||||
"entity.grid returned a view; it must return the map (#361)"
|
||||
print(" OK")
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue