plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


23627d9 — Peter John 2026-11-02T18:18:04+05:30
docs(plans): scene z-index, sortChildren and culling review plan
docs/plans/2026-11-02-scene-zindex-culling.review.md ADDED
@@ -0,0 +1,69 @@
1
+ # std/Scene: Scene2D z-index, child sorting and culling
2
+
3
+ ## Summary
4
+ `std/Scene` already draws children in list order, as libGDX Scene2D does. This adds Scene2D's API for changing that order:
5
+ - z-index as the child's position in its parent's list;
6
+ - `toFront`/`toBack`;
7
+ - an in-place, stable `sortChildren`, for y-sorting a world layer every frame;
8
+ - Scene2D-style culling: a group with a culling area skips children whose bounds lie outside it.
9
+
10
+ These are the pieces the KaM port needs to draw a large y-sorted map through the scene graph. Existing scenes don't change.
11
+
12
+ ## What changes for Plum code
13
+ ```plum
14
+ n.zIndex() # index among its siblings, -1 without a parent
15
+ n.setZIndex(0) # move within the parent's child list (clamped to 0..length-1)
16
+ n.toFront() # last child: drawn on top, hit first
17
+ n.toBack() # first child: drawn first
18
+
19
+ world.sortChildren(|a, b| a.y < b.y) # stable, in place, no allocation; cheap on nearly-sorted lists
20
+
21
+ world.setCullingArea(cam.x, cam.y, viewW, viewH) # in world's own coordinates
22
+ world.clearCullingArea()
23
+ ```
24
+ - These follow Scene2D's semantics and names (`getZIndex`/`setZIndex`/`toFront`/`toBack`, `Group.setCullingArea`), with Plum's naming conventions.
25
+ - **Z-index is the list position:** there is no separate z field. Draw order is list order, and hit-testing goes in reverse list order, as it does today.
26
+ - **`sortChildren(before)`:**
27
+ - it reorders the existing child list in place;
28
+ - it is stable (equal keys keep their order, so ties don't flicker);
29
+ - it is O(n) when the list is nearly sorted (the per-frame y-sort case) and O(n log n) in the worst case. A natural merge sort or binary-insertion runs with merging, using a reusable scratch buffer kept on the node, so steady-state frames allocate nothing.
30
+ - **Culling, as in Scene2D:**
31
+ - the culling area is a rectangle in the group's own coordinates;
32
+ - a child is skipped from drawing and from hit-testing when its own box (x, y, width, height, scaled, rotation ignored) doesn't overlap the area;
33
+ - culling applies one level down (the group's direct children);
34
+ - a culled child's whole subtree is skipped.
35
+
36
+ ## How it works
37
+ - `Node` gains `cull: Bool, cullX/cullY/cullW/cullH: Float` and a private scratch `List[Node]` for sorting.
38
+ - `setZIndex`/`toFront`/`toBack` use `parent` (already on `Node`): remove the node from the parent's children and insert it at the new index.
39
+ - `drawNode`, `collect` (flatten) and `hitNode` check the parent's culling area before descending into a child.
40
+
41
+ ## Work, in order
42
+ 1. Z-index API + `sortChildren`, plus pure `test` blocks:
43
+ - order after setZIndex, toFront and toBack;
44
+ - a stable sort with ties;
45
+ - y-sort of 5,000 random nodes is correct;
46
+ - a nearly-sorted re-sort takes ≈ n comparisons, counted through the comparator;
47
+ - no allocation in steady state, checked by reusing the node's scratch buffer identity.
48
+ 2. Culling, plus `test` blocks:
49
+ - flatten skips children outside the area;
50
+ - hit doesn't return culled children;
51
+ - a scaled child near the edge is kept;
52
+ - clearing the area restores them.
53
+ 3. `plum-examples/scene.plum`: a y-sorted layer of moving sprites using `sortChildren` and `setCullingArea` from the camera view, plus `toFront` on the clicked node.
54
+ 4. Benchmark: `plum-game/web-test/fixtures/stress_scene.plum` variant with a 20,000-node world, of which ~2,000 are on screen, using a per-frame y-sort and culling. Record frame time with and without culling in `BENCHMARK.md`.
55
+ 5. README `std/Scene` section: z-index, sortChildren, culling, and the KaM-style layer recipe (terrain / world y-sorted / effects / ui).
56
+
57
+ ## How we will know it works
58
+ - The `std/Scene` tests cover every rule above. `plum test plum-std plum-examples plum-game/web-test/fixtures` passes, along with `cargo test --workspace` and the plum-game and web-test suites.
59
+ - The browser check passes for scene.plum and the new fixture; the screenshots look right.
60
+ - In the benchmark, culling makes frame time scale with what's on screen, not with total nodes; with culling on, the 20k-node fixture stays close to the 5k-node fixture.
61
+
62
+ ## Decisions (settled)
63
+ - Scene2D semantics and names (user ruling).
64
+ - Z-index = list position, not a stored number.
65
+ - The culling area is in the group's own coordinates and ignores rotation, as in Scene2D.
66
+
67
+ ## Not in scope
68
+ - Scene2D actions (sequences, tweens), layout widgets/tables, and event listeners/bubbling.
69
+ - Spatial indexing (a `Grid` in game code stays the tool for gameplay queries).