Files
skills/docs/media/image/emblem-solver/src/grid/analyse/analyse.md
2026-05-29 08:20:56 +01:00

2.6 KiB

grid/analyse/analyse.ts — colour groups, islands, and shape stats

(no submodules)

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

import { analyseGrid } from "@suaveplan/emblem-solver";

const { colourGroups, colourAnalysis } = analyseGrid(pixels, w, h);
console.log("colours:", colourGroups.size);

Usage: Advanced

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.