brush-paint-opt: lex-rank structural fails above bg-rate fail

Reorder tier-1 from (cov, bg, 1stk, 2stk, len) to (cov, 1stk, 2stk,
len, bg). Previous run found the comparator preferred a tiny-brush
config (bg fails 4 vs 7) over a structurally-correct one
(1stk fails 11 vs 38, len-budget fails 0 vs 104). bg-rate is an
aesthetic — wrong stroke counts and wandering paths break the
glyph's topology, so they outrank.

Mirrored in the jq sort in scripts/meta_optimize_distributed.sh.
This commit is contained in:
Mitchell Hansen
2026-05-06 08:05:04 -07:00
parent 357db2b061
commit 901c851b08
2 changed files with 16 additions and 9 deletions

View File

@@ -108,16 +108,16 @@ if [[ "$LINES" -ne "$N" ]]; then
fi fi
# Lex-sort matching CorpusReport's compare_reports: # Lex-sort matching CorpusReport's compare_reports:
# tier-1: fail_coverage, fail_bg, fail_single_stroke, fail_two_stroke, fail_length_budget # tier-1: fail_coverage, fail_single_stroke, fail_two_stroke, fail_length_budget, fail_bg
# tier-2: total_bg, total_strokes, total_unpainted_density, total_repaint, total_length # tier-2: total_bg, total_strokes, total_unpainted_density, total_repaint, total_length
echo "" >&2 echo "" >&2
echo "[orch] top 5 by lex order:" >&2 echo "[orch] top 5 by lex order:" >&2
jq -s 'sort_by([ jq -s 'sort_by([
.report.fail_coverage, .report.fail_coverage,
.report.fail_bg,
.report.fail_single_stroke, .report.fail_single_stroke,
.report.fail_two_stroke, .report.fail_two_stroke,
.report.fail_length_budget, .report.fail_length_budget,
.report.fail_bg,
.report.total_bg, .report.total_bg,
.report.total_strokes, .report.total_strokes,
.report.total_unpainted_density, .report.total_unpainted_density,
@@ -129,10 +129,10 @@ echo "" >&2
echo "[orch] best (full JSON on stdout):" >&2 echo "[orch] best (full JSON on stdout):" >&2
jq -s 'sort_by([ jq -s 'sort_by([
.report.fail_coverage, .report.fail_coverage,
.report.fail_bg,
.report.fail_single_stroke, .report.fail_single_stroke,
.report.fail_two_stroke, .report.fail_two_stroke,
.report.fail_length_budget, .report.fail_length_budget,
.report.fail_bg,
.report.total_bg, .report.total_bg,
.report.total_strokes, .report.total_strokes,
.report.total_unpainted_density, .report.total_unpainted_density,

View File

@@ -253,15 +253,21 @@ pub fn run_one_start(start_idx: usize, base: &PaintParams, max_passes: u32) -> R
// //
// Tier 1 — count of letters violating each hard criterion: // Tier 1 — count of letters violating each hard criterion:
// 1. max unpainted cluster > 0.5 × brush_area (a feature is missing) // 1. max unpainted cluster > 0.5 × brush_area (a feature is missing)
// 2. bg_painted / total_swept > 5 % // 2. SINGLE_STROKE_LETTERS with strokes ≠ 1 (wrong topology)
// 3. SINGLE_STROKE_LETTERS with strokes ≠ 1 // 3. TWO_STROKE_LETTERS with strokes ≠ 2 (wrong topology)
// 4. TWO_STROKE_LETTERS with strokes ≠ 2 // 4. total_length > 2 × skeleton_length (wandering path)
// 5. total_length > 2 × skeleton_length // 5. bg_painted / total_swept > 5 % (off-glyph paint)
//
// Order rationale: structural correctness (cluster coverage, stroke
// topology, path budget) ranks above aesthetic cleanliness (off-glyph
// paint). A previous run found the comparator was preferring
// tiny-brush configs that minimised bg paint at the cost of doubling
// stroke counts and 5×ing path length — bg sat at the wrong tier.
// //
// Tier 2 — corpus aggregates (smaller is better, in this order): // Tier 2 — corpus aggregates (smaller is better, in this order):
// 6. total bg pixels // 6. total bg pixels
// 7. total stroke count // 7. total stroke count
// 8. total density-weighted unpainted (Σ size^1.5) // 8. total density-weighted unpainted (exponential per-cluster)
// 9. total repaint // 9. total repaint
// 10. total length // 10. total length
@@ -350,11 +356,12 @@ pub fn compare_reports(a: &CorpusReport, b: &CorpusReport) -> Ordering {
} }
}; } }; }
// Tier 1: count of letters failing each hard criterion. // Tier 1: count of letters failing each hard criterion.
// Structural fails first; bg-rate last (a soft aesthetic).
cmp_field!(fail_coverage); cmp_field!(fail_coverage);
cmp_field!(fail_bg);
cmp_field!(fail_single_stroke); cmp_field!(fail_single_stroke);
cmp_field!(fail_two_stroke); cmp_field!(fail_two_stroke);
cmp_field!(fail_length_budget); cmp_field!(fail_length_budget);
cmp_field!(fail_bg);
// Tier 2: aggregates. // Tier 2: aggregates.
cmp_field!(total_bg); cmp_field!(total_bg);
cmp_field!(total_strokes); cmp_field!(total_strokes);