first commit
This commit is contained in:
75
docs/media/image/emblem-solver/src/grid/analyse/analyse.md
Normal file
75
docs/media/image/emblem-solver/src/grid/analyse/analyse.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# `grid/analyse/analyse.ts` — colour groups, islands, and shape stats
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module owns `analyseGrid`, the diagnostic phase of the conversion
|
||||
pipeline. It walks an RGBA grid once, buckets opaque pixels by colour, runs
|
||||
a 4-connected flood-fill on each bucket to recover the islands, and
|
||||
computes per-colour `density` (`pixels / bbox area`) and `fragmentation`
|
||||
(`islands / pixels`). The result feeds the layer-ordering heuristics and
|
||||
the per-island cover strategy in the solver pipeline.
|
||||
|
||||
## Features
|
||||
|
||||
- Single linear pass over the grid to populate `opaquePixels` and the
|
||||
colour buckets.
|
||||
- 4-connected BFS flood-fill — diagonals are treated as separate islands,
|
||||
matching the upstream reference and the convention used by every
|
||||
pixel-art tool.
|
||||
- Density and fragmentation are pure functions of the bucket and its
|
||||
bounding box; the solver uses both to score the `island-aware` ordering.
|
||||
- Output is structurally shared with the rest of the package via
|
||||
`GridAnalysis` — solver kernels consume `colourGroups`/`colourAnalysis`
|
||||
directly.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { analyseGrid } from "@suaveplan/emblem-solver";
|
||||
|
||||
const { colourGroups, colourAnalysis } = analyseGrid(pixels, w, h);
|
||||
console.log("colours:", colourGroups.size);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { analyseGrid } from "@suaveplan/emblem-solver";
|
||||
|
||||
function pickBackgroundColour(pixels, w, h) {
|
||||
const a = analyseGrid(pixels, w, h);
|
||||
let best = "";
|
||||
let bestScore = -Infinity;
|
||||
for (const [key, info] of a.colourAnalysis) {
|
||||
const score = info.totalPixels * info.density;
|
||||
if (score > bestScore) {
|
||||
best = key;
|
||||
bestScore = score;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Description |
|
||||
|---|---|---|
|
||||
| `analyseGrid(pixels, w, h)` | fn | Returns `GridAnalysis` |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The flood-fill uses an explicit stack (`pop()`) rather than recursion to
|
||||
avoid blowing the call stack on large emblems. We `add` to `visited`
|
||||
*before* enqueueing the neighbour, which guarantees each pixel is touched
|
||||
at most twice (once when discovered, once when its packed index is popped).
|
||||
The neighbour set is computed as a small inline array of `-1`-sentinel
|
||||
checks for the four cardinal directions; benchmarks against a `for (const
|
||||
[dx,dy] …)` form showed the inline form measurably faster in V8 and Bun.
|
||||
The `Map<string, Set<number>>` colour-group layout is intentional: the
|
||||
string key is the canonical colour identity, while the packed-index `Set`
|
||||
gives O(1) `has`/`add`/`delete` for the cover strategies.
|
||||
Reference in New Issue
Block a user