Stroke and Curve gain `vertical_band: VerticalBandId`, the field GlyphObject has always carried. Curve's own doc comment used to call it "a *free* primitive (no vertical band, no spring slot)" -- but the projection computed each primitive's band, used it for the glyphs, and threw it away for the strokes and curves. The engraver then reconstructed it geometrically, and got it wrong twice: nearest glyph by x handed a lower-staff stem to the upper staff ( |
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| DECISIONS.md | ||
| README.md | ||
README.md
epiphany-engrave
Agent I's engraving constraint solver (spec Chapter 9): turns a
ConstrainedLayoutIR into a ResolvedLayoutIR with real geometry. It is the
production-side replacement for epiphany-layout-ir's interface-only
StubSolver; the two live in separate crates so the spec's core/product boundary
stays sharp.
Status: Minimal tier, with casting-off
Engraverruns a deterministic horizontal spacing pass (the first axis of the two-pass spring layout): each spring slot is placed left-to-right by a collision-aware advance derived from real glyph bearings.- A casting-off pass (Phase 3's layout track) then breaks the spaced line
into systems at measure boundaries (greedy first-fit against a
PageGeometry— default A4 portrait at an 8 mm staff), stacks systems vertically at the vertical-band model's inter-system gap, assigns pages by content height, and populates the realResolvedPage/ResolvedSystemtree. Every position is baked into a single y-up world frame (pages stacked vertically), so the SVG renderer and hit-testing consume the flat glyph/stroke lists unchanged. - The IR's declared constraints are evaluated — geometric families against
the pre-casting spaced frame, break constraints against the final break
structure (hard breaks are always honoured; a pathological soft break is
skipped and recorded as an
IrOverridedecision). Chosen breaks are recorded asEngravingDecisions withSynthesisKind::EngravedBreaktargets, attributed to the user override that requested them when one did. - It reports
SolverTier::Minimal: hard constraints (break family included) satisfied, no optimality claim — the quality-metric vector stays the honest all-worst placeholder until the Quality Metric Catalog lands.
Deferred: the vertical soft-spring solve, per-system justification/stretch,
optimal break search, widow/orphan control, and casting-off caching. See
DECISIONS.md.
use epiphany_engrave::Engraver;
use epiphany_layout_ir::{ConstraintSolver, SolverConfig};
let report = Engraver::default().solve(&constrained_ir, &SolverConfig::default());
assert!(report.satisfied_hard_constraints);
let resolved = report.layout; // real pages/systems; hand to epiphany-render-svg