Files
skills/docs/media/image/emblem-solver/src/solver/greedy/greedy.md
2026-05-29 08:20:56 +01:00

2.5 KiB
Raw Blame History

solver/greedy/greedy.ts — deterministic greedy set-cover

(no submodules)

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

import {
    dualAxisMaximalRects,
    enhancedGreedyCover,
} from "@suaveplan/emblem-solver/solver";

const candidates = dualAxisMaximalRects(available, rows, cols);
const cover = enhancedGreedyCover(candidates, required, cols, rows);

Usage: Advanced

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.