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

2.7 KiB

solver/layer/layer.ts — multi-strategy per-layer cover

(no submodules)

Purpose

This module owns processLayerMultiStrategy, the function the converter calls once per layer in the current ordering. It runs every covering strategy the package implements (dual-axis histogram + greedy, per-island cover, strip decomposition, adjacent-rect merge) and returns the result with the fewest rectangles. Running every strategy is what makes the upstream algorithm robust — no single strategy wins on every emblem.

Features

  • Strategy A (dual-axis histogram → greedy → overlap optimise) is always evaluated; it is the workhorse.
  • Strategy B (per-island cover) only runs when the colour has more than one island — pure optimisation, since a single-island colour would produce the same answer.
  • Strategy C (strip decomposition) is only adopted when the strips fully cover the required pixels; the layer pipeline does not stitch in a partial strip cover.
  • Strategy D (adjacent-rect merge) runs against the best-so-far so we never reject a free merge.
  • The final winner is the smallest of all four (or three when B is skipped).

Usage: Basic

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

const a = analyseGrid(pixels, w, h);
const ownPixels = a.colourGroups.get("255,0,0")!;
const rects = processLayerMultiStrategy(
    "255,0,0",
    ownPixels,
    a,
    new Set<number>()
);

Usage: Advanced

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

const earlier = new Set<number>();
for (const key of orderedKeys) {
    const ownPixels = analysis.colourGroups.get(key)!;
    const rects = processLayerMultiStrategy(
        key,
        ownPixels,
        analysis,
        earlier
    );
    layers.push({ key, rects /* ... */ });
    for (const p of ownPixels) earlier.add(p);
}

API Reference

Symbol Kind Description
processLayerMultiStrategy(key, ownPixels, analysis, earlier) fn Smallest rect cover across all strategies

Implementation Notes

The strip strategy only contributes when it fully covers the required mask. We compute coverage by intersecting each valid strip with the required grid (via pack) and comparing the resulting count to a single pre-tallied totalRequired. The adjacent-rect merge pass runs after the strategies have produced their winners; running it earlier would force a merge on results that would have been pruned anyway. The final reduce uses a strictly-less-than comparison (a.length <= b.length) so the first result wins on ties — this preserves deterministic ordering across runs.