first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# `solver/layer/layer.ts` — multi-strategy per-layer cover
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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.