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.