Mibo.MonoGame.Adaptive
6.0.0
dotnet add package Mibo.MonoGame.Adaptive --version 6.0.0
NuGet\Install-Package Mibo.MonoGame.Adaptive -Version 6.0.0
<PackageReference Include="Mibo.MonoGame.Adaptive" Version="6.0.0" />
<PackageVersion Include="Mibo.MonoGame.Adaptive" Version="6.0.0" />
<PackageReference Include="Mibo.MonoGame.Adaptive" />
paket add Mibo.MonoGame.Adaptive --version 6.0.0
#r "nuget: Mibo.MonoGame.Adaptive, 6.0.0"
#:package Mibo.MonoGame.Adaptive@6.0.0
#addin nuget:?package=Mibo.MonoGame.Adaptive&version=6.0.0
#tool nuget:?package=Mibo.MonoGame.Adaptive&version=6.0.0
Mibo
Install the templates:
dotnet new install Mibo.Templates dotnet new mibo-2d -o MyGame # MVU runtime dotnet new mibo-2d-adaptive -o MyGame # adaptive runtime cd MyGame dotnet run
NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.
Following that spirit, Mibo keeps it lean, just F# and the Elmish loop with a handful of commodities to get out of your way and let you enjoy the craft.
Mibo is an Elmish-based F# game framework with two interchangeable backends — raylib-cs and MonoGame (DesktopGL/OpenGL and WindowsDX/DirectX) — designed to allow developers to write games using familiar MVU patterns for all kinds of game genres and sizes.
Mibo aims to solve 80/20 of use cases for enabling developers to focus on game logic rather than boilerplate code, providing guidelines and architecture for structuring game code, handling input, rendering, asset management, and time management among others.
What's in the box?
- Elmish runtime (MVU loop) with
Cmd,Sub, optional fixed timestep, and frame-bounded dispatch - Input — raw input (
Keyboard,Mouse) + semantic mapping viaInputMap/ActionState - Assets — texture, font, sound, and model loading caches
- Rendering — Command buffer based rendering:
- 2D batch renderer with layers and multi-camera support
- 3D batch renderer with opaque/transparent passes and custom shader switching
- Escape hatches for custom GPU work
- Camera helpers with screen-to-world, orbit, and ray casting
- Layout: 2D procedural level layout. One
CellGrid2Dfor squares and hexes, authored with the Flow DSL (grid template areas, flexbox rows and columns, docks, landmark queries) - 3D levels: authored as 2D grids with per-column height; the retired
Layout3Dvoxel family still compiles with obsolete warnings (seedocs/migration-to-v6.md) - Animation — sprite sheet slicing,
AnimatedSpritestate machines, and grid-based animation definitions - Mibo.Adaptive — a pull-based incremental computation library for tight-loop workloads:
CVal/AValroots and projections plus adaptive sets, maps, and lists with element-level deltas, allocation-free in steady state. The Mibo integration (AdaptiveProgram/AdaptiveHeadlessand the windowed hosts) is experimental. - Input Mapper — Listen to raw input and map it to semantic actions
- Performance — zero-allocation hot paths: spatial grid queries return a single result array per call, and per-frame dictionary lookups, light merging, and render-pipeline bookkeeping allocate nothing
Packages
Mibo ships as two independent runtime lanes on top of a shared kernel. Pick one lane per game — MVU installs pull no adaptive code, and adaptive installs pull no MVU code:
| Package | Gives you |
|---|---|
Mibo.Core |
Runtime-neutral kernel: GameContext, GameTime, render buffers, input contracts, layout, diagnostics |
Mibo.Markup |
Authored text documents for Flow levels (XML and KDL front-ends, resolver, Flow emitter) |
Mibo.Mvu |
The Elmish/MVU runtime: Cmd, Sub, Program, loops, and headless support |
Mibo.Adaptive |
The dependency-free incremental computation library (CVal/AVal roots, adaptive sets, maps, and lists) |
Mibo.Adaptive.Mibo |
The Mibo-side adaptive runtime: AdaptiveProgram, AdaptiveHeadless |
Mibo.Raylib |
Neutral raylib shell: renderers, camera, windowing, input polling |
Mibo.Raylib.Mvu |
MVU host for raylib (RaylibProgram, RaylibGame) |
Mibo.Raylib.Adaptive |
Adaptive host for raylib (AdaptiveRaylibGame) |
Mibo.MonoGame |
Neutral MonoGame shell (same surface as the raylib shell) |
Mibo.MonoGame.Mvu |
MVU host for MonoGame (MonoGameProgram, MiboGame) |
Mibo.MonoGame.Adaptive |
Adaptive host for MonoGame (AdaptiveMonoGameProgram) |
MVU games reference Mibo.Raylib.Mvu or Mibo.MonoGame.Mvu; adaptive games reference Mibo.Raylib.Adaptive or Mibo.MonoGame.Adaptive (which bring the kernel, shell, and Mibo.Adaptive transitively). All namespaces and type names are unchanged from previous releases — the split only moves code between packages.
Getting started
Prerequisites:
- .NET SDK 8 or later
- A working OpenGL setup
dotnet --version
dotnet tool restore
dotnet restore
dotnet build
dotnet test
To build the docs site locally:
dotnet tool restore
dotnet fsdocs build
# or for live editing:
dotnet fsdocs watch
Samples
The samples are stored in a separate repository: Mibo.Samples.
You'll find examples of:
2D:
- PlatformerSample - A 2D side-scrolling platformer with procedural world generation, sprite animation, lighting, particles, and sound. Uses Mibo's Elmish architecture with
InputMap,AnimatedSprite,CellGrid2D, andLightContext2D.- Mibo.Raylib targeting Desktop OpenGL
- Mibo.MonoGame targeting DesktopGL (cross-platform)
- SpaceBattle - A turn-based tactical strategy game on a hex grid with fog of war, laser combat, particle effects, faction-based turns (Human + AI), and animated unit movement. Demonstrates complex game state management, hex grid spatial queries, and multi-phase turn resolution.
- Mibo.Raylib targeting Desktop OpenGL
- PingPong - A networked multiplayer Pong game with a client-server architecture over WebSockets. The server runs game logic and broadcasts state; the client renders locally and sends input.
- Mibo.Raylib Client
- Mibo.MonoGame Client
- dotnet app acting as a server running Mibo.Mvu's headless support
3D:
ThreeDSample - A 3D platformer with procedurally generated voxel terrain, PBR lighting, shadow atlas, 3D character animation, minimap overlay, and physics. Showcases Mibo's
Renderer3D,ForwardPbrPipeline, andAnimation3DState.- Mibo.Raylib targeting Desktop OpenGL
- Mibo.MonoGame targeting DesktopGL (cross-platform)
FPSSample - A first-person shooter featuring enemy AI, weapon systems, health management, and atmospheric lighting. Demonstrates Mibo's composable systems architecture with per-system sub-models, event-driven cross-system communication, and a
Systempipeline with snapshot barriers.- Mibo.Raylib targeting Desktop OpenGL
- Mibo.MonoGame targeting DesktopGL (cross-platform)
- Mibo.MonoGame targeting WindowsDX (Windows only, DirectX)
License
Mibo is distributed under the zlib/libpng License.
Built on
Mibo is built on top of:
- raylib — the cross-platform graphics library that powers the raylib backend's rendering, input, and audio layers
- raylib-cs — the C# bindings that make raylib accessible from .NET
- MonoGame — the cross-platform framework that powers the MonoGame backend (DesktopGL/OpenGL and WindowsDX/DirectX)
- AdaptiveSlop — the pull-based incremental computation library by TheAngryByrd that Mibo.Adaptive was adopted from
Mibo.Adaptive originated from AdaptiveSlop — adopted in its entirety, renamed, and maintained as part of Mibo.
Feedback
Issues and PRs are very welcome. If you're interested in using F# for game development beyond simple 2D games, Mibo aims to be a practical, batteries-included framework that scales with your ambition.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FSharp.Core (>= 10.1.401)
- Mibo.Adaptive.Mibo (>= 6.0.0)
- Mibo.MonoGame (>= 6.0.0)
-
net8.0
- FSharp.Core (>= 10.1.401)
- Mibo.Adaptive.Mibo (>= 6.0.0)
- Mibo.MonoGame (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
### Added
- **Core:** an instance can cover more than one cell. `InstanceSpan` states how many cells one drawn instance covers — `Span(across, deep)` on a square grid, `Radius r` on a hex one, `One` for the identity — and `Occupancy.scan` turns a painted grid plus a span projection into the occupancy of the map: which instance owns each cell, the rectangle each one covers, and how many populated cells a span hides. A span claims the cells it covers, so a plain cell under a plate stops drawing on its own; two spans that overlap, a span that leaves the grid, and a shape that disagrees with the grid geometry each fail with the cell named. `Occupancy.owner` is the one query a hover, a collision test, or a spawn reads, `rectOf` gives an anchor's rectangle, and `iterInWindow` enumerates the anchors whose rectangle meets a window, so an instance that covers a window cell from an anchor outside it still draws. `Stack.feet` derives the height every layer of a stack stands on from the layers below it, a spanning anchor lifting its whole rectangle. `CellGrid2D.visibleRange` exposes the world-to-cell window bounds that `iterVisible` used to keep to itself, hex padding included.
- **Raylib/MonoGame:** `InstancedRenderContext` draws instances that cover several cells. `InstancedRenderContext.Rect(getKey, getMeshesAndMaterial, getTransform)` hands the transform the rectangle each instance covers, so a game scales one model over the cells it stands for instead of re-deriving the size from the cell. `RenderInstanced` and `RenderWindowInstanced`, and their `...WithEffect` twins, take an occupancy as well as a grid: the whole-map form draws one instance per anchor, and the windowed form keeps an instance whose anchor is outside the window while any of its rectangle is inside. The `Draw` DSL routes to the same members through the occupancy forms of `renderFootprintInstanced` and `renderFootprintWindowInstanced`.
- **Markup:** a flow child claims the tracks its own size needs. A container with declared `cols`/`rows` places each flow child into the tracks it requires, so a piece that covers two cells by two flows into a two-cell block of one-cell tracks instead of landing in one track and painting over its neighbour, and the next child wraps to the following row when the row runs out. A child whose footprint is larger than the declared tracks fails the build with the child's name. A child with no size of its own keeps the previous behaviour: it stretches over the track it lands in.
- **Markup:** `cols`, `rows` and `areas` are properties of the container that owns them, and the resolver fails where the mistake is written. A container states its tracks and its area template directly — `plot w=20 h=8 cols="fixed 6 1 1" rows="fixed 3 1" areas="road woods; road lake"` — with `;` ending a template row and `.` standing for an empty cell. A one-token list reads the same in both front-ends whatever type the attribute takes, and the three lists belong to no `style` rule. The build fails on a container that states a list twice, an empty list, an area row wider than the declared columns, a repeated property on one node, leftover arguments on an `element` definition, and a template or surface element named `map`, `layer`, `element`, `style`, `repeat`, `cols`, `rows`, `areas`, `fill`, `fillRect`, `set`, `border`, `rect`, or `generate`. An `element` declaration reads its two extents from either channel, so `element hut 3 2` and `element hut w=3 h=2` agree.
- **Markup:** a document sizes one instance and a build reports the occupancy of every layer. `Doc.Surface` gains `Span`, the projection that reads how many cells a cell covers, and `WithSpan`, the write that lets a statement size one; both absent keeps every existing document building exactly as before. `set` takes `spanX=` and `spanZ=` as a pair, a word states the default span and a statement overrides it, and `fill`, `fillRect`, `border`, and `rect` refuse a spanning word with the word's position — an area statement paints every cell of its box, so one instance has no single place to stand in it. An element's extent follows the span it places, and a style size or a declared extent that disagrees with it fails naming both sizes. `DocFlow.BuiltLayer` carries the layer's `Occupancy`, `DocFlow.buildLayers` scans every layer and prefixes a failure with the layer's name, and `DocFlow.build`/`buildXml` refuse a document that places a spanning word, naming `buildLayers`, because a single grid cannot report the occupancy the map needs.
- **Markup:** a document declares layers, and each layer builds its own grid. `layer ground { ... }` in KDL and `<layer name="ground">` in XML are legal only as direct children of `map`; paint written outside a layer — the map's own statements and its non-layer children — forms an implicit bottom layer named `main`. `DocFlow.buildLayers` (KDL) and `DocFlow.buildLayersXml` (XML) return every layer in draw order, each with its name, its grid, and its landmarks, so an upper layer draws transparent over the lower one and collision reads the layer it means. `DocFlow.emitLayers` hands back the layer stamps unpainted, for a caller that owns its grids. `DocFlow.build` and `buildXml` keep their signatures: a document without layers builds exactly as before, and a document that resolves to two or more layers fails naming them instead of silently painting only one. A layer carries one name and nothing else; a duplicate name, a misplaced `layer`, an empty layer, and a layer name outside the map all fail the build with their position. Every element still reports its rectangle under its own name through its layer's landmarks, so a name may repeat across layers.
- **Core:** `Flow.runLayers` and `Flow.buildLayers` lay out a layer stack — the plural of `Flow.run` and `Flow.build`. The caller owns the grids; a layer is an array position, stamp i paints grid i, bottom first, and each layer gets its own landmarks registry so element names are unique per layer and may repeat across layers. `Flow.runLayers` throws when the arrays differ in length, when the grids differ in width or height, or when a stamp asks for `Expand`, and it checks every stamp and grid before painting any of them. Hex grids run unchanged.
- **Markup:** an emitted document reports its own structure. Every element reports its resolved rectangle through `Flow.run`'s landmarks — `Flow.taggedRects` for the group, `Flow.isTag` and `Flow.tryTagGrid` for per-cell queries — so a live editor can outline and name the region that painted the cell under the cursor. Element names ride the tag channel, not the named-stamp channel: one document may use the same element name many times, and named stamps must be unique. The anonymous `plot` container reports under `plot`, so a document written without named elements still answers "what painted this cell". `DocFlow.build` and `buildXml` keep returning the grid alone and record no landmarks — a build allocates no memory for the structure. A caller that needs the landmarks runs parse, resolve, `Doc.findMapNode`/`Doc.dimsOf`, and `Flow.run (DocFlow.emit root)` itself, as the markup guide shows.
- **Markup:** the Flow emitter completes `Mibo.Markup` (`DocFlow.build` for KDL, `DocFlow.buildXml` for XML): one call parses, resolves, emits to Flow stamps, and lays the map out in one pass. Every layout channel rides the framework's own primitives — exact `x=`/`y=` placement is `Flow.at`, stack alignment is `Dock` flags over `overlay` layers, flow packing is `Flow.grid` with named areas and explicit `Slot` places (`auto` tracks size from the children's footprints inside the grid), and `pack=scatter` is `Flow.scatter`'s seeded rule. Child sizes come from the document, so no measuring pass runs on the emitter path. Golden tests hand-lay each layout channel with the raw `Layout` ops and compare cell for cell, so the emitter is checked against the framework's own painting; the same document built in KDL and in XML produces the identical grid; scatter additionally asserts containment, non-overlap, and rebuild-identical output. Slot spans below one, negative `col=`/`row=`, a span without a slot, an `area=` mixed with `col=`/`row=`, an unknown area name, a slot past the tracks, and `x=`/`y=` inside flow or scatter all fail the build instead of degrading. A document that declares `gapx=` different from `gapy=` fails with a clear message until per-axis grid gaps exist in Flow.
- **Markup:** document resolution lands in `Mibo.Markup` (`Doc`). `Doc.resolve` turns a front-end's `Node` tree into an `Item` tree: `element name { ... }` templates expand and merge their bodies with use-site bodies, `repeat n` duplicates its children (capped at 100000, and a duplicate template name or a template that collides with a game surface element fails the build with its position), `style name` rules cascade with inline properties (solver defaults, then rules in order, then the node), and every failure — unknown elements, unknown words and kernels, bad properties, leftover arguments, bad map dimensions — carries its document position when the front-end tracks one. Layout rides the framework's geometry (`CellPoint`, `CellRect`, `Align`, and `Track` with `Auto` from `Mibo.Layout`, plus `CellSize`, the package's named width/height pair for element sizes), paint resolves to a closed `Op` union interpreted through `Layout` at render time, and the game speaks through a `Surface` of frozen tables (words, kernels, elements) built once. An element's extent comes from the geometry of its own body unless the document declares one, so no measuring pass runs on the document path; `Doc.measure` remains for elements a game builds by hand. An unknown kernel inside a game-declared element body fails the build instead of vanishing at render. A document holds one map: a stray root node or a second map fails the build, and the map's two dimensions may come from either channel or one of each (`map 36 20`, `map w="36" h="20"`, `map 36 h="20"`). A `style` rule names itself the same way in both channels — a word argument in KDL, the `name` property in XML — and the property is the rule's key rather than a style of its own, so an XML rule applies cleanly.
- **Markup:** the KDL front-end joins XML in `Mibo.Markup` (`Kdl.parse`, KDL 2.0 on KdlSharp — the package's only external dependency, MIT, zero transitive dependencies, confined to the adapter file). Bare values are positional args, `name=value` pairs are properties, a node whose kind is `element` carries its template name as the label, and slashdash `/-` comments out a whole node (parsed and dropped, so a bad body still errors). Unclosed and unbalanced braces fail visibly. Node positions come from the reader's own line and column — no text scanning — so KDL resolution errors point at the authoring line (an integer past the int32 range becomes a decimal; typed literals such as hex and underscores are KDL-only and pinned by tests). The resolver reads every scalar by name, so KDL's positional args and XML's attributes resolve the same documents, and the emitter's tests build the same document in both syntaxes and compare the grids cell for cell — teams pick a syntax and documents migrate between them without touching the game.
- **Markup:** a new `Mibo.Markup` package: authored text documents for Flow levels, format-neutral at the core. The `Node` tree (kind, positional args, named properties, children) is the contract every front-end produces, and `Markup.where` turns an offset into a line and column for positioned error messages. The first front-end is XML (`Xml.parse`, on the BCL parser — no external dependency, and no hand-rolled scanning): elements are nodes and children, attributes are the scalar channel (`map w="36" h="20"` — the resolver reads every scalar by name), values type int then finite float then word, an `element` definition names itself with the `name` attribute, comments and whitespace are free, text that is not markup fails the parse instead of vanishing, and parse failures carry the parser's own line and position. XML documents carry no node positions, so XML resolution errors name the element; KDL documents carry real line-and-column positions from the parser. The game side (word/kernel/element surfaces, the resolver, and the Flow emitter) ships in the same package.
- **Core:** layer containers fail loud on sized children. `Flow.overlay`, `Flow.group`, and `Stamp.overlay` are full-bleed: every child paints the whole assigned area, so a sized child was silently stretched over the container. A nonzero-footprint direct child now throws at construction with the fix in the message — mount sized layouts with the new `Flow.stretch` (a zero-footprint layer that stretches its stamp over the whole container), place exact rectangles with `Flow.at`/`docked`, or use a footprint-honoring container (`row`/`column`/`grid`). `Flow.overlay` and `Stamp.overlay` are context-sized (zero footprint), since every legal layer is.
- **Core:** seeded element placement for the flow DSL. `Flow.scatter seed children` places sized children at non-overlapping origins over the assigned area: candidate origins are the container's cells in the order of a seeded permutation (a fixed xorshift Fisher-Yates — no BCL RNG, so layouts cannot drift across .NET versions, unlike the paint-side scatter styles), children place in the given order, and each takes the first origin where it fits. The same seed and the same container build the same level on every run; a child that fits nowhere fails the build naming the child when it is named, and `expand` children throw at construction. The footprint is the largest child's, so give the scatter a sized region — a grid area, a stretch over a `group`, or a stretched dock. The paint-side scatter styles (`noise`, `clumps`) stay for cells and small stamps; `Flow.scatter` is the element-level counterpart.
- **Core:** explicit cell placement and content-sized tracks in `Flow.grid`. Places mount by named template area or by `Slot (col, row, colspan, rowspan)` — track indices with spans, so a consumer no longer synthesizes one unique area name per slot; slots may overlap and paint in `Places` order, later places on top. The new `Auto` track sizes to the largest footprint of its span-1 places at construction (a place reports no footprint on a zero axis, so it contributes nothing there), and a place that spans tracks shares its footprint over the `Auto` tracks in its span, so a span no single-track place can size still paints. Auto counts toward the grid's intrinsic footprint like `Fixed`; an auto track with no content collapses to zero. Invalid slots (negative indices, spans below one, spans past the declared tracks) and unknown area names throw at construction, before anything paints. `GridOpts.Places` takes `(Place * Stamp)` pairs — area-name places became `Place.Area "name"`.
- **Core:** exact-placement vocabulary for the flow DSL. `DockSpec.Inset` takes per-side amounts (`InsetSpec`, with `InsetSpec.Zero` for no inset), so a docked element can sit at different distances from each edge — an edge anchor honors its own side, and a stretching dock spans the container minus the two insets of its axis. `Flow.at x y stamp` places an element at an exact cell offset of its container (zero footprint, mounts inside `overlay` and grid areas like `docked`; a zero dimension of the wrapped stamp stretches from that origin to the far edge), and `Flow.fillRect` fills a sub-rectangle of a box in local coordinates — the local-area counterpart of `fill` for rooms, roads, and platforms inside a bigger element. Negative insets and offsets clamp at zero, and a span clamps at zero length instead of going negative.
- **Core:** a prototype flow layout DSL for 2D level authoring (`Mibo.Layout.Flow`). Elements (`Stamp`) declare their footprint in cells and compose by arithmetic (`Stamp.beside`, `above`, `overlay`, `inset`, `offset`, `repeat`), so containers compute their own size and authors stop hand-counting offsets. `Flow.row`/`column` lay children out with gaps, cross-axis alignment, main-axis justification, wrapping, and flex-style `expand` shares; `Flow.grid` ports CSS grid to cells with `Fixed`/`Weight`/`Percent`/`Auto` tracks and grid-area template strings configured through a labeled `GridOpts` record over arrays; `Flow.dock` pins elements to container edges through a labeled `DockSpec` (anchor, inset, and the stamped element), and `Flow.docked` layers docked elements over a base layout (`Flow.stretch` mounts a sized base). Any existing stamp pipeline joins the flow model through `Stamp.sized`, and everything still paints through the unchanged `Layout.*` primitives. `Flow.run` lays a stamp out over the grid and returns the grid together with the resolved rectangle of every `Stamp.named` element, so levels can drive entity spawns from the same document that paints the tiles.
- **Core:** coordinate-free box styles for the flow DSL. `Flow.fill`, `border`, `rect`, `corners`, `checker`, `noise`, `noiseBy`, `texture`, `replace`, `weather`, and `clumps` paint the whole area of the element they are applied to, so leaf elements become `Stamp.box w h [ styles ]` with the size stated once and no x/y anywhere. `Flow.noiseBy` scatters generated content: the callback picks the tile per scattered cell, the sparse counterpart of `texture`. `Flow.canvas` is the context-sized box (it paints whatever area its container assigns — grid areas stretch it, zero footprint always means "stretch this axis"), `Flow.group` is a sized box with layered children, `Flow.strip` docks a full-length bar of a given thickness to one edge, and `Flow.dock`/`Flow.docked` stretch a zero footprint dimension over the section like grid areas do. `Flow.grid` placements honor a fixed child footprint and stretch zero-footprint children over the area, and `Flow.row`/`column` cross-stretch children that declare no cross size, matching CSS defaults.
- **Core:** landmark tags for the flow DSL. `Stamp.tagged [ "safe-zone" ]` groups an element's resolved rectangle under opaque string tags (additive, many elements per tag, many tags per element), `Flow.region [ "no-build" ] w h` is the non-painting extent version, and every tagged rectangle rasterizes into a per-tag bit grid. `Flow.isTag "no-build"` takes the cell as a `CellPoint` and answers "is this cell tagged" with one dictionary lookup and one array read for walking-style queries, `Flow.taggedRects` returns the group's rectangles as a list, most recent first, and `Flow.tryTagGrid` hands the raw bit grid to hot loops that hoist the lookup. Named and tagged rectangles record the painted intersection with their container, so spawn math never reads outside the level. `Flow.scanTiles` derives cell tags from the painted tiles through a game-supplied extraction function, so irregular regions (noise-carved woods, scattered props) support the same queries while the engine never interprets the tags. `Flow.run` and `Flow.build` report a `Landmarks` value with the named positions, the tag groups, and the cell grids. Duplicate `Stamp.named` names throw at build time instead of silently keeping the last rectangle.
- **Core:** authoring sugars for the flow DSL. `Flow.prop` is a single-cell prop, `Flow.expand` keeps level documents on `Flow.*` (alias of `Stamp.expand`), and `Flow.build` lays a level out and derives its cell-tag bit grids in one call instead of `run` plus `scanTiles`. Layer containers document their full-bleed contract: children paint into the whole assigned area, so sized children mount through `Flow.stretch` and fixed-footprint placement goes through `docked`/`at` or footprint-honoring containers (`grid` areas, `row`/`column`). `Flow.expand` acts on `row`/`column` children; containers that cannot expand a child (`overlay`, `group`, `grid`, `dock`, `run`) and the arithmetic combinators (`beside`, `above`, `overlay`, `inset`, `offset`, `repeat`) throw instead of ignoring it — apply `expand` to the composite. `Flow.dock` documents that it records no positions; `Flow.docked` is the form that reports the docked rectangle inside a level document.
- **Core:** hex grids join the unified grid storage. `CellGrid2D` gains a `Geometry` (`Square` or `Hex of HexOrientation`) and `CellGrid2D.createHex` builds hexagons over the same storage as squares from a labeled `HexSpec` record, so hex maps author with the same Flow DSL, landmarks, and tag queries as square maps — geometry changes world positions and spatial queries only. `CellGrid2D.hexOrientation` and `hexRadius` read the hex parameters back. `iterVisible` culls both geometries with orientation-aware windows, `Grid2DSpatial` and `GridOccluders` reject hex grids, and hex-aware queries live in `Hex2DSpatial`.
- **Core:** the complete paint vocabulary for the flow DSL. `Flow.cell` paints one cell, `Flow.repeatX`/`repeatY` paint runs of cells (`background-repeat`), `Flow.line` paints a Bresenham segment, `Flow.circle` and `Flow.polygon` paint shapes (`clip-path`), `Flow.scatterBorder`/`Flow.scatterLine` weather edges and routes, `Flow.checkerBorder` alternates the border, `Flow.clear()` erases the box, `Flow.setIfEmpty` paints only empty cells (the `:empty` selector), and `Flow.map` rewrites the existing cells as a derive pass. Styles with several settings take named struct specs — `CellPoint` for coordinates, `CircleSpec`, `ScatterSpec`, `ScatterLineSpec`, `WeatherSpec`, and `InsetSpec` (for `Stamp.insetEx`) — so every value is labeled at the call site and bare x/y ints cannot be transposed; `Flow.polygon` takes its vertices as a `struct (int * int)[]`. The square `Layout` module stays open as the pixel-perfect escape hatch; it keeps only section plumbing that Flow deliberately does not wrap.
- **Raylib/MonoGame:** `InstancedRenderContext` renders 2D footprint grids directly. `RenderInstanced` and `RenderWindowInstanced` (plus their `...WithEffect` variants) iterate a `CellGrid2D` — square or hex, culled to a world-space window — and emit one instanced draw per key, giving heightmap levels a first-class replacement for the retired voxel renderers. The transform function receives each column's base world position; games scale the unit block by the column's height there. The `Draw` DSL routes to the same members: `Draw.renderFootprintInstanced` and `Draw.renderFootprintWindowInstanced`, each with a `shaderForKey` overload.
### Deprecated
- **Core:** the [migration guide](docs/migration-to-v6.md) maps every retired API below to its replacement.
- **Core:** the hex compatibility surface. `HexGrid` is now a type abbreviation over `CellGrid2D` and its module delegates to the unified grid, and `HexLayout`/`HexGridSection` still compile — all marked obsolete in favor of `CellGrid2D.createHex` and the Flow DSL (the square `Layout` ops also run on hex storage for pixel-perfect control). Existing `Hex2DSpatial` code compiles unchanged.
- **Core:** the 3D grid families. `CellGrid3D`, `Layout3D` (module, section, helpers), `Grid3DSpatial`, `HexGrid3D`, `HexLayout3D`, `Hex3DSpatial`, the layered 3D grids, and the stamp libraries (`Platformer`, `TopDown`, `Terrain`, `Interior`) are obsolete — author 3D as a 2D grid with per-column height. `BoundingBox`, the general culling type, stays.
- **Core:** the layered grids (`LayeredGrid2D`/`LayeredLayout` and the 3D/hex siblings). A layered grid is a dictionary of grids; game code can own the dictionary.
- **Raylib/MonoGame:** `CellGridRenderer3D`, `HexGrid3DRenderer`, and the `renderCellGrid`/`renderHexGrid` instanced buffer members are obsolete with the 3D grid family. Render from your own instance data; the draw surface itself stays.