121 lines
4.2 KiB
Rust
121 lines
4.2 KiB
Rust
//! Demo harness (NOT an application): render a fixture score to SVG on stdout.
|
|
//!
|
|
//! This is the "show the work" tool from the QUICKSTART (Agent I, "Demo binary
|
|
//! discipline"): it drives the full v0 pipeline
|
|
//! `Score → to_logical → to_constrained → solver → render` for a named fixture
|
|
//! and writes the SVG to stdout (stats + diagnostics to stderr). It is invaluable
|
|
//! for visual regression review and triage; it is not a viewer or a GUI host.
|
|
//!
|
|
//! Usage:
|
|
//!
|
|
//! ```text
|
|
//! cargo run -p epiphany-render-svg --example render_fixture -- \
|
|
//! ten_measure_single_staff [--solver=stub|real] [--seed=N] [--no-provenance] \
|
|
//! > out.svg
|
|
//! ```
|
|
//!
|
|
//! The `--solver` flag selects the interface-only stub (`stub`, the default this
|
|
//! phase) or Agent I's engrave solver (`real`); keeping the renderer working
|
|
//! against both is how a renderer-vs-solver bug is bisected with a one-flag
|
|
//! change (QUICKSTART, Agent I, "Development pattern").
|
|
|
|
use std::process::ExitCode;
|
|
|
|
use epiphany_engrave::Engraver;
|
|
use epiphany_layout_ir::{to_constrained, to_logical, ConstraintSolver, SolverConfig, StubSolver};
|
|
use epiphany_render_svg::{render, RenderOptions};
|
|
|
|
const FIXTURES: &str = "ten_measure_single_staff, valid_score_rich, valid_score";
|
|
const SOLVERS: &str = "stub, real";
|
|
|
|
fn main() -> ExitCode {
|
|
let mut fixture: Option<String> = None;
|
|
let mut solver = String::from("stub");
|
|
let mut seed: u64 = 0x000A_11CE;
|
|
let mut emit_provenance = true;
|
|
|
|
for arg in std::env::args().skip(1) {
|
|
if let Some(v) = arg.strip_prefix("--solver=") {
|
|
solver = v.to_owned();
|
|
} else if let Some(v) = arg.strip_prefix("--seed=") {
|
|
match v.parse() {
|
|
Ok(n) => seed = n,
|
|
Err(_) => return fail(&format!("invalid --seed value: {v}")),
|
|
}
|
|
} else if arg == "--no-provenance" {
|
|
emit_provenance = false;
|
|
} else if arg == "--help" || arg == "-h" {
|
|
usage();
|
|
return ExitCode::SUCCESS;
|
|
} else if arg.starts_with("--") {
|
|
return fail(&format!("unknown flag: {arg}"));
|
|
} else if fixture.is_none() {
|
|
fixture = Some(arg);
|
|
} else {
|
|
return fail(&format!("unexpected argument: {arg}"));
|
|
}
|
|
}
|
|
|
|
let Some(fixture) = fixture else {
|
|
usage();
|
|
return fail("a fixture name is required");
|
|
};
|
|
|
|
let score = match fixture.as_str() {
|
|
"ten_measure_single_staff" => epiphany_testkit::fixtures::ten_measure_single_staff(seed),
|
|
"valid_score_rich" => epiphany_core::generators::valid_score_rich(seed),
|
|
"valid_score" => epiphany_core::generators::valid_score(seed),
|
|
other => return fail(&format!("unknown fixture {other:?}; known: {FIXTURES}")),
|
|
};
|
|
|
|
let constrained = to_constrained(&to_logical(&score));
|
|
let config = SolverConfig::default();
|
|
let (report, tier) = match solver.as_str() {
|
|
"stub" => (StubSolver.solve(&constrained, &config), StubSolver.tier()),
|
|
"real" => (Engraver.solve(&constrained, &config), Engraver.tier()),
|
|
other => return fail(&format!("unknown solver {other:?}; known: {SOLVERS}")),
|
|
};
|
|
|
|
let out = render(
|
|
&report.layout,
|
|
&RenderOptions {
|
|
emit_provenance,
|
|
..RenderOptions::default()
|
|
},
|
|
);
|
|
|
|
eprintln!(
|
|
"fixture={fixture} seed={seed:#x} solver={solver} tier={tier:?} status={:?}",
|
|
report.status
|
|
);
|
|
eprintln!(
|
|
"glyphs={} paths={} fallback_rects={} provenance={} layers={} hard_constraints={} well_formed={}",
|
|
out.stats.glyph_count,
|
|
out.stats.path_count,
|
|
out.stats.fallback_rect_count,
|
|
out.stats.provenance_count,
|
|
out.stats.layer_count,
|
|
constrained.constraints.len(),
|
|
out.is_well_formed(),
|
|
);
|
|
for d in &out.diagnostics {
|
|
eprintln!("diagnostic: {} (glyph: {:?})", d.message, d.glyph);
|
|
}
|
|
|
|
print!("{}", out.svg);
|
|
ExitCode::SUCCESS
|
|
}
|
|
|
|
fn usage() {
|
|
eprintln!(
|
|
"usage: render_fixture <fixture> [--solver=stub|real] [--seed=N] [--no-provenance]\n\
|
|
fixtures: {FIXTURES}\n\
|
|
solvers: {SOLVERS} (default: stub)"
|
|
);
|
|
}
|
|
|
|
fn fail(message: &str) -> ExitCode {
|
|
eprintln!("error: {message}");
|
|
ExitCode::from(2)
|
|
}
|