2.1 KiB
solver/strip/strip.ts — horizontal/vertical strip decomposition
(no submodules)
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
whileloops. - 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
import { stripDecomposition } from "@suaveplan/emblem-solver/solver";
const required = [
[true, true, true],
[true, true, true],
];
const rects = stripDecomposition(required, 2, 3);
Usage: Advanced
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.