Skip to content

feat(tilemap): ImageLayerNode — render Tiled image layers with parallax, tint, and infinite repeat#384

Merged
Exoridus merged 5 commits into
mainfrom
worktree-infinite-maps-slice2
Jul 16, 2026
Merged

feat(tilemap): ImageLayerNode — render Tiled image layers with parallax, tint, and infinite repeat#384
Exoridus merged 5 commits into
mainfrom
worktree-infinite-maps-slice2

Conversation

@Exoridus

Copy link
Copy Markdown
Owner

Summary

Wave-5 roadmap item. ImageLayer (data class) finally gets a render node: ImageLayerNode extends Container wrapping one core RepeatingSprite, with collect-time parallax (TileLayerNode's exact pattern) and per-axis infinite repeat (period-aligned coverage of the view, recomputed only when view bounds or the patched origin change). TileMapView constructs/owns canonical nodes (imageLayerNodes, getImageLayerNodeById/ByName, destroy + pixel-snap cascade).

Design decisions (made autonomously — owner review welcome)

  • D1 Container wrapping one RepeatingSprite — the existing primitive does all GPU work; no new renderer.
  • D2 Parallax + repeat resolved at collect time; repeat OR parallax ⇒ cullable = false (position is camera-dependent, static bounds can't be culled against).
  • D3 Image layers are not band-selectable: toTileMap() walks Tiled document order once but stores tile/image layers separately, discarding combined position — and Tiled layer.id is creation-order, NOT stacking-order, so id-ordering is no safe proxy. Correct fix (combined document index recorded during the existing walk) is a small TileMap schema addition, deferred. Apps parent image nodes explicitly.
  • D4 visible/opacity/tintColor applied once at construction — ImageLayer is immutable.
  • D5 texture: null ⇒ empty node, documented.
  • D6 TileMapNode unchanged (same document-order reason); JSDoc pointer added.

Review findings addressed

  • Final review caught a real pop-out: parallax non-repeat layers were culled at their un-patched base offset (cull runs before the collect-time patch). Guard extended to parallax axes (TDD). Known symmetric behavior exists in TileLayerNode (bounded parallax tile layers cull at base bounds) — deliberately not touched here; flag if you want a follow-up.
  • Retained-tier note added to guide + JSDoc: repeat/parallax image nodes re-size their sprite at period crossings, content-dirtying an enclosing retained group — same "own RetainedContainer or none" rule as streamed layers.
  • Deferred (documented): refreshLayers()/removeImageLayer don't reconcile image nodes; combined document order for band membership.

Verification

TDD throughout (17+2+1 new node tests incl. hand-derived coverage math, 8 view tests); tilemap suite 467/467; test:core green; docs regenerated + docs:api:check clean; repeat math independently re-derived in two reviews (no off-by-one, world-stable under panning, pixel-snap cannot fight period alignment).

Exoridus added 5 commits July 16, 2026 22:03
…okup, destroy, snap cascade)

TileMapView now constructs one canonical ImageLayerNode per TileMap image
layer (map document order), exposed via imageLayerNodes, getImageLayerNodeById,
and getImageLayerNodeByName. The view owns these nodes: destroy() destroys and
detaches them, and pixelSnapMode cascades to them, mirroring the tile-layer
node conventions. Image layers remain outside band selectors (documented
rationale: no combined document order with tile layers to interleave against).
Adds the public export next to TileLayerNode, a TileMapNode JSDoc pointer to
TileMapView/ImageLayerNode for image-layer rendering, and three review
follow-ups: ImageLayerNode JSDoc on repeat+parallax bounds/hit-test staleness,
TileMapView.refreshLayers JSDoc noting image-layer nodes aren't reconciled,
and two new ImageLayerNode tests (combined repeatX+repeatY coverage, and the
per-frame coverage cache skip).
…ling

The cull test runs before the collect-time parallax patch, so a parallax layer was culled at its base offset while rendering elsewhere — a parallax background could pop out while still on-screen. Same camera-dependent-position rationale as the existing repeat-axis guard. Also documents the retained-tier implication for repeat/parallax image layers.
@Exoridus
Exoridus enabled auto-merge (squash) July 16, 2026 23:43
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.43590% with 3 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/exojs-tilemap/src/ImageLayerNode.ts 96.87% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 28.52kB (0.12%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
exo-full-iife-Exo-iife 2.62MB 8.75kB (0.34%) ⬆️
exojs-tilemap-esm 350.35kB 19.77kB (5.98%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: exo-full-iife-Exo-iife

Assets Changed:

Asset Name Size Change Total Size Change (%)
exo.full.iife.js 8.75kB 2.62MB 0.34%

Files in exo.full.iife.js:

  • ./packages/exojs-tilemap/src/TileMapNode.ts → Total Size: 2.73kB

  • ./packages/exojs-tilemap/src/TileMapView.ts → Total Size: 13.12kB

  • ./packages/exojs-tilemap/src/ImageLayerNode.ts → Total Size: 5.48kB

view changes for bundle: exojs-tilemap-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
TileMapView.js 3.69kB 17.75kB 26.26% ⚠️
TileMapView.d.ts 2.7kB 11.34kB 31.19% ⚠️
ImageLayerNode.js (New) 8.68kB 8.68kB 100.0% 🚀
TileMapNode.js 168 bytes 4.38kB 3.99%
ImageLayerNode.d.ts (New) 4.21kB 4.21kB 100.0% 🚀
TileMapNode.d.ts 168 bytes 3.09kB 5.74% ⚠️
public.d.ts 51 bytes 2.37kB 2.2%
register.js 54 bytes 1.53kB 3.66%
index.js 54 bytes 1.05kB 5.43% ⚠️

Files in TileMapView.js:

  • ./src/TileMapView.ts → Total Size: 17.33kB

Files in ImageLayerNode.js:

  • ./src/ImageLayerNode.ts → Total Size: 8.48kB

Files in TileMapNode.js:

  • ./src/TileMapNode.ts → Total Size: 4.1kB

@Exoridus
Exoridus merged commit 504f14e into main Jul 16, 2026
17 of 18 checks passed
@Exoridus
Exoridus deleted the worktree-infinite-maps-slice2 branch July 16, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant