Close the inter-staff residual risk: a three-staff cascade fixture

The reviewer signed off on the inter-staff slice with one named residual risk:
the 3+-staff cumulative shift cascade was documented and traced correct but had
no fixture behind it -- valid_score_rich's "three staves" are three separate
single-staff regions, so each lands in its own system and the cascade never runs.

three_staff_close_content puts three staves in ONE region with deliberately
asymmetric pressure: the upper pair collides hard (C1 against C7), the lower pair
only gently. That asymmetry is what makes the fixture discriminating. Sizing each
pair independently -- the plausible wrong implementation -- measures the lower
pair against the middle staff's ORIGINAL position, hands the bottom staff only
its own small correction, and drags it back up through the middle staff.

Verified by mutation, not by assertion alone: with the cascade removed the bottom
staff's shift collapses from 34.68 to 4.56 against the middle staff's 15.06, and
both the shift ordering and the staff-line-gap assertions fail. two_staff_close_content
still passes under that same mutation, which is precisely why the new fixture was
needed.

Writing the test also corrected a wrong mental model, now recorded in DECISIONS.md:
a shift INCREMENT generally exceeds the lower pair's own raw correction, because
the upper staff's descent has itself eaten into that pair's gap and must be undone.
The first version of the test asserted the opposite and failed against a correct
solve.

The fixture additionally pins curve attribution against a three-band choice -- the
slur must still find the bottom staff, not merely the nearer of two -- and carries
its own render golden. No existing golden churns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-07-09 10:20:35 -04:00
parent b1bfe04d6c
commit f7350362a3
8 changed files with 487 additions and 4 deletions

View File

@ -772,10 +772,7 @@ under a staff space of margin).
rather than from the region's *declared* inter-staff band. Harmless while both
come from the same constructor; it would silently diverge from the metric the
day per-region gaps become customizable.
- The cumulative shift **cascades** correctly for 3+ staves (staff *i* carries the
sum of every gap correction above it), but is unexercised: `valid_score_rich`'s
three staves are three separate single-staff regions, so each lands in its own
system.
The 3+-staff cascade was the last of these to be closed; see below.
**The solve.** Per system, per staff, the real content y-extent is collected
(glyphs, strokes, curves — ledgers and slurs included, not just noteheads). The
@ -800,3 +797,25 @@ render golden (the visible before/after: slice 1 tight, slice 2 separated).
only expands, never pulls staves together — the fixed pitch is generous by
default, so this is rarely wanted); per-staff spring *stretch* to fill spare
system height (the inter-system justification carries the fill for now).
**The cascade, and why it grows faster than the raw corrections.** A pair's gap
is measured against the upper staff's **already shifted** bottom, so staff *i*'s
shift is the sum of every correction above it plus its own. A consequence worth
stating because it is counter-intuitive: an *increment* `shift(i+1) - shift(i)`
generally EXCEEDS the lower pair's own raw correction, because the upper staff's
descent has itself eaten into that pair's gap and must be undone. (The first
version of the cascade test asserted the opposite — that a gently-pressed lower
pair's increment would be the small one — and failed against a correct solve.)
Locked by `inter_staff_shifts_cascade_down_three_staves` over the new
`three_staff_close_content` fixture: three staves in ONE region (so all three
land in one system), with **asymmetric** pressure — the upper pair collides hard
(C1 against C7), the lower pair only gently. Sizing each pair independently — the
plausible wrong implementation — measures the lower pair against the middle
staff's ORIGINAL position and hands the bottom staff only its small raw
correction, dragging it back up through the middle staff. **Verified by
mutation:** with the cascade removed the bottom staff's shift collapses from
34.68 to 4.56 (against the middle staff's 15.06) and both the shift ordering and
the staff-line-gap assertions fail — while `two_staff_close_content` still
passes, which is exactly why the three-staff fixture was needed. The fixture also
pins curve attribution against a THREE-band choice (the slur must still find the
bottom staff, not merely the nearer of two) and carries its own render golden.

View File

@ -2383,6 +2383,91 @@ mod tests {
);
}
/// Three staves in one system, with deliberately ASYMMETRIC pressure: the
/// upper pair collides hard (C1 against C7), the lower pair only gently. A
/// staff's shift must accumulate the corrections of every pair above it,
/// because each pair's gap is measured against the upper staff's *already
/// shifted* position. So the bottom staff's shift is large — it must clear
/// the middle staff where the middle staff now sits, not where it started.
///
/// That is exactly what a solve sizing each pair independently gets wrong:
/// it would measure the lower pair against the middle staff's ORIGINAL
/// position, hand the bottom staff only its own small correction, and leave
/// it above the middle staff's new position — closing their staff-line gap
/// to well under the fixed pitch. Verified by mutation: with the cascade
/// removed this test fails on both `s2 > s1` and the staff-line gap.
#[test]
fn inter_staff_shifts_cascade_down_three_staves() {
use epiphany_layout_ir::{to_constrained, to_logical};
let report = Engraver::default().solve(
&to_constrained(&to_logical(
&epiphany_testkit::fixtures::three_staff_close_content(1),
)),
&SolverConfig::default(),
);
let sys = &report.layout.pages[0].systems[0];
assert_eq!(sys.staves.len(), 3, "three staves in ONE system");
// Staff records are top-first. Recover each staff's solved shift: under
// fixed stacking staff `i` sits `i * SYSTEM_STAFF_PITCH` below the top,
// so whatever separation exceeds that is the shift the solve added.
let top_y = sys.staves[0].bounding_box.origin.y.0;
let shift = |i: usize| (top_y - sys.staves[i].bounding_box.origin.y.0) - 12.0 * i as f32;
let (s0, s1, s2) = (shift(0), shift(1), shift(2));
assert!(s0.abs() < 1e-3, "the top staff anchors the system: {s0}");
assert!(s1 > 0.0, "the hard-colliding upper pair separates: {s1}");
// The cascade. The bottom staff strictly outruns the middle staff: its
// correction is measured against the middle staff's SHIFTED bottom, so
// it inherits that descent and adds its own clearance on top. Sizing the
// lower pair independently yields only its own small raw correction —
// less than `s1` for this fixture, which the strict inequality rejects.
assert!(
s2 > s1,
"the bottom staff inherits the shift above it: s1={s1} s2={s2}"
);
// Every adjacent pair ends up clear of the fixed pitch, and nothing
// collides — the invariant a broken cascade destroys for the lower pair.
for i in 0..2 {
let (upper, lower) = (&sys.staves[i].bounding_box, &sys.staves[i + 1].bounding_box);
let gap = upper.origin.y.0 - (lower.origin.y.0 + lower.size.height.0);
assert!(
gap > 12.0,
"staves {i}/{} separate: staff-line gap {gap}",
i + 1
);
}
assert_eq!(
report.metric_vector.collision_penalty.0, 0.0,
"the cascaded staves collide nowhere"
);
// The slur belongs to the BOTTOM staff, the one carrying the largest
// cascaded shift — so curve attribution has to survive a THREE-band
// choice, not just pick the nearer of two. Misattributing it to the
// middle staff would leave it ~`s2 - s1` above its own notes.
assert_eq!(report.layout.curves.len(), 1, "the fixture draws one slur");
let p0y = report.layout.curves[0].p0.y.0;
let band_gap = |b: &epiphany_layout_ir::Rect| {
let (lo, hi) = (b.origin.y.0, b.origin.y.0 + b.size.height.0);
if p0y < lo {
lo - p0y
} else if p0y > hi {
p0y - hi
} else {
0.0
}
};
let gaps: Vec<f32> = (0..3)
.map(|i| band_gap(&sys.staves[i].bounding_box))
.collect();
assert!(
gaps[2] < gaps[1] && gaps[2] < gaps[0],
"the slur rides its OWN (bottom) staff: {gaps:?}"
);
assert!(gaps[2] < 2.0, "and sits close against it: {}", gaps[2]);
}
#[test]
fn vertical_justification_fills_non_final_pages() {
use epiphany_layout_ir::{Margins, Size2D, StaffSpace};

View File

@ -49,6 +49,10 @@ fn fixtures() -> Vec<(&'static str, Score)> {
"two_staff_close_content",
epiphany_testkit::fixtures::two_staff_close_content(0x000A_11CE),
),
(
"three_staff_close_content",
epiphany_testkit::fixtures::three_staff_close_content(0x000A_11CE),
),
]
}

View File

@ -0,0 +1,15 @@
fixture=three_staff_close_content solver=engrave
glyph_count=18
path_count=18
fallback_rect_count=0
stroke_count=77
curve_count=1
provenance_count=96
layer_count=1
hard_constraint_count=27
xml_well_formed=true
view_box=[5.5 -75.204575 17.379084 69.703995]
class_counts:
barline=3
clef=3
notehead=12

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="173.7908" height="697.0399" viewBox="0 0 17.3791 69.704">
<!-- epiphany-render-svg: glyphs are genuine Bravura SMuFL outlines inlined as paths; geometry is the resolved layout verbatim, no engraving performed here; every glyph, stroke, and curve carries a data-prov trace to its score-graph source -->
<g transform="translate(-5.5 -5.5006) scale(1 -1)">
<g data-layer="0">
<line x1="8.6599" y1="-12.8926" x2="8.6599" y2="-12.8926" stroke="#000000" stroke-width="0" data-prov="cab69c17ed41c68ac4c9d9fd2585f7b8" data-source-kind="6" data-kind="stroke"/>
<line x1="7.565" y1="-12.8926" x2="20.2543" y2="-12.8926" stroke="#000000" stroke-width="0.13" data-prov="53de5472c3073e1e2fabab582e7a6a2a" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-11.8926" x2="20.2543" y2="-11.8926" stroke="#000000" stroke-width="0.13" data-prov="af9e9ab8cff6e189b2b70311d333c4ab" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-10.8926" x2="20.2543" y2="-10.8926" stroke="#000000" stroke-width="0.13" data-prov="2298edab2b482b968611b1dfd4c4184a" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-9.8926" x2="20.2543" y2="-9.8926" stroke="#000000" stroke-width="0.13" data-prov="4688e9bd99908438eb2cfa44aa9b69f7" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-8.8926" x2="20.2543" y2="-8.8926" stroke="#000000" stroke-width="0.13" data-prov="9129d5d4f57c3cc9a6f2761bc4da188d" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-39.9526" x2="20.2543" y2="-39.9526" stroke="#000000" stroke-width="0.13" data-prov="f6aa19fc306fddc0ee6ffe5815adea15" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-38.9526" x2="20.2543" y2="-38.9526" stroke="#000000" stroke-width="0.13" data-prov="cf7f37a324c02c59d88ed58b2d6eff09" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-37.9526" x2="20.2543" y2="-37.9526" stroke="#000000" stroke-width="0.13" data-prov="eed90e04088a49096ff2df61528076f3" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-36.9526" x2="20.2543" y2="-36.9526" stroke="#000000" stroke-width="0.13" data-prov="eeba57d92a18b4e508a773d735498540" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-35.9526" x2="20.2543" y2="-35.9526" stroke="#000000" stroke-width="0.13" data-prov="7961fdc38d6911be6ccafdd0d4b0e650" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-71.5726" x2="20.2543" y2="-71.5726" stroke="#000000" stroke-width="0.13" data-prov="48e35b5f0c754fb61417abb3162cd139" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-70.5726" x2="20.2543" y2="-70.5726" stroke="#000000" stroke-width="0.13" data-prov="d1649442bdf17a90fad13e068be1d468" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-69.5726" x2="20.2543" y2="-69.5726" stroke="#000000" stroke-width="0.13" data-prov="a63a879181f96b3d9282817ce4854fa8" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-68.5726" x2="20.2543" y2="-68.5726" stroke="#000000" stroke-width="0.13" data-prov="407ae799175a7a5107f9b17fae444ea1" data-source-kind="3" data-kind="stroke"/>
<line x1="7.565" y1="-67.5726" x2="20.2543" y2="-67.5726" stroke="#000000" stroke-width="0.13" data-prov="213d70c1d0c044450c85f49532775c9e" data-source-kind="3" data-kind="stroke"/>
<line x1="8.6599" y1="-12.8926" x2="8.6599" y2="-12.8926" stroke="#000000" stroke-width="0" data-prov="ce07aadf437a74ccac9fc50e9d4d21a6" data-source-kind="2" data-kind="stroke"/>
<line x1="13.0944" y1="-13.8926" x2="13.0944" y2="-10.3926" stroke="#000000" stroke-width="0.12" data-prov="1c2dad5590859e5faf984977c99a71b5" data-source-kind="0" data-kind="stroke"/>
<line x1="11.6444" y1="-13.8926" x2="13.4251" y2="-13.8926" stroke="#000000" stroke-width="0.13" data-prov="411616f3ac077092b76181619405cb3c" data-source-kind="1" data-kind="stroke"/>
<line x1="15.1751" y1="-17.3926" x2="15.1751" y2="-13.8926" stroke="#000000" stroke-width="0.12" data-prov="051d981decadda77a3146e86c38a45c5" data-source-kind="0" data-kind="stroke"/>
<line x1="13.7251" y1="-13.8926" x2="15.5058" y2="-13.8926" stroke="#000000" stroke-width="0.13" data-prov="64b25a9451c9c4c2ebaf1971ff62ca28" data-source-kind="1" data-kind="stroke"/>
<line x1="13.7251" y1="-14.8926" x2="15.5058" y2="-14.8926" stroke="#000000" stroke-width="0.13" data-prov="fcefb1c81f1ffcbb0c6a249a51faa480" data-source-kind="1" data-kind="stroke"/>
<line x1="13.7251" y1="-15.8926" x2="15.5058" y2="-15.8926" stroke="#000000" stroke-width="0.13" data-prov="f75a4800bb0d3634144a515ddeb63099" data-source-kind="1" data-kind="stroke"/>
<line x1="13.7251" y1="-16.8926" x2="15.5058" y2="-16.8926" stroke="#000000" stroke-width="0.13" data-prov="58afb85138289ff2586446383df8e27c" data-source-kind="1" data-kind="stroke"/>
<line x1="17.2558" y1="-20.8926" x2="17.2558" y2="-17.3926" stroke="#000000" stroke-width="0.12" data-prov="1a8a05c715ac178f13b3ede06a6e0247" data-source-kind="0" data-kind="stroke"/>
<line x1="15.8058" y1="-13.8926" x2="17.5864" y2="-13.8926" stroke="#000000" stroke-width="0.13" data-prov="52d557448dd11b6701d6d99d59427241" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-14.8926" x2="17.5864" y2="-14.8926" stroke="#000000" stroke-width="0.13" data-prov="7449ac621352223bd08844a0968b0ca4" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-15.8926" x2="17.5864" y2="-15.8926" stroke="#000000" stroke-width="0.13" data-prov="e7e3b71288255ba6d01ce135782f6a7a" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-16.8926" x2="17.5864" y2="-16.8926" stroke="#000000" stroke-width="0.13" data-prov="9fa4bd9211c697d31a922b1f182d80dd" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-17.8926" x2="17.5864" y2="-17.8926" stroke="#000000" stroke-width="0.13" data-prov="d4adbb2db3a83dcf0ad969763b19c8ee" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-18.8926" x2="17.5864" y2="-18.8926" stroke="#000000" stroke-width="0.13" data-prov="9ffeb9f08bff743cf9cbd70dd373b4e4" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-19.8926" x2="17.5864" y2="-19.8926" stroke="#000000" stroke-width="0.13" data-prov="6d34ca5eed6359ef4a7308ee9fd47944" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-20.8926" x2="17.5864" y2="-20.8926" stroke="#000000" stroke-width="0.13" data-prov="b7e0c1d5273bcdf8c156e36bce721a2e" data-source-kind="1" data-kind="stroke"/>
<line x1="19.3364" y1="-24.3926" x2="19.3364" y2="-20.8926" stroke="#000000" stroke-width="0.12" data-prov="48b9108504e0ae37ce8fb9b4bc54f1ba" data-source-kind="0" data-kind="stroke"/>
<line x1="17.8864" y1="-13.8926" x2="19.6671" y2="-13.8926" stroke="#000000" stroke-width="0.13" data-prov="84b60a528ad68bfc9e8bfc0e782d12e2" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-14.8926" x2="19.6671" y2="-14.8926" stroke="#000000" stroke-width="0.13" data-prov="0171d3d7b3cdf2edc122e86785927703" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-15.8926" x2="19.6671" y2="-15.8926" stroke="#000000" stroke-width="0.13" data-prov="502c590d7d2a32b774784c452305e963" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-16.8926" x2="19.6671" y2="-16.8926" stroke="#000000" stroke-width="0.13" data-prov="fae4e983fb4a6dbb4a6cbd34947162ba" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-17.8926" x2="19.6671" y2="-17.8926" stroke="#000000" stroke-width="0.13" data-prov="e19fefcdd1c1b6cbc3fa9f4e7f7ed21e" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-18.8926" x2="19.6671" y2="-18.8926" stroke="#000000" stroke-width="0.13" data-prov="738c2046094a373ec3aa5acf7d9865f9" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-19.8926" x2="19.6671" y2="-19.8926" stroke="#000000" stroke-width="0.13" data-prov="a799f1e4cce19534a24aa29739206ec0" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-20.8926" x2="19.6671" y2="-20.8926" stroke="#000000" stroke-width="0.13" data-prov="7aca9e5f8c50128e6618cc31072bf923" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-21.8926" x2="19.6671" y2="-21.8926" stroke="#000000" stroke-width="0.13" data-prov="77ec8ac817b9921221350030998f20cf" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-22.8926" x2="19.6671" y2="-22.8926" stroke="#000000" stroke-width="0.13" data-prov="5315bb47b55a85a9f4bf997263674f8b" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-23.8926" x2="19.6671" y2="-23.8926" stroke="#000000" stroke-width="0.13" data-prov="89ad40af92c1fba61fa859789484f042" data-source-kind="1" data-kind="stroke"/>
<line x1="8.6599" y1="-39.9526" x2="8.6599" y2="-39.9526" stroke="#000000" stroke-width="0" data-prov="54fe6da1801e4fce8316c678f29cacdf" data-source-kind="2" data-kind="stroke"/>
<line x1="13.0944" y1="-30.4526" x2="13.0944" y2="-26.9526" stroke="#000000" stroke-width="0.12" data-prov="952f3712f8f29866ac797bbea256d2d0" data-source-kind="0" data-kind="stroke"/>
<line x1="11.6444" y1="-34.9526" x2="13.4251" y2="-34.9526" stroke="#000000" stroke-width="0.13" data-prov="837d109efb37f74957a713aba3123810" data-source-kind="1" data-kind="stroke"/>
<line x1="11.6444" y1="-33.9526" x2="13.4251" y2="-33.9526" stroke="#000000" stroke-width="0.13" data-prov="4b888b04616f17f2f3b39f919a4d1f73" data-source-kind="1" data-kind="stroke"/>
<line x1="11.6444" y1="-32.9526" x2="13.4251" y2="-32.9526" stroke="#000000" stroke-width="0.13" data-prov="bb3c4f1c88311a08545a507484b65ec5" data-source-kind="1" data-kind="stroke"/>
<line x1="11.6444" y1="-31.9526" x2="13.4251" y2="-31.9526" stroke="#000000" stroke-width="0.13" data-prov="7bb81a275725608c136691c9bdc2bcdc" data-source-kind="1" data-kind="stroke"/>
<line x1="11.6444" y1="-30.9526" x2="13.4251" y2="-30.9526" stroke="#000000" stroke-width="0.13" data-prov="048e69987982c24dcfc601c985d4de2d" data-source-kind="1" data-kind="stroke"/>
<line x1="15.1751" y1="-44.4526" x2="15.1751" y2="-40.9526" stroke="#000000" stroke-width="0.12" data-prov="c7b2ab7fbc11b592cb08478feb22b263" data-source-kind="0" data-kind="stroke"/>
<line x1="13.7251" y1="-40.9526" x2="15.5058" y2="-40.9526" stroke="#000000" stroke-width="0.13" data-prov="041a0efe1178a5a9ece7f6bae256cb49" data-source-kind="1" data-kind="stroke"/>
<line x1="13.7251" y1="-41.9526" x2="15.5058" y2="-41.9526" stroke="#000000" stroke-width="0.13" data-prov="d9bb40761053cbb2e56baa1d226c7354" data-source-kind="1" data-kind="stroke"/>
<line x1="13.7251" y1="-42.9526" x2="15.5058" y2="-42.9526" stroke="#000000" stroke-width="0.13" data-prov="1402804aba9f279387d6f183f80d79b4" data-source-kind="1" data-kind="stroke"/>
<line x1="13.7251" y1="-43.9526" x2="15.5058" y2="-43.9526" stroke="#000000" stroke-width="0.13" data-prov="6d52f1fb3cdd40d6274abeb906770ef1" data-source-kind="1" data-kind="stroke"/>
<line x1="17.2558" y1="-30.4526" x2="17.2558" y2="-26.9526" stroke="#000000" stroke-width="0.12" data-prov="5f9c5d54f7ab283f189cea904cb9bb42" data-source-kind="0" data-kind="stroke"/>
<line x1="15.8058" y1="-34.9526" x2="17.5864" y2="-34.9526" stroke="#000000" stroke-width="0.13" data-prov="755af675fea2b73be02f98d5ce681561" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-33.9526" x2="17.5864" y2="-33.9526" stroke="#000000" stroke-width="0.13" data-prov="729c3d20e1cf7fe25b2e9a4e5d72d5eb" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-32.9526" x2="17.5864" y2="-32.9526" stroke="#000000" stroke-width="0.13" data-prov="0f3c5819991c4d887c5e26bd166cf6fb" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-31.9526" x2="17.5864" y2="-31.9526" stroke="#000000" stroke-width="0.13" data-prov="c52e73fbf914910d9dedebd4ce938f16" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-30.9526" x2="17.5864" y2="-30.9526" stroke="#000000" stroke-width="0.13" data-prov="78e39176f8952b71f69bef9a4436e4a1" data-source-kind="1" data-kind="stroke"/>
<line x1="19.3364" y1="-44.4526" x2="19.3364" y2="-40.9526" stroke="#000000" stroke-width="0.12" data-prov="c47796a73dccf69155b4078d112fde27" data-source-kind="0" data-kind="stroke"/>
<line x1="17.8864" y1="-40.9526" x2="19.6671" y2="-40.9526" stroke="#000000" stroke-width="0.13" data-prov="2a36935e840d6ef047c51d1d7a82ed93" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-41.9526" x2="19.6671" y2="-41.9526" stroke="#000000" stroke-width="0.13" data-prov="79b311a1708aaab4af813b7b959c6d45" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-42.9526" x2="19.6671" y2="-42.9526" stroke="#000000" stroke-width="0.13" data-prov="290ec68875364ec6a4a96dba4456fa60" data-source-kind="1" data-kind="stroke"/>
<line x1="17.8864" y1="-43.9526" x2="19.6671" y2="-43.9526" stroke="#000000" stroke-width="0.13" data-prov="b4222559838ef95fbb9829f715732d00" data-source-kind="1" data-kind="stroke"/>
<line x1="8.6599" y1="-71.5726" x2="8.6599" y2="-71.5726" stroke="#000000" stroke-width="0" data-prov="80230a27e89abbdc2803f4ccae35620d" data-source-kind="2" data-kind="stroke"/>
<line x1="13.0944" y1="-65.5726" x2="13.0944" y2="-62.0726" stroke="#000000" stroke-width="0.12" data-prov="12b12b407f09c2ce7151e271b80b4575" data-source-kind="0" data-kind="stroke"/>
<line x1="11.6444" y1="-66.5726" x2="13.4251" y2="-66.5726" stroke="#000000" stroke-width="0.13" data-prov="02c8f971668afe306c2b63145b0eda5e" data-source-kind="1" data-kind="stroke"/>
<line x1="11.6444" y1="-65.5726" x2="13.4251" y2="-65.5726" stroke="#000000" stroke-width="0.13" data-prov="ad9922ca450855f46c76ced4da8d3eba" data-source-kind="1" data-kind="stroke"/>
<line x1="15.1751" y1="-69.0726" x2="15.1751" y2="-65.5726" stroke="#000000" stroke-width="0.12" data-prov="a6be2c1fdd05ec3c34b4655d0c7f7b7d" data-source-kind="0" data-kind="stroke"/>
<line x1="17.2558" y1="-65.5726" x2="17.2558" y2="-62.0726" stroke="#000000" stroke-width="0.12" data-prov="e596a52ab94676af6dd2afb8839bf6f7" data-source-kind="0" data-kind="stroke"/>
<line x1="15.8058" y1="-66.5726" x2="17.5864" y2="-66.5726" stroke="#000000" stroke-width="0.13" data-prov="373157f721d0b1004b19cc474cd1653c" data-source-kind="1" data-kind="stroke"/>
<line x1="15.8058" y1="-65.5726" x2="17.5864" y2="-65.5726" stroke="#000000" stroke-width="0.13" data-prov="01ac7b54f5668ab8da8500c7bf12dd08" data-source-kind="1" data-kind="stroke"/>
<line x1="19.3364" y1="-69.0726" x2="19.3364" y2="-65.5726" stroke="#000000" stroke-width="0.12" data-prov="b76cf9581fb59757345a09d2eba68529" data-source-kind="0" data-kind="stroke"/>
<path d="M 12.7247 -66.8726 C 13.5916 -65.8059 14.4586 -65.8059 15.3255 -66.8726" fill="none" stroke="#000000" stroke-width="0.12" data-prov="1c938c5c0f8fd1c1397fca33e64be814" data-source-kind="11" data-kind="curve"/>
<path d="M1.504 1.66C1.496 1.708 1.504 1.712 1.528 1.736C1.96 2.14 2.288 2.648 2.288 3.26C2.288 3.608 2.192 3.952 2.028 4.192C1.968 4.28 1.864 4.392 1.82 4.392C1.764 4.392 1.64 4.288 1.56 4.2C1.264 3.872 1.168 3.372 1.168 2.956C1.168 2.724 1.196 2.464 1.224 2.3C1.232 2.252 1.236 2.244 1.188 2.204C0.612 1.728 0 1.156 0 0.348C0 -0.348 0.476 -1.008 1.456 -1.008C1.548 -1.008 1.652 -1 1.732 -0.984C1.776 -0.976 1.784 -0.972 1.792 -1.02C1.84 -1.288 1.9 -1.636 1.9 -1.824C1.9 -2.416 1.5 -2.488 1.264 -2.488C1.048 -2.488 0.944 -2.424 0.944 -2.372C0.944 -2.344 0.98 -2.332 1.072 -2.304C1.196 -2.268 1.34 -2.16 1.34 -1.928C1.34 -1.708 1.2 -1.52 0.956 -1.52C0.688 -1.52 0.528 -1.732 0.528 -1.98C0.528 -2.24 0.684 -2.632 1.288 -2.632C1.556 -2.632 2.076 -2.512 2.076 -1.832C2.076 -1.604 2.004 -1.224 1.96 -0.976C1.952 -0.928 1.956 -0.932 2.012 -0.908C2.416 -0.748 2.684 -0.408 2.684 0.044C2.684 0.556 2.308 1.008 1.72 1.008C1.616 1.008 1.616 1.008 1.604 1.08ZM1.88 3.772C2.012 3.772 2.12 3.664 2.12 3.444C2.12 3 1.74 2.64 1.424 2.364C1.396 2.34 1.38 2.344 1.372 2.396C1.356 2.5 1.348 2.636 1.348 2.764C1.348 3.388 1.636 3.772 1.88 3.772ZM1.444 1.048C1.456 0.972 1.456 0.976 1.384 0.952C1.032 0.832 0.804 0.516 0.804 0.176C0.804 -0.184 0.992 -0.44 1.264 -0.532C1.296 -0.544 1.344 -0.556 1.372 -0.556C1.404 -0.556 1.42 -0.536 1.42 -0.512C1.42 -0.484 1.388 -0.472 1.36 -0.46C1.192 -0.388 1.072 -0.216 1.072 -0.032C1.072 0.196 1.228 0.368 1.472 0.436C1.536 0.452 1.544 0.448 1.552 0.404L1.752 -0.788C1.76 -0.832 1.756 -0.832 1.696 -0.844C1.632 -0.856 1.552 -0.864 1.472 -0.864C0.772 -0.864 0.32 -0.476 0.32 0.08C0.32 0.316 0.36 0.632 0.692 1.008C0.932 1.276 1.116 1.424 1.304 1.576C1.344 1.608 1.352 1.604 1.36 1.56ZM1.72 0.412C1.712 0.46 1.716 0.472 1.764 0.468C2.088 0.44 2.356 0.168 2.356 -0.184C2.356 -0.436 2.204 -0.64 1.98 -0.752C1.932 -0.776 1.924 -0.776 1.916 -0.728Z" transform="translate(8.6599 -11.8926)" fill="#000000" data-prov="e39c94e4224c7060995ea50ab3f1a891" data-source-kind="4" data-glyph="gClef" data-class="clef"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(11.9444 -13.8926)" fill="#000000" data-prov="2489cab25ad1c58249798a96c93a19df" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(14.0251 -17.3926)" fill="#000000" data-prov="d25c7d3368d839c8286dd224bf53b46b" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(16.1058 -20.8926)" fill="#000000" data-prov="02b4ad76298971279a4f0f908ab1a999" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(18.1864 -24.3926)" fill="#000000" data-prov="02a400a2dc076ba0d606e0437828d1fb" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M1.504 1.66C1.496 1.708 1.504 1.712 1.528 1.736C1.96 2.14 2.288 2.648 2.288 3.26C2.288 3.608 2.192 3.952 2.028 4.192C1.968 4.28 1.864 4.392 1.82 4.392C1.764 4.392 1.64 4.288 1.56 4.2C1.264 3.872 1.168 3.372 1.168 2.956C1.168 2.724 1.196 2.464 1.224 2.3C1.232 2.252 1.236 2.244 1.188 2.204C0.612 1.728 0 1.156 0 0.348C0 -0.348 0.476 -1.008 1.456 -1.008C1.548 -1.008 1.652 -1 1.732 -0.984C1.776 -0.976 1.784 -0.972 1.792 -1.02C1.84 -1.288 1.9 -1.636 1.9 -1.824C1.9 -2.416 1.5 -2.488 1.264 -2.488C1.048 -2.488 0.944 -2.424 0.944 -2.372C0.944 -2.344 0.98 -2.332 1.072 -2.304C1.196 -2.268 1.34 -2.16 1.34 -1.928C1.34 -1.708 1.2 -1.52 0.956 -1.52C0.688 -1.52 0.528 -1.732 0.528 -1.98C0.528 -2.24 0.684 -2.632 1.288 -2.632C1.556 -2.632 2.076 -2.512 2.076 -1.832C2.076 -1.604 2.004 -1.224 1.96 -0.976C1.952 -0.928 1.956 -0.932 2.012 -0.908C2.416 -0.748 2.684 -0.408 2.684 0.044C2.684 0.556 2.308 1.008 1.72 1.008C1.616 1.008 1.616 1.008 1.604 1.08ZM1.88 3.772C2.012 3.772 2.12 3.664 2.12 3.444C2.12 3 1.74 2.64 1.424 2.364C1.396 2.34 1.38 2.344 1.372 2.396C1.356 2.5 1.348 2.636 1.348 2.764C1.348 3.388 1.636 3.772 1.88 3.772ZM1.444 1.048C1.456 0.972 1.456 0.976 1.384 0.952C1.032 0.832 0.804 0.516 0.804 0.176C0.804 -0.184 0.992 -0.44 1.264 -0.532C1.296 -0.544 1.344 -0.556 1.372 -0.556C1.404 -0.556 1.42 -0.536 1.42 -0.512C1.42 -0.484 1.388 -0.472 1.36 -0.46C1.192 -0.388 1.072 -0.216 1.072 -0.032C1.072 0.196 1.228 0.368 1.472 0.436C1.536 0.452 1.544 0.448 1.552 0.404L1.752 -0.788C1.76 -0.832 1.756 -0.832 1.696 -0.844C1.632 -0.856 1.552 -0.864 1.472 -0.864C0.772 -0.864 0.32 -0.476 0.32 0.08C0.32 0.316 0.36 0.632 0.692 1.008C0.932 1.276 1.116 1.424 1.304 1.576C1.344 1.608 1.352 1.604 1.36 1.56ZM1.72 0.412C1.712 0.46 1.716 0.472 1.764 0.468C2.088 0.44 2.356 0.168 2.356 -0.184C2.356 -0.436 2.204 -0.64 1.98 -0.752C1.932 -0.776 1.924 -0.776 1.916 -0.728Z" transform="translate(8.6599 -38.9526)" fill="#000000" data-prov="57ece9af76f53869ab2df3c844cbb990" data-source-kind="4" data-glyph="gClef" data-class="clef"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(11.9444 -30.4526)" fill="#000000" data-prov="a29e010753de6d915fea6b92d42d2a6b" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(14.0251 -44.4526)" fill="#000000" data-prov="edd00864090a7fbfeb86d9fc66d5fdfc" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(16.1058 -30.4526)" fill="#000000" data-prov="7a77efa323697b85c6e570b201b40373" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(18.1864 -44.4526)" fill="#000000" data-prov="5df0b734253de82c2360eb30508e2ccd" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M1.504 1.66C1.496 1.708 1.504 1.712 1.528 1.736C1.96 2.14 2.288 2.648 2.288 3.26C2.288 3.608 2.192 3.952 2.028 4.192C1.968 4.28 1.864 4.392 1.82 4.392C1.764 4.392 1.64 4.288 1.56 4.2C1.264 3.872 1.168 3.372 1.168 2.956C1.168 2.724 1.196 2.464 1.224 2.3C1.232 2.252 1.236 2.244 1.188 2.204C0.612 1.728 0 1.156 0 0.348C0 -0.348 0.476 -1.008 1.456 -1.008C1.548 -1.008 1.652 -1 1.732 -0.984C1.776 -0.976 1.784 -0.972 1.792 -1.02C1.84 -1.288 1.9 -1.636 1.9 -1.824C1.9 -2.416 1.5 -2.488 1.264 -2.488C1.048 -2.488 0.944 -2.424 0.944 -2.372C0.944 -2.344 0.98 -2.332 1.072 -2.304C1.196 -2.268 1.34 -2.16 1.34 -1.928C1.34 -1.708 1.2 -1.52 0.956 -1.52C0.688 -1.52 0.528 -1.732 0.528 -1.98C0.528 -2.24 0.684 -2.632 1.288 -2.632C1.556 -2.632 2.076 -2.512 2.076 -1.832C2.076 -1.604 2.004 -1.224 1.96 -0.976C1.952 -0.928 1.956 -0.932 2.012 -0.908C2.416 -0.748 2.684 -0.408 2.684 0.044C2.684 0.556 2.308 1.008 1.72 1.008C1.616 1.008 1.616 1.008 1.604 1.08ZM1.88 3.772C2.012 3.772 2.12 3.664 2.12 3.444C2.12 3 1.74 2.64 1.424 2.364C1.396 2.34 1.38 2.344 1.372 2.396C1.356 2.5 1.348 2.636 1.348 2.764C1.348 3.388 1.636 3.772 1.88 3.772ZM1.444 1.048C1.456 0.972 1.456 0.976 1.384 0.952C1.032 0.832 0.804 0.516 0.804 0.176C0.804 -0.184 0.992 -0.44 1.264 -0.532C1.296 -0.544 1.344 -0.556 1.372 -0.556C1.404 -0.556 1.42 -0.536 1.42 -0.512C1.42 -0.484 1.388 -0.472 1.36 -0.46C1.192 -0.388 1.072 -0.216 1.072 -0.032C1.072 0.196 1.228 0.368 1.472 0.436C1.536 0.452 1.544 0.448 1.552 0.404L1.752 -0.788C1.76 -0.832 1.756 -0.832 1.696 -0.844C1.632 -0.856 1.552 -0.864 1.472 -0.864C0.772 -0.864 0.32 -0.476 0.32 0.08C0.32 0.316 0.36 0.632 0.692 1.008C0.932 1.276 1.116 1.424 1.304 1.576C1.344 1.608 1.352 1.604 1.36 1.56ZM1.72 0.412C1.712 0.46 1.716 0.472 1.764 0.468C2.088 0.44 2.356 0.168 2.356 -0.184C2.356 -0.436 2.204 -0.64 1.98 -0.752C1.932 -0.776 1.924 -0.776 1.916 -0.728Z" transform="translate(8.6599 -70.5726)" fill="#000000" data-prov="ac9b59e5511a1d14b4c4901bf7f6ae3b" data-source-kind="4" data-glyph="gClef" data-class="clef"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(11.9444 -65.5726)" fill="#000000" data-prov="7ec9b93ecb2a3b9b3c63ec3c09eeada8" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(14.0251 -69.0726)" fill="#000000" data-prov="18f3bbf40e6f0db46de148caf3b9598f" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(16.1058 -65.5726)" fill="#000000" data-prov="648c96e7875d5eee9329c93ed05ff8f1" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(18.1864 -69.0726)" fill="#000000" data-prov="fa45c91c3e12b58dd82f091a759f5c2d" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.144 0V4H0V0ZM0.912 4H0.412V0H0.912Z" transform="translate(19.9671 -12.8926)" fill="#000000" data-prov="99aebfebeec540a79f695c2034cb6fd4" data-source-kind="9" data-glyph="barlineFinal" data-class="barline"/>
<path d="M0.144 0V4H0V0ZM0.912 4H0.412V0H0.912Z" transform="translate(19.9671 -39.9526)" fill="#000000" data-prov="7310a658cfa443d83676f3cbc4c5bf1b" data-source-kind="9" data-glyph="barlineFinal" data-class="barline"/>
<path d="M0.144 0V4H0V0ZM0.912 4H0.412V0H0.912Z" transform="translate(19.9671 -71.5726)" fill="#000000" data-prov="1b9067fde6ae04fdb1d24e65c8734725" data-source-kind="9" data-glyph="barlineFinal" data-class="barline"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,15 @@
fixture=three_staff_close_content solver=stub
glyph_count=18
path_count=18
fallback_rect_count=0
stroke_count=77
curve_count=1
provenance_count=96
layer_count=1
hard_constraint_count=27
xml_well_formed=true
view_box=[-3.065 -27.632 16.876999 35.024002]
class_counts:
barline=3
clef=3
notehead=12

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="168.77" height="350.24" viewBox="0 0 16.877 35.024">
<!-- epiphany-render-svg: glyphs are genuine Bravura SMuFL outlines inlined as paths; geometry is the resolved layout verbatim, no engraving performed here; every glyph, stroke, and curve carries a data-prov trace to its score-graph source -->
<g transform="translate(3.065 7.392) scale(1 -1)">
<g data-layer="0">
<line x1="0" y1="0" x2="0" y2="0" stroke="#000000" stroke-width="0" data-prov="cab69c17ed41c68ac4c9d9fd2585f7b8" data-source-kind="6" data-kind="stroke"/>
<line x1="-1" y1="0" x2="11.4" y2="0" stroke="#000000" stroke-width="0.13" data-prov="53de5472c3073e1e2fabab582e7a6a2a" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="1" x2="11.4" y2="1" stroke="#000000" stroke-width="0.13" data-prov="af9e9ab8cff6e189b2b70311d333c4ab" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="2" x2="11.4" y2="2" stroke="#000000" stroke-width="0.13" data-prov="2298edab2b482b968611b1dfd4c4184a" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="3" x2="11.4" y2="3" stroke="#000000" stroke-width="0.13" data-prov="4688e9bd99908438eb2cfa44aa9b69f7" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="4" x2="11.4" y2="4" stroke="#000000" stroke-width="0.13" data-prov="9129d5d4f57c3cc9a6f2761bc4da188d" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-12" x2="11.4" y2="-12" stroke="#000000" stroke-width="0.13" data-prov="f6aa19fc306fddc0ee6ffe5815adea15" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-11" x2="11.4" y2="-11" stroke="#000000" stroke-width="0.13" data-prov="cf7f37a324c02c59d88ed58b2d6eff09" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-10" x2="11.4" y2="-10" stroke="#000000" stroke-width="0.13" data-prov="eed90e04088a49096ff2df61528076f3" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-9" x2="11.4" y2="-9" stroke="#000000" stroke-width="0.13" data-prov="eeba57d92a18b4e508a773d735498540" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-8" x2="11.4" y2="-8" stroke="#000000" stroke-width="0.13" data-prov="7961fdc38d6911be6ccafdd0d4b0e650" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-24" x2="11.4" y2="-24" stroke="#000000" stroke-width="0.13" data-prov="48e35b5f0c754fb61417abb3162cd139" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-23" x2="11.4" y2="-23" stroke="#000000" stroke-width="0.13" data-prov="d1649442bdf17a90fad13e068be1d468" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-22" x2="11.4" y2="-22" stroke="#000000" stroke-width="0.13" data-prov="a63a879181f96b3d9282817ce4854fa8" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-21" x2="11.4" y2="-21" stroke="#000000" stroke-width="0.13" data-prov="407ae799175a7a5107f9b17fae444ea1" data-source-kind="3" data-kind="stroke"/>
<line x1="-1" y1="-20" x2="11.4" y2="-20" stroke="#000000" stroke-width="0.13" data-prov="213d70c1d0c044450c85f49532775c9e" data-source-kind="3" data-kind="stroke"/>
<line x1="0" y1="0" x2="0" y2="0" stroke="#000000" stroke-width="0" data-prov="ce07aadf437a74ccac9fc50e9d4d21a6" data-source-kind="2" data-kind="stroke"/>
<line x1="4.15" y1="-1" x2="4.15" y2="2.5" stroke="#000000" stroke-width="0.12" data-prov="1c2dad5590859e5faf984977c99a71b5" data-source-kind="0" data-kind="stroke"/>
<line x1="2.7" y1="-1" x2="4.4807" y2="-1" stroke="#000000" stroke-width="0.13" data-prov="411616f3ac077092b76181619405cb3c" data-source-kind="1" data-kind="stroke"/>
<line x1="5.75" y1="-4.5" x2="5.75" y2="-1" stroke="#000000" stroke-width="0.12" data-prov="051d981decadda77a3146e86c38a45c5" data-source-kind="0" data-kind="stroke"/>
<line x1="4.3" y1="-1" x2="6.0807" y2="-1" stroke="#000000" stroke-width="0.13" data-prov="64b25a9451c9c4c2ebaf1971ff62ca28" data-source-kind="1" data-kind="stroke"/>
<line x1="4.3" y1="-2" x2="6.0807" y2="-2" stroke="#000000" stroke-width="0.13" data-prov="fcefb1c81f1ffcbb0c6a249a51faa480" data-source-kind="1" data-kind="stroke"/>
<line x1="4.3" y1="-3" x2="6.0807" y2="-3" stroke="#000000" stroke-width="0.13" data-prov="f75a4800bb0d3634144a515ddeb63099" data-source-kind="1" data-kind="stroke"/>
<line x1="4.3" y1="-4" x2="6.0807" y2="-4" stroke="#000000" stroke-width="0.13" data-prov="58afb85138289ff2586446383df8e27c" data-source-kind="1" data-kind="stroke"/>
<line x1="7.35" y1="-8" x2="7.35" y2="-4.5" stroke="#000000" stroke-width="0.12" data-prov="1a8a05c715ac178f13b3ede06a6e0247" data-source-kind="0" data-kind="stroke"/>
<line x1="5.9" y1="-1" x2="7.6807" y2="-1" stroke="#000000" stroke-width="0.13" data-prov="52d557448dd11b6701d6d99d59427241" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-2" x2="7.6807" y2="-2" stroke="#000000" stroke-width="0.13" data-prov="7449ac621352223bd08844a0968b0ca4" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-3" x2="7.6807" y2="-3" stroke="#000000" stroke-width="0.13" data-prov="e7e3b71288255ba6d01ce135782f6a7a" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-4" x2="7.6807" y2="-4" stroke="#000000" stroke-width="0.13" data-prov="9fa4bd9211c697d31a922b1f182d80dd" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-5" x2="7.6807" y2="-5" stroke="#000000" stroke-width="0.13" data-prov="d4adbb2db3a83dcf0ad969763b19c8ee" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-6" x2="7.6807" y2="-6" stroke="#000000" stroke-width="0.13" data-prov="9ffeb9f08bff743cf9cbd70dd373b4e4" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-7" x2="7.6807" y2="-7" stroke="#000000" stroke-width="0.13" data-prov="6d34ca5eed6359ef4a7308ee9fd47944" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-8" x2="7.6807" y2="-8" stroke="#000000" stroke-width="0.13" data-prov="b7e0c1d5273bcdf8c156e36bce721a2e" data-source-kind="1" data-kind="stroke"/>
<line x1="8.95" y1="-11.5" x2="8.95" y2="-8" stroke="#000000" stroke-width="0.12" data-prov="48b9108504e0ae37ce8fb9b4bc54f1ba" data-source-kind="0" data-kind="stroke"/>
<line x1="7.5" y1="-1" x2="9.2807" y2="-1" stroke="#000000" stroke-width="0.13" data-prov="84b60a528ad68bfc9e8bfc0e782d12e2" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-2" x2="9.2807" y2="-2" stroke="#000000" stroke-width="0.13" data-prov="0171d3d7b3cdf2edc122e86785927703" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-3" x2="9.2807" y2="-3" stroke="#000000" stroke-width="0.13" data-prov="502c590d7d2a32b774784c452305e963" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-4" x2="9.2807" y2="-4" stroke="#000000" stroke-width="0.13" data-prov="fae4e983fb4a6dbb4a6cbd34947162ba" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-5" x2="9.2807" y2="-5" stroke="#000000" stroke-width="0.13" data-prov="e19fefcdd1c1b6cbc3fa9f4e7f7ed21e" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-6" x2="9.2807" y2="-6" stroke="#000000" stroke-width="0.13" data-prov="738c2046094a373ec3aa5acf7d9865f9" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-7" x2="9.2807" y2="-7" stroke="#000000" stroke-width="0.13" data-prov="a799f1e4cce19534a24aa29739206ec0" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-8" x2="9.2807" y2="-8" stroke="#000000" stroke-width="0.13" data-prov="7aca9e5f8c50128e6618cc31072bf923" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-9" x2="9.2807" y2="-9" stroke="#000000" stroke-width="0.13" data-prov="77ec8ac817b9921221350030998f20cf" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-10" x2="9.2807" y2="-10" stroke="#000000" stroke-width="0.13" data-prov="5315bb47b55a85a9f4bf997263674f8b" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-11" x2="9.2807" y2="-11" stroke="#000000" stroke-width="0.13" data-prov="89ad40af92c1fba61fa859789484f042" data-source-kind="1" data-kind="stroke"/>
<line x1="0" y1="-12" x2="0" y2="-12" stroke="#000000" stroke-width="0" data-prov="54fe6da1801e4fce8316c678f29cacdf" data-source-kind="2" data-kind="stroke"/>
<line x1="4.15" y1="-2.5" x2="4.15" y2="1" stroke="#000000" stroke-width="0.12" data-prov="952f3712f8f29866ac797bbea256d2d0" data-source-kind="0" data-kind="stroke"/>
<line x1="2.7" y1="-7" x2="4.4807" y2="-7" stroke="#000000" stroke-width="0.13" data-prov="837d109efb37f74957a713aba3123810" data-source-kind="1" data-kind="stroke"/>
<line x1="2.7" y1="-6" x2="4.4807" y2="-6" stroke="#000000" stroke-width="0.13" data-prov="4b888b04616f17f2f3b39f919a4d1f73" data-source-kind="1" data-kind="stroke"/>
<line x1="2.7" y1="-5" x2="4.4807" y2="-5" stroke="#000000" stroke-width="0.13" data-prov="bb3c4f1c88311a08545a507484b65ec5" data-source-kind="1" data-kind="stroke"/>
<line x1="2.7" y1="-4" x2="4.4807" y2="-4" stroke="#000000" stroke-width="0.13" data-prov="7bb81a275725608c136691c9bdc2bcdc" data-source-kind="1" data-kind="stroke"/>
<line x1="2.7" y1="-3" x2="4.4807" y2="-3" stroke="#000000" stroke-width="0.13" data-prov="048e69987982c24dcfc601c985d4de2d" data-source-kind="1" data-kind="stroke"/>
<line x1="5.75" y1="-16.5" x2="5.75" y2="-13" stroke="#000000" stroke-width="0.12" data-prov="c7b2ab7fbc11b592cb08478feb22b263" data-source-kind="0" data-kind="stroke"/>
<line x1="4.3" y1="-13" x2="6.0807" y2="-13" stroke="#000000" stroke-width="0.13" data-prov="041a0efe1178a5a9ece7f6bae256cb49" data-source-kind="1" data-kind="stroke"/>
<line x1="4.3" y1="-14" x2="6.0807" y2="-14" stroke="#000000" stroke-width="0.13" data-prov="d9bb40761053cbb2e56baa1d226c7354" data-source-kind="1" data-kind="stroke"/>
<line x1="4.3" y1="-15" x2="6.0807" y2="-15" stroke="#000000" stroke-width="0.13" data-prov="1402804aba9f279387d6f183f80d79b4" data-source-kind="1" data-kind="stroke"/>
<line x1="4.3" y1="-16" x2="6.0807" y2="-16" stroke="#000000" stroke-width="0.13" data-prov="6d52f1fb3cdd40d6274abeb906770ef1" data-source-kind="1" data-kind="stroke"/>
<line x1="7.35" y1="-2.5" x2="7.35" y2="1" stroke="#000000" stroke-width="0.12" data-prov="5f9c5d54f7ab283f189cea904cb9bb42" data-source-kind="0" data-kind="stroke"/>
<line x1="5.9" y1="-7" x2="7.6807" y2="-7" stroke="#000000" stroke-width="0.13" data-prov="755af675fea2b73be02f98d5ce681561" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-6" x2="7.6807" y2="-6" stroke="#000000" stroke-width="0.13" data-prov="729c3d20e1cf7fe25b2e9a4e5d72d5eb" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-5" x2="7.6807" y2="-5" stroke="#000000" stroke-width="0.13" data-prov="0f3c5819991c4d887c5e26bd166cf6fb" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-4" x2="7.6807" y2="-4" stroke="#000000" stroke-width="0.13" data-prov="c52e73fbf914910d9dedebd4ce938f16" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-3" x2="7.6807" y2="-3" stroke="#000000" stroke-width="0.13" data-prov="78e39176f8952b71f69bef9a4436e4a1" data-source-kind="1" data-kind="stroke"/>
<line x1="8.95" y1="-16.5" x2="8.95" y2="-13" stroke="#000000" stroke-width="0.12" data-prov="c47796a73dccf69155b4078d112fde27" data-source-kind="0" data-kind="stroke"/>
<line x1="7.5" y1="-13" x2="9.2807" y2="-13" stroke="#000000" stroke-width="0.13" data-prov="2a36935e840d6ef047c51d1d7a82ed93" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-14" x2="9.2807" y2="-14" stroke="#000000" stroke-width="0.13" data-prov="79b311a1708aaab4af813b7b959c6d45" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-15" x2="9.2807" y2="-15" stroke="#000000" stroke-width="0.13" data-prov="290ec68875364ec6a4a96dba4456fa60" data-source-kind="1" data-kind="stroke"/>
<line x1="7.5" y1="-16" x2="9.2807" y2="-16" stroke="#000000" stroke-width="0.13" data-prov="b4222559838ef95fbb9829f715732d00" data-source-kind="1" data-kind="stroke"/>
<line x1="0" y1="-24" x2="0" y2="-24" stroke="#000000" stroke-width="0" data-prov="80230a27e89abbdc2803f4ccae35620d" data-source-kind="2" data-kind="stroke"/>
<line x1="4.15" y1="-18" x2="4.15" y2="-14.5" stroke="#000000" stroke-width="0.12" data-prov="12b12b407f09c2ce7151e271b80b4575" data-source-kind="0" data-kind="stroke"/>
<line x1="2.7" y1="-19" x2="4.4807" y2="-19" stroke="#000000" stroke-width="0.13" data-prov="02c8f971668afe306c2b63145b0eda5e" data-source-kind="1" data-kind="stroke"/>
<line x1="2.7" y1="-18" x2="4.4807" y2="-18" stroke="#000000" stroke-width="0.13" data-prov="ad9922ca450855f46c76ced4da8d3eba" data-source-kind="1" data-kind="stroke"/>
<line x1="5.75" y1="-21.5" x2="5.75" y2="-18" stroke="#000000" stroke-width="0.12" data-prov="a6be2c1fdd05ec3c34b4655d0c7f7b7d" data-source-kind="0" data-kind="stroke"/>
<line x1="7.35" y1="-18" x2="7.35" y2="-14.5" stroke="#000000" stroke-width="0.12" data-prov="e596a52ab94676af6dd2afb8839bf6f7" data-source-kind="0" data-kind="stroke"/>
<line x1="5.9" y1="-19" x2="7.6807" y2="-19" stroke="#000000" stroke-width="0.13" data-prov="373157f721d0b1004b19cc474cd1653c" data-source-kind="1" data-kind="stroke"/>
<line x1="5.9" y1="-18" x2="7.6807" y2="-18" stroke="#000000" stroke-width="0.13" data-prov="01ac7b54f5668ab8da8500c7bf12dd08" data-source-kind="1" data-kind="stroke"/>
<line x1="8.95" y1="-21.5" x2="8.95" y2="-18" stroke="#000000" stroke-width="0.12" data-prov="b76cf9581fb59757345a09d2eba68529" data-source-kind="0" data-kind="stroke"/>
<path d="M 3.6 -19.3 C 4.2667 -18.2333 4.9333 -18.2333 5.6 -19.3" fill="none" stroke="#000000" stroke-width="0.12" data-prov="1c938c5c0f8fd1c1397fca33e64be814" data-source-kind="11" data-kind="curve"/>
<path d="M1.504 1.66C1.496 1.708 1.504 1.712 1.528 1.736C1.96 2.14 2.288 2.648 2.288 3.26C2.288 3.608 2.192 3.952 2.028 4.192C1.968 4.28 1.864 4.392 1.82 4.392C1.764 4.392 1.64 4.288 1.56 4.2C1.264 3.872 1.168 3.372 1.168 2.956C1.168 2.724 1.196 2.464 1.224 2.3C1.232 2.252 1.236 2.244 1.188 2.204C0.612 1.728 0 1.156 0 0.348C0 -0.348 0.476 -1.008 1.456 -1.008C1.548 -1.008 1.652 -1 1.732 -0.984C1.776 -0.976 1.784 -0.972 1.792 -1.02C1.84 -1.288 1.9 -1.636 1.9 -1.824C1.9 -2.416 1.5 -2.488 1.264 -2.488C1.048 -2.488 0.944 -2.424 0.944 -2.372C0.944 -2.344 0.98 -2.332 1.072 -2.304C1.196 -2.268 1.34 -2.16 1.34 -1.928C1.34 -1.708 1.2 -1.52 0.956 -1.52C0.688 -1.52 0.528 -1.732 0.528 -1.98C0.528 -2.24 0.684 -2.632 1.288 -2.632C1.556 -2.632 2.076 -2.512 2.076 -1.832C2.076 -1.604 2.004 -1.224 1.96 -0.976C1.952 -0.928 1.956 -0.932 2.012 -0.908C2.416 -0.748 2.684 -0.408 2.684 0.044C2.684 0.556 2.308 1.008 1.72 1.008C1.616 1.008 1.616 1.008 1.604 1.08ZM1.88 3.772C2.012 3.772 2.12 3.664 2.12 3.444C2.12 3 1.74 2.64 1.424 2.364C1.396 2.34 1.38 2.344 1.372 2.396C1.356 2.5 1.348 2.636 1.348 2.764C1.348 3.388 1.636 3.772 1.88 3.772ZM1.444 1.048C1.456 0.972 1.456 0.976 1.384 0.952C1.032 0.832 0.804 0.516 0.804 0.176C0.804 -0.184 0.992 -0.44 1.264 -0.532C1.296 -0.544 1.344 -0.556 1.372 -0.556C1.404 -0.556 1.42 -0.536 1.42 -0.512C1.42 -0.484 1.388 -0.472 1.36 -0.46C1.192 -0.388 1.072 -0.216 1.072 -0.032C1.072 0.196 1.228 0.368 1.472 0.436C1.536 0.452 1.544 0.448 1.552 0.404L1.752 -0.788C1.76 -0.832 1.756 -0.832 1.696 -0.844C1.632 -0.856 1.552 -0.864 1.472 -0.864C0.772 -0.864 0.32 -0.476 0.32 0.08C0.32 0.316 0.36 0.632 0.692 1.008C0.932 1.276 1.116 1.424 1.304 1.576C1.344 1.608 1.352 1.604 1.36 1.56ZM1.72 0.412C1.712 0.46 1.716 0.472 1.764 0.468C2.088 0.44 2.356 0.168 2.356 -0.184C2.356 -0.436 2.204 -0.64 1.98 -0.752C1.932 -0.776 1.924 -0.776 1.916 -0.728Z" transform="translate(0 1)" fill="#000000" data-prov="e39c94e4224c7060995ea50ab3f1a891" data-source-kind="4" data-glyph="gClef" data-class="clef"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(3 -1)" fill="#000000" data-prov="2489cab25ad1c58249798a96c93a19df" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(4.6 -4.5)" fill="#000000" data-prov="d25c7d3368d839c8286dd224bf53b46b" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(6.2 -8)" fill="#000000" data-prov="02b4ad76298971279a4f0f908ab1a999" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(7.8 -11.5)" fill="#000000" data-prov="02a400a2dc076ba0d606e0437828d1fb" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M1.504 1.66C1.496 1.708 1.504 1.712 1.528 1.736C1.96 2.14 2.288 2.648 2.288 3.26C2.288 3.608 2.192 3.952 2.028 4.192C1.968 4.28 1.864 4.392 1.82 4.392C1.764 4.392 1.64 4.288 1.56 4.2C1.264 3.872 1.168 3.372 1.168 2.956C1.168 2.724 1.196 2.464 1.224 2.3C1.232 2.252 1.236 2.244 1.188 2.204C0.612 1.728 0 1.156 0 0.348C0 -0.348 0.476 -1.008 1.456 -1.008C1.548 -1.008 1.652 -1 1.732 -0.984C1.776 -0.976 1.784 -0.972 1.792 -1.02C1.84 -1.288 1.9 -1.636 1.9 -1.824C1.9 -2.416 1.5 -2.488 1.264 -2.488C1.048 -2.488 0.944 -2.424 0.944 -2.372C0.944 -2.344 0.98 -2.332 1.072 -2.304C1.196 -2.268 1.34 -2.16 1.34 -1.928C1.34 -1.708 1.2 -1.52 0.956 -1.52C0.688 -1.52 0.528 -1.732 0.528 -1.98C0.528 -2.24 0.684 -2.632 1.288 -2.632C1.556 -2.632 2.076 -2.512 2.076 -1.832C2.076 -1.604 2.004 -1.224 1.96 -0.976C1.952 -0.928 1.956 -0.932 2.012 -0.908C2.416 -0.748 2.684 -0.408 2.684 0.044C2.684 0.556 2.308 1.008 1.72 1.008C1.616 1.008 1.616 1.008 1.604 1.08ZM1.88 3.772C2.012 3.772 2.12 3.664 2.12 3.444C2.12 3 1.74 2.64 1.424 2.364C1.396 2.34 1.38 2.344 1.372 2.396C1.356 2.5 1.348 2.636 1.348 2.764C1.348 3.388 1.636 3.772 1.88 3.772ZM1.444 1.048C1.456 0.972 1.456 0.976 1.384 0.952C1.032 0.832 0.804 0.516 0.804 0.176C0.804 -0.184 0.992 -0.44 1.264 -0.532C1.296 -0.544 1.344 -0.556 1.372 -0.556C1.404 -0.556 1.42 -0.536 1.42 -0.512C1.42 -0.484 1.388 -0.472 1.36 -0.46C1.192 -0.388 1.072 -0.216 1.072 -0.032C1.072 0.196 1.228 0.368 1.472 0.436C1.536 0.452 1.544 0.448 1.552 0.404L1.752 -0.788C1.76 -0.832 1.756 -0.832 1.696 -0.844C1.632 -0.856 1.552 -0.864 1.472 -0.864C0.772 -0.864 0.32 -0.476 0.32 0.08C0.32 0.316 0.36 0.632 0.692 1.008C0.932 1.276 1.116 1.424 1.304 1.576C1.344 1.608 1.352 1.604 1.36 1.56ZM1.72 0.412C1.712 0.46 1.716 0.472 1.764 0.468C2.088 0.44 2.356 0.168 2.356 -0.184C2.356 -0.436 2.204 -0.64 1.98 -0.752C1.932 -0.776 1.924 -0.776 1.916 -0.728Z" transform="translate(0 -11)" fill="#000000" data-prov="57ece9af76f53869ab2df3c844cbb990" data-source-kind="4" data-glyph="gClef" data-class="clef"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(3 -2.5)" fill="#000000" data-prov="a29e010753de6d915fea6b92d42d2a6b" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(4.6 -16.5)" fill="#000000" data-prov="edd00864090a7fbfeb86d9fc66d5fdfc" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(6.2 -2.5)" fill="#000000" data-prov="7a77efa323697b85c6e570b201b40373" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(7.8 -16.5)" fill="#000000" data-prov="5df0b734253de82c2360eb30508e2ccd" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M1.504 1.66C1.496 1.708 1.504 1.712 1.528 1.736C1.96 2.14 2.288 2.648 2.288 3.26C2.288 3.608 2.192 3.952 2.028 4.192C1.968 4.28 1.864 4.392 1.82 4.392C1.764 4.392 1.64 4.288 1.56 4.2C1.264 3.872 1.168 3.372 1.168 2.956C1.168 2.724 1.196 2.464 1.224 2.3C1.232 2.252 1.236 2.244 1.188 2.204C0.612 1.728 0 1.156 0 0.348C0 -0.348 0.476 -1.008 1.456 -1.008C1.548 -1.008 1.652 -1 1.732 -0.984C1.776 -0.976 1.784 -0.972 1.792 -1.02C1.84 -1.288 1.9 -1.636 1.9 -1.824C1.9 -2.416 1.5 -2.488 1.264 -2.488C1.048 -2.488 0.944 -2.424 0.944 -2.372C0.944 -2.344 0.98 -2.332 1.072 -2.304C1.196 -2.268 1.34 -2.16 1.34 -1.928C1.34 -1.708 1.2 -1.52 0.956 -1.52C0.688 -1.52 0.528 -1.732 0.528 -1.98C0.528 -2.24 0.684 -2.632 1.288 -2.632C1.556 -2.632 2.076 -2.512 2.076 -1.832C2.076 -1.604 2.004 -1.224 1.96 -0.976C1.952 -0.928 1.956 -0.932 2.012 -0.908C2.416 -0.748 2.684 -0.408 2.684 0.044C2.684 0.556 2.308 1.008 1.72 1.008C1.616 1.008 1.616 1.008 1.604 1.08ZM1.88 3.772C2.012 3.772 2.12 3.664 2.12 3.444C2.12 3 1.74 2.64 1.424 2.364C1.396 2.34 1.38 2.344 1.372 2.396C1.356 2.5 1.348 2.636 1.348 2.764C1.348 3.388 1.636 3.772 1.88 3.772ZM1.444 1.048C1.456 0.972 1.456 0.976 1.384 0.952C1.032 0.832 0.804 0.516 0.804 0.176C0.804 -0.184 0.992 -0.44 1.264 -0.532C1.296 -0.544 1.344 -0.556 1.372 -0.556C1.404 -0.556 1.42 -0.536 1.42 -0.512C1.42 -0.484 1.388 -0.472 1.36 -0.46C1.192 -0.388 1.072 -0.216 1.072 -0.032C1.072 0.196 1.228 0.368 1.472 0.436C1.536 0.452 1.544 0.448 1.552 0.404L1.752 -0.788C1.76 -0.832 1.756 -0.832 1.696 -0.844C1.632 -0.856 1.552 -0.864 1.472 -0.864C0.772 -0.864 0.32 -0.476 0.32 0.08C0.32 0.316 0.36 0.632 0.692 1.008C0.932 1.276 1.116 1.424 1.304 1.576C1.344 1.608 1.352 1.604 1.36 1.56ZM1.72 0.412C1.712 0.46 1.716 0.472 1.764 0.468C2.088 0.44 2.356 0.168 2.356 -0.184C2.356 -0.436 2.204 -0.64 1.98 -0.752C1.932 -0.776 1.924 -0.776 1.916 -0.728Z" transform="translate(0 -23)" fill="#000000" data-prov="ac9b59e5511a1d14b4c4901bf7f6ae3b" data-source-kind="4" data-glyph="gClef" data-class="clef"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(3 -18)" fill="#000000" data-prov="7ec9b93ecb2a3b9b3c63ec3c09eeada8" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(4.6 -21.5)" fill="#000000" data-prov="18f3bbf40e6f0db46de148caf3b9598f" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(6.2 -18)" fill="#000000" data-prov="648c96e7875d5eee9329c93ed05ff8f1" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.388 -0.5C0.744 -0.5 1.18 -0.172 1.18 0.168C1.18 0.372 1.02 0.5 0.792 0.5C0.352 0.5 0 0.176 0 -0.168C0 -0.376 0.172 -0.5 0.388 -0.5Z" transform="translate(7.8 -21.5)" fill="#000000" data-prov="fa45c91c3e12b58dd82f091a759f5c2d" data-source-kind="1" data-glyph="noteheadBlack" data-class="notehead"/>
<path d="M0.144 0V4H0V0ZM0.912 4H0.412V0H0.912Z" transform="translate(10.9 0)" fill="#000000" data-prov="99aebfebeec540a79f695c2034cb6fd4" data-source-kind="9" data-glyph="barlineFinal" data-class="barline"/>
<path d="M0.144 0V4H0V0ZM0.912 4H0.412V0H0.912Z" transform="translate(10.9 -12)" fill="#000000" data-prov="7310a658cfa443d83676f3cbc4c5bf1b" data-source-kind="9" data-glyph="barlineFinal" data-class="barline"/>
<path d="M0.144 0V4H0V0ZM0.912 4H0.412V0H0.912Z" transform="translate(10.9 -24)" fill="#000000" data-prov="1b9067fde6ae04fdb1d24e65c8734725" data-source-kind="9" data-glyph="barlineFinal" data-class="barline"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -211,6 +211,128 @@ pub fn two_staff_close_content(seed: u64) -> Score {
score
}
/// A one-measure, THREE-staff metric score with **asymmetric** inter-staff
/// pressure, built to exercise the vertical solve's cumulative shift cascade.
///
/// The upper pair collides hard (staff 1 plunges to C1 while staff 2 towers to
/// C7); the lower pair collides gently (staff 2's C3 against staff 3's C6 and
/// the slur arcing over it). So the first correction is several times the
/// second — which is the point. A solve that computed each pair's correction
/// *independently* would shift staff 3 by only the small amount while staff 2
/// took the large one, pulling staff 3 back **through** staff 2 and closing
/// their staff-line gap to well under the fixed pitch. Only a solve whose shift
/// accumulates down the stack keeps every pair separated. Invariant-clean.
pub fn three_staff_close_content(seed: u64) -> Score {
let mut rng = SplitMix64::new(seed ^ 0x0003_57AF_F123);
let replica =
ReplicaId::from_entropy(rng.next_u64().to_le_bytes()).unwrap_or(ReplicaId(0x3ADF));
let mut idc = IdentityContext::new(replica);
let staves: Vec<StaffId> = (0..3).map(|_| idc.mint()).collect();
let instrument: InstrumentId = idc.mint();
let region_id: RegionId = idc.mint();
let measure = |id: MeasureId| Measure {
id,
start: TimeAnchor::Region {
id: region_id,
edge: RegionEdge::Start,
offset: AnchorOffset::Zero,
},
time_signature: None,
explicit_number: Some(1),
number_visibility: Default::default(),
};
// Staff 1 dips to low ledgers; staff 2 spans C7 down to C3 (so it presses
// upward on staff 1 and downward on staff 3); staff 3 climbs to C6.
let octaves: [[i8; 4]; 3] = [[4, 3, 2, 1], [7, 3, 7, 3], [6, 5, 6, 5]];
let mut arena = EventArena::new();
let mut instances: Vec<StaffInstance> = Vec::new();
let mut low_events: Vec<EventId> = Vec::new();
for (staff_index, staff_id) in staves.iter().enumerate() {
let instance_id: StaffInstanceId = idc.mint();
let voice_id: VoiceId = idc.mint();
let mut voice = Voice::user(voice_id);
for (i, oct) in octaves[staff_index].into_iter().enumerate() {
let (eid, pid): (EventId, PitchId) = (idc.mint(), idc.mint());
arena
.insert(c_at(eid, voice_id, pid, i as i64, oct))
.unwrap();
voice.events.push(eid);
if staff_index == 2 {
low_events.push(eid);
}
}
let mut instance = StaffInstance::new(instance_id, *staff_id);
instance.voices.push(voice);
instance.measures.push(measure(idc.mint()));
instances.push(instance);
}
// A slur over the bottom staff's high notes, arcing further up — the extra
// upward extent that turns the lower pair's near-miss into real pressure.
let mut cross_cutting = CrossCuttingRegistry::default();
cross_cutting.slurs.push(Slur {
id: idc.mint::<SlurId>(),
start_event: low_events[0],
end_event: low_events[2],
kind: SlurKind::Legato,
curvature_override: None,
style: SpanStyle::default(),
});
let region = epiphany_core::Region {
id: region_id,
time_model: RegionTimeModel::Metric(MetricTimeModel::default()),
content: RegionContent::StaffBased(StaffBasedContent {
staff_instances: instances,
..Default::default()
}),
time_extent: TimeExtent {
start: TimeAnchor::WallClock {
time: WallClockTime(0),
},
end: TimeAnchor::WallClock {
time: WallClockTime(10_000_000),
},
},
staff_extent: StaffExtent {
staves: staves.clone(),
},
local_tempo_map: None,
permits_spanning_slurs: false,
};
let staff = |id: StaffId, name: &str| Staff {
id,
name: String::from(name),
abbreviation: None,
instrument,
default_staff_lines: StaffLineConfiguration::default(),
group: None,
default_clef: epiphany_core::Clef::treble(),
};
let mut score = Score::empty(idc.clone());
score.identity = idc;
score.instruments = vec![epiphany_core::Instrument::new(
instrument,
String::from("Organ"),
)];
score.staves = vec![
staff(staves[0], "Upper"),
staff(staves[1], "Middle"),
staff(staves[2], "Lower"),
];
score.events = arena;
score.cross_cutting = cross_cutting;
score.canvas = Canvas {
regions: vec![region],
..Default::default()
};
score
}
/// A 10-measure, single-staff, single-voice metric score with 40 quarter notes
/// (four per measure), plus a tie, a spanner, a marker, and a chord symbol. The
/// QUICKSTART layout hand-off case. Invariant-clean (the returned graph passes
@ -491,6 +613,21 @@ mod tests {
assert_eq!(s.cross_cutting.slurs.len(), 1, "a slur over the high notes");
}
#[test]
fn three_staff_close_content_is_invariant_clean_and_asymmetric() {
let s = three_staff_close_content(1);
let v = check_invariants(&s);
assert!(v.is_empty(), "three-staff fixture has violations: {v:?}");
assert_eq!(s.staves.len(), 3, "three staves");
assert_eq!(
s.canvas.regions[0].staff_instances().len(),
3,
"three staff instances in ONE region — so all three land in one system"
);
assert_eq!(s.events.len(), 12, "four notes per staff");
assert_eq!(s.cross_cutting.slurs.len(), 1, "a slur over the low staff");
}
#[test]
fn repeat_fixture_is_invariant_clean_and_carries_three_repeats() {
let s = ten_measure_with_repeats(1);