brush-paint: roll meta-optimizer idx-20 winners into PaintParams + ScoreWeights defaults

Inner-score result on the alphabet corpus: cov_fail=8, bg_fail=0,
len_fail=0; ~12 px bg per letter. Stroke-count constraints (12
single-stroke + 11 two-stroke letters) are NOT respected by the
soft inner score; that's a known limitation of the search, not of
these defaults — would need a larger per-letter penalty in the
inner score to bite the gradient.

Frontend DEFAULT_PAINT_PARAMS mirror updated to match.
This commit is contained in:
Mitchell Hansen
2026-05-05 22:25:13 -07:00
parent 513d07228a
commit 50c8be46ee
2 changed files with 32 additions and 28 deletions

View File

@@ -63,20 +63,20 @@ export async function getStreamlineDebug(passIdx, hullIdx, params = DEFAULT_STRE
// Default PaintParams must match Rust's `impl Default for PaintParams`. // Default PaintParams must match Rust's `impl Default for PaintParams`.
export const DEFAULT_PAINT_PARAMS = { export const DEFAULT_PAINT_PARAMS = {
brush_radius_factor: 1.15, brush_radius_factor: 0.88,
brush_radius_offset_px: 0.25, brush_radius_offset_px: 0.50,
brush_radius_percentile: 0.85, brush_radius_percentile: 0.93,
step_size_factor: 0.40, step_size_factor: 0.40,
n_directions: 48, n_directions: 48,
lookahead_steps: 3, lookahead_steps: 3,
momentum_weight: 0.20, momentum_weight: 0.20,
overpaint_penalty: 0.10, overpaint_penalty: 0.10,
walk_bg_penalty: 4.0, walk_bg_penalty: 0.69,
min_score_factor: 0.20, min_score_factor: 0.20,
polish_iters: 1, polish_iters: 2,
polish_search_factor: 0.5, polish_search_factor: 0.5,
bg_penalty: 2.0, bg_penalty: 2.0,
min_component_factor: 1.20, min_component_factor: 1.49,
pen_lift_penalty: 0.0, pen_lift_penalty: 0.0,
pen_lift_reach: 3.0, pen_lift_reach: 3.0,
max_steps_per_stroke: 4000, max_steps_per_stroke: 4000,

View File

@@ -140,27 +140,26 @@ pub struct PaintParams {
impl Default for PaintParams { impl Default for PaintParams {
fn default() -> Self { fn default() -> Self {
Self { Self {
// OPTIMIZER-TUNED DEFAULTS (under hard 5%-bg / 5%-unpainted / // META-OPTIMIZER WINNING CONFIG (idx 20 of 24-sample lex-
// 2×-skel ceilings, score 333M → 41M). Bigger brush, // ranked search). Tier-1 result on the corpus: cov_fail=8,
// step_size_factor=0.4 to keep disks tightly packed, // bg_fail=0, len_fail=0; ~12 px bg per letter on average.
// walk_bg_penalty=4 to steer the walker onto the ridge, // Stroke-count constraints (12 single-stroke + 11 two-stroke
// n_directions=48 for finer turning resolution. Dijkstra // letters miscounted) are NOT respected — known limitation
// repaint still disabled — single-stroke W/M still need it // of the soft inner-score the meta search uses.
// turned on per-letter via the slider. brush_radius_factor: 0.88,
brush_radius_factor: 1.15, brush_radius_offset_px: 0.50,
brush_radius_offset_px: 0.25, brush_radius_percentile: 0.93,
brush_radius_percentile: 0.85,
step_size_factor: 0.40, step_size_factor: 0.40,
n_directions: 48, n_directions: 48,
lookahead_steps: 3, lookahead_steps: 3,
momentum_weight: 0.20, momentum_weight: 0.20,
overpaint_penalty: 0.10, overpaint_penalty: 0.10,
walk_bg_penalty: 4.0, walk_bg_penalty: 0.69,
min_score_factor: 0.20, min_score_factor: 0.20,
polish_iters: 1, polish_iters: 2,
polish_search_factor: 0.5, polish_search_factor: 0.5,
bg_penalty: 2.0, bg_penalty: 2.0,
min_component_factor: 1.20, min_component_factor: 1.49,
pen_lift_penalty: 0.0, pen_lift_penalty: 0.0,
pen_lift_reach: 3.0, pen_lift_reach: 3.0,
max_steps_per_stroke: 4000, max_steps_per_stroke: 4000,
@@ -1645,16 +1644,21 @@ impl Default for ScoreWeights {
// So the sweep prefers a smaller-radius solution that leaves a // So the sweep prefers a smaller-radius solution that leaves a
// few unpainted pixels over a larger-radius solution that paints // few unpainted pixels over a larger-radius solution that paints
// 50× as many bg pixels. // 50× as many bg pixels.
// Meta-optimizer winning weights (idx 20). Note: meta-opt
// didn't fix stroke-count constraint failures — those need a
// larger per-letter penalty in the inner score before they
// bite the gradient. Soft costs are well-tuned for the
// tier-1/tier-2 lex objective.
Self { Self {
stroke: 500.0, stroke: 844.0,
length: 5.0, length: 8.6,
bg: 50.0, bg: 98.0,
repaint: 30.0, repaint: 8.8,
unpainted: 50.0, // mild — density carries the weight unpainted: 70.0,
unpainted_density: 10.0, // cluster_size^1.5 × 10 unpainted_density: 22.8,
length_excess: 300.0, length_excess: 423.0,
curvature: 500.0, curvature: 515.0,
brush_size: 2000.0, // pressure toward bigger brush. Per brush_size: 214.0, // (was 2000 — meta dropped pressure
// letter, +1 px brush = +2000 bonus; // letter, +1 px brush = +2000 bonus;
// vs bg=50/px that's "worth" up to // vs bg=50/px that's "worth" up to
// ~40 extra bg pixels per letter. So // ~40 extra bg pixels per letter. So