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,72 @@
# `solver/greedy/greedy.ts` — deterministic greedy set-cover
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
This module provides the greedy selector that drives the primary covering
strategy. `enhancedGreedyCover` picks rectangles from a candidate pool
using a deterministic three-key score (covered count, efficiency, centroid
proximity). `perIslandCover` runs the same selector independently on each
4-connected island so the algorithm never wastes budget on rectangles that
span the transparent gap between two disjoint islands.
## Features
- Three-key score: `covered * 1000 + efficiency * 100 + proximity * 10`.
The factors are deliberately spaced so a more-covering rect always beats
a more-efficient one, and ties are broken by centroid proximity.
- Falls back to emitting `1×1` rectangles for any residual uncovered
pixels — guarantees the cover is complete even when no candidate
intersects the residual.
- `perIslandCover` filters the candidate pool per island so the selector
never considers a spanning rect; this halves selection work on
multi-island colours.
## Usage: Basic
```ts
import {
dualAxisMaximalRects,
enhancedGreedyCover,
} from "@suaveplan/emblem-solver/solver";
const candidates = dualAxisMaximalRects(available, rows, cols);
const cover = enhancedGreedyCover(candidates, required, cols, rows);
```
## Usage: Advanced
```ts
import {
analyseGrid,
perIslandCover,
dualAxisMaximalRects,
} from "@suaveplan/emblem-solver";
const a = analyseGrid(pixels, w, h);
const info = a.colourAnalysis.get("255,0,0")!;
const candidates = dualAxisMaximalRects(available, h, w);
const cover = perIslandCover(info.islands, candidates, w, h);
```
## API Reference
| Symbol | Kind | Description |
|---|---|---|
| `enhancedGreedyCover(candidates, required, w, h)` | fn | Greedy cover with three-key score |
| `perIslandCover(islands, candidates, w, h)` | fn | Independent cover per island |
## Implementation Notes
The centroid term is computed against the running set of *uncovered*
required pixels, not the original set — this keeps selections clustered
even as the residual shrinks. We use a precomputed `maxDist =
sqrt(w² + h²)` so the proximity term is normalised to `[0, 1]`; without
this, the third tiebreaker could dominate the second on large grids.
`perIslandCover` materialises a fresh `boolean[][]` per island; this is
acceptable because the typical island count is small (≤ 10 on the
upstream fixtures) and the allocation cost is dominated by the cover work
inside.