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,64 @@
# `solver/strip/strip.ts` — horizontal/vertical strip decomposition
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
This module implements `stripDecomposition`, a complementary cover strategy
that ignores the maximal-rect candidate pool entirely. It finds maximal
horizontal runs of required pixels, merges vertically-adjacent runs that
share both `x` and `length`, and runs the transposed variant (vertical runs
merged horizontally). The smaller of the two orientations wins. This
strategy is what gives `convert()` the right answer on tall thin shapes
that the histogram + greedy combo fragments.
## Features
- O(rows × cols) per axis; tail-call-free `while` loops.
- Two orientations evaluated, smaller wins — deterministic.
- Produces non-overlapping rects (unlike the overlap optimiser); the layer
pipeline only adopts the strip result if it covers the entire required
mask, otherwise it falls back to the greedy cover.
## Usage: Basic
```ts
import { stripDecomposition } from "@suaveplan/emblem-solver/solver";
const required = [
[true, true, true],
[true, true, true],
];
const rects = stripDecomposition(required, 2, 3);
```
## Usage: Advanced
```ts
import {
stripDecomposition,
isFullyAvailable,
} from "@suaveplan/emblem-solver";
const strips = stripDecomposition(required, rows, cols);
const valid = strips.filter((r) => isFullyAvailable(r, available));
```
## API Reference
| Symbol | Kind | Description |
|---|---|---|
| `stripDecomposition(required, rows, cols)` | fn | Smaller of horizontal-strips / vertical-strips |
## Implementation Notes
`mergeRuns` sorts runs by their primary coordinate, then their length, then
their secondary coordinate. The merge loop walks the sorted list once and
collapses consecutive entries that share `(x, length)` (horizontal) or
`(y, length)` (vertical) into a single taller/wider rect. Because the sort
is stable on JavaScript's `Array.prototype.sort` and we break on a length
mismatch, the merge terminates in O(n). The dual-orientation evaluation
ensures we never make the cover worse by picking the wrong axis — the
chess-board test exercises this branch heavily.