Fix Issue #63: Implement z-order rendering with dirty flag optimization

- Add dirty flags to PyScene and UIFrame to track when sorting is needed
- Implement lazy sorting - only sort when z_index changes or elements are added/removed
- Make Frame children respect z_index (previously rendered in insertion order only)
- Update UIDrawable::set_int to notify when z_index changes
- Mark collections dirty on append, remove, setitem, and slice operations
- Remove per-frame vector copy in PyScene::render for better performance

Performance improvement: Static scenes now use O(1) check instead of O(n log n) sort every frame

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John McCardle 2025-07-05 10:34:06 -04:00
commit 90c318104b
11 changed files with 154 additions and 83 deletions

View file

@ -33,34 +33,34 @@ def test_Entity():
# Test entity properties
try:
print(f" Entity1 pos: {entity1.pos}")
print(f" Entity1 draw_pos: {entity1.draw_pos}")
print(f" Entity1 sprite_number: {entity1.sprite_number}")
print(f" Entity1 pos: {entity1.pos}")
print(f" Entity1 draw_pos: {entity1.draw_pos}")
print(f" Entity1 sprite_number: {entity1.sprite_number}")
# Modify properties
entity1.pos = mcrfpy.Vector(3, 3)
entity1.sprite_number = 5
print(" Entity properties modified")
print(" Entity properties modified")
except Exception as e:
print(f" Entity property access failed: {e}")
print(f"X Entity property access failed: {e}")
# Test gridstate access
try:
gridstate = entity2.gridstate
print(f" Entity gridstate accessible")
print(" Entity gridstate accessible")
# Test at() method
point_state = entity2.at(0, 0)
print(f" Entity at() method works")
point_state = entity2.at()#.at(0, 0)
print(" Entity at() method works")
except Exception as e:
print(f" Entity gridstate/at() failed: {e}")
print(f"X Entity gridstate/at() failed: {e}")
# Test index() method (Issue #73)
print("\nTesting index() method (Issue #73)...")
try:
# Try to find entity2's index
index = entity2.index()
print(f" index() method works: entity2 is at index {index}")
print(f":) index() method works: entity2 is at index {index}")
# Verify by checking collection
if entities[index] == entity2:
@ -70,7 +70,7 @@ def test_Entity():
# Remove using index
entities.remove(index)
print(f" Removed entity using index, now {len(entities)} entities")
print(f":) Removed entity using index, now {len(entities)} entities")
except AttributeError:
print("✗ index() method not implemented (Issue #73)")
# Try manual removal as workaround
@ -78,21 +78,21 @@ def test_Entity():
for i in range(len(entities)):
if entities[i] == entity2:
entities.remove(i)
print(f" Manual removal workaround succeeded")
print(":) Manual removal workaround succeeded")
break
except:
print("✗ Manual removal also failed")
except Exception as e:
print(f" index() method error: {e}")
print(f":) index() method error: {e}")
# Test EntityCollection iteration
try:
positions = []
for entity in entities:
positions.append(entity.pos)
print(f" Entity iteration works: {len(positions)} entities")
print(f":) Entity iteration works: {len(positions)} entities")
except Exception as e:
print(f" Entity iteration failed: {e}")
print(f"X Entity iteration failed: {e}")
# Test EntityCollection extend (Issue #27)
try:
@ -101,11 +101,11 @@ def test_Entity():
mcrfpy.Entity(mcrfpy.Vector(9, 9), mcrfpy.default_texture, 4, grid)
]
entities.extend(new_entities)
print(f" extend() method works: now {len(entities)} entities")
print(f":) extend() method works: now {len(entities)} entities")
except AttributeError:
print("✗ extend() method not implemented (Issue #27)")
except Exception as e:
print(f" extend() method error: {e}")
print(f"X extend() method error: {e}")
# Skip screenshot in headless mode
print("PASS")
@ -113,4 +113,4 @@ def test_Entity():
# Run test immediately in headless mode
print("Running test immediately...")
test_Entity()
print("Test completed.")
print("Test completed.")