Playback Mode Pages
ci / checks (push) Successful in 50m29s

This commit is contained in:
khodowany
2026-10-06 23:11:25 -05:00
parent 125aaa1637
commit d2f493c17b
33 changed files with 620 additions and 130 deletions
+28 -19
View File
@@ -57,7 +57,6 @@ def column(index: generator.engraving.notation.ScoreIndex, time: fractions.Fract
items.append(fonts.text(generator.harmony.symbol(item), 0, 0, 18 if index.staff.get('tablature') else 21, 'harmony', (item['id'],)))
items.extend(generator.engraving.scales.column(index, time, start, fonts))
if time == measure_start:
items.append(fonts.text(str(measure['number']), 0, -style.space * (index.state(time)['staff_details']['lines'] + 0.5), 12, 'measure_number', (measure['id'],)))
if label := measure.get('section_label'):
items.append(fonts.text(label, 0, 0, 16 if index.staff.get('tablature') else 19, 'section', (measure['id'] + ':section',)))
return items, anchors, fragments
@@ -103,6 +102,10 @@ def make_system(index: generator.engraving.notation.ScoreIndex, start: fractions
for owner, ci, change in index.attributes_by_onset.get(time, []):
extra = generator.engraving.attributes.staff_attributes(index, index.state(time), time, fonts, style.space, owner['id'] + f':attributes:{ci}', tuple(change))
leading.extend(p.moved(max((p.box.right for p in leading), default=0) + 10, 0) for p in extra)
if time == measure_start:
# Anchor labels to the barline, not to the first note's time column.
bar_x = cursor - generator.engraving.types.bounds(leading).left if leading else 0
items.append(fonts.text(str(m['number']), bar_x, 0, 12, 'measure_number', (m['id'],), True))
if leading:
width = generator.engraving.types.bounds(leading).right - generator.engraving.types.bounds(leading).left + 14
items.extend(p.moved(cursor - generator.engraving.types.bounds(leading).left, 0) for p in leading)
@@ -162,7 +165,7 @@ def fit_part_systems(systems, index, available_height, fonts, style):
def fitting_scale(items):
scale = 1
for first in range(0, len(items), 2):
for first in range(len(items)):
pair = items[first:first + 2]
height = sum(s.box.bottom - s.box.top for s in pair)
scale = min(scale, (available_height - style.system_gap * (len(pair) - 1)) / height)
@@ -214,30 +217,36 @@ def part_header(document, fonts, style):
result.append(fonts.text(line, style.margin_x, baseline, 12, 'subtitle', ()))
return result
def paginate(systems: list[generator.engraving.types.System], document: dict, fonts: generator.engraving.fonts.Fonts, style: generator.engraving.types.Style, *, index=None) -> tuple[generator.engraving.types.Page, ...]:
pages = []
def page_views(systems: list[generator.engraving.types.System], document: dict, fonts: generator.engraving.fonts.Fonts, style: generator.engraving.types.Style, *, index=None) -> tuple[tuple[generator.engraving.types.Page, ...], tuple[generator.engraving.types.Page, ...]]:
index = index or generator.engraving.notation.ScoreIndex(document)
header = part_header(document, fonts, style)
top = generator.engraving.types.bounds(header).bottom + style.page_clearance
footers = [fonts.text(str(number), style.width / 2, style.height - style.margin_y, 12, 'page_number', (), True) for number in range(1, (len(systems) + 1) // 2 + 1)]
footers = [fonts.text(str(number), style.width / 2, style.height - style.margin_y, 12, 'page_number', (), True) for number in range(1, len(systems) + 1)]
bottom = min(p.box.top for p in footers) - style.page_clearance
if bottom - top <= style.system_gap:
raise generator.engraving.types.EngravingError('page header leaves no room for two music systems')
fitted = fit_part_systems(systems, index, bottom - top, fonts, style)
for first in range(0, len(systems), 2):
number = len(pages) + 1
pair = fitted[first:first + 2]
slack = max(0, bottom - top - sum(s.box.bottom - s.box.top for s in pair) - style.system_gap * (len(pair) - 1))
spacing = slack / 3 if len(pair) == 2 else 0
cursor = top + spacing
placed = []
for system in pair:
dx, dy = style.margin_x + 1, cursor - system.box.top
placed.append(dataclasses.replace(system, primitives=tuple(p.moved(dx, dy) for p in system.primitives), anchors=tuple(a.moved(dx, dy) for a in system.anchors), box=system.box.moved(dx, dy)))
cursor = placed[-1].box.bottom + style.system_gap + spacing
footer = footers[first // 2]
pages.append(generator.engraving.types.Page(number, style.width, style.height, style.supersampling, tuple(placed), tuple(header + [p for s in placed for p in s.primitives] + [footer])))
return tuple(pages)
def place(stride):
pages = []
for first in range(0, len(fitted), stride):
number = len(pages) + 1
pair = fitted[first:first + 2]
slack = max(0, bottom - top - sum(s.box.bottom - s.box.top for s in pair) - style.system_gap * (len(pair) - 1))
spacing = slack / 3 if len(pair) == 2 else 0
cursor = top + spacing
placed = []
for system in pair:
dx, dy = style.margin_x + 1, cursor - system.box.top
placed.append(dataclasses.replace(system, primitives=tuple(p.moved(dx, dy) for p in system.primitives), anchors=tuple(a.moved(dx, dy) for a in system.anchors), box=system.box.moved(dx, dy)))
cursor = placed[-1].box.bottom + style.system_gap + spacing
pages.append(generator.engraving.types.Page(number, style.width, style.height, style.supersampling, tuple(placed), tuple(header + [p for s in placed for p in s.primitives] + [footers[number - 1]])))
return tuple(pages)
return place(2), place(1)
def paginate(systems, document, fonts, style, *, index=None):
return page_views(systems, document, fonts, style, index=index)[0]
def layout_systems(document: dict, style: generator.engraving.types.Style, fonts: generator.engraving.fonts.Fonts, *, index=None) -> tuple[generator.engraving.types.System, ...]:
if index is None: