@@ -58,22 +58,6 @@ def audit_layout(document: dict, layout: LayoutDocument, fonts: Fonts) -> None:
|
||||
raise EngravingError(f'{item.sources} and {other.sources}: colliding {item.role}/{other.role}')
|
||||
if previous_end != index.end:
|
||||
raise EngravingError('layout omits the end of the score')
|
||||
from generator.repeats import analyze
|
||||
_, endings = analyze(index.staff['measures'])
|
||||
for ending in endings:
|
||||
if not ending.visible:
|
||||
continue
|
||||
identifier = index.measures[ending.start][2]['id'] + ':ending'
|
||||
if not any(p.role == 'ending-label' and p.value == ending.text for p in primitives.get(identifier, [])):
|
||||
raise EngravingError(f'{identifier}: numbered ending was omitted')
|
||||
low, high = index.measures[ending.start][0], index.measures[ending.end - 1][1]
|
||||
for page in layout.pages:
|
||||
for system in page.systems:
|
||||
if system.start < high and system.end > low and not any(p.role == 'ending' and identifier in p.sources for p in system.primitives):
|
||||
raise EngravingError(f'{identifier}: ending continuation was omitted')
|
||||
for _, _, measure in index.measures:
|
||||
if measure.get('repeats') and not any(p.role == 'repeat-dot' for p in primitives.get(measure['id'], [])):
|
||||
raise EngravingError(f'{measure["id"]}: repeat dots were omitted')
|
||||
for event in index.events:
|
||||
note = event.value
|
||||
for source in note['pitches'] or [note]:
|
||||
@@ -101,7 +85,7 @@ def audit_layout(document: dict, layout: LayoutDocument, fonts: Fonts) -> None:
|
||||
if any(f.visible for f in pieces) and identifier in index.tones and index.accidentals[identifier]:
|
||||
if not any(p.role == 'accidental' for p in primitives.get(identifier, [])):
|
||||
raise EngravingError(f'{identifier}: required accidental was omitted')
|
||||
if identifier in index.tones and (source.get('tie_to') or source.get('printed_ties')) and any(f.visible for f in pieces):
|
||||
if identifier in index.tones and source.get('tie_to') and any(f.visible for f in pieces):
|
||||
if not any(p.role == 'tie' for p in primitives.get(identifier, [])):
|
||||
raise EngravingError(f'{identifier}: tie was omitted')
|
||||
if not note.get('hidden'):
|
||||
@@ -113,8 +97,6 @@ def audit_layout(document: dict, layout: LayoutDocument, fonts: Fonts) -> None:
|
||||
for _, _, measure in index.measures:
|
||||
for value in measure['harmonies']:
|
||||
label = harmony.symbol(value)
|
||||
if value.get('chord_optional'):
|
||||
label = '(' + label + ')'
|
||||
if not any(p.role == 'harmony' and p.value == label for p in primitives.get(value['id'], [])):
|
||||
raise EngravingError(f'{value["id"]}: harmony was omitted')
|
||||
for i, _ in enumerate(measure.get('tempos', [])):
|
||||
|
||||
@@ -10,7 +10,6 @@ from generator.engraving.attributes import barline, staff_attributes, tempo
|
||||
from generator.engraving.fonts import Fonts
|
||||
from generator.engraving.notation import ScoreIndex
|
||||
from generator.engraving.notes import draw_event
|
||||
from generator.engraving.repeats import draw_boundary, draw_endings
|
||||
from generator.engraving.spans import draw_beams, draw_lyric_spans, draw_spans, place_text_lanes
|
||||
from generator.engraving.types import Anchor, EngravingError, Fragment, LayoutDocument, Page, Primitive, Style, System, bounds, line
|
||||
|
||||
@@ -52,8 +51,6 @@ def column(index: ScoreIndex, time: Fraction, start: Fraction, end: Fraction, fo
|
||||
for item in measure['harmonies']:
|
||||
if measure_start + fraction(item['onset']) == time:
|
||||
label = harmony.symbol(item)
|
||||
if item.get('chord_optional'):
|
||||
label = '(' + label + ')'
|
||||
items.append(fonts.text(label, 0, 0, 21, 'harmony', (item['id'],)))
|
||||
for i, value in enumerate(measure.get('tempos', [])):
|
||||
if measure_start + fraction(value['onset']) == time:
|
||||
@@ -71,12 +68,6 @@ def make_system(index: ScoreIndex, start: Fraction, end: Fraction, fonts: Fonts,
|
||||
header = staff_attributes(index, state, start, fonts, style.space, measure['id'] + ':initial', ('clef', 'key', 'meter', 'staff_details'))
|
||||
items.extend(header)
|
||||
cursor = max((p.box.right for p in header), default=0) + 22
|
||||
boundary_positions = {start: cursor - 9}
|
||||
if start in index.boundaries and measure.get('repeats', {}).get('left'):
|
||||
initial = draw_boundary(None, measure, cursor, -style.space * (state['staff_details']['lines'] - 1), fonts, style)
|
||||
boundary_positions[start] = cursor
|
||||
items.extend(initial)
|
||||
cursor = max(p.box.right for p in initial) + 18
|
||||
content_left = cursor - 9
|
||||
times = sorted({start} | {t for t in index.columns if start < t < end})
|
||||
placements = []
|
||||
@@ -88,18 +79,15 @@ def make_system(index: ScoreIndex, start: Fraction, end: Fraction, fonts: Fonts,
|
||||
for measure_start, measure_end, m in index.measures:
|
||||
if time == measure_start and time > start:
|
||||
previous = next(previous for a, b, previous in index.measures if b == time)
|
||||
leading.extend(draw_boundary(previous, m, 0, -style.space * (index.state(time)['staff_details']['lines'] - 1), fonts, style))
|
||||
leading.extend(barline(previous.get('barlines', {}).get('right', 'regular'), 0, -style.space * (index.state(time)['staff_details']['lines'] - 1), previous['id']))
|
||||
old_state = index.state(time, before=True)
|
||||
fields = tuple(k for k in ('clef', 'key', 'meter', 'staff_details') if old_state[k] != index.state(time)[k])
|
||||
offset = max((p.box.right for p in leading), default=0) + 12 if previous.get('repeats') or m.get('repeats') else 12
|
||||
leading.extend(p.moved(offset, 0) for p in staff_attributes(index, index.state(time), time, fonts, style.space, m['id'] + ':initial', fields))
|
||||
leading.extend(p.moved(12, 0) for p in staff_attributes(index, index.state(time), time, fonts, style.space, m['id'] + ':initial', fields))
|
||||
for ci, change in enumerate(m.get('attributes', [])):
|
||||
if measure_start + fraction(change['onset']) == time and time > start:
|
||||
extra = staff_attributes(index, index.state(time), time, fonts, style.space, m['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 leading:
|
||||
if time in index.boundaries:
|
||||
boundary_positions[time] = cursor - bounds(leading).left
|
||||
width = bounds(leading).right - bounds(leading).left + 14
|
||||
items.extend(p.moved(cursor - bounds(leading).left, 0) for p in leading)
|
||||
cursor += width
|
||||
@@ -121,18 +109,13 @@ def make_system(index: ScoreIndex, start: Fraction, end: Fraction, fonts: Fonts,
|
||||
items[first_item:last_item] = [p.moved(i * stretch, 0) for p in items[first_item:last_item]]
|
||||
anchors[first_anchor:last_anchor] = [a.moved(i * stretch, 0) for a in anchors[first_anchor:last_anchor]]
|
||||
placements = [(time, x + i * stretch) for i, (time, x) in enumerate(placements)]
|
||||
boundary_positions = {time: x + times.index(time) * stretch for time, x in boundary_positions.items()}
|
||||
width = available
|
||||
right = width - 5
|
||||
boundary_positions[end] = width - 3
|
||||
last_measure = next(m for a, b, m in index.measures if a < end <= b)
|
||||
if end in index.boundaries and last_measure.get('repeats', {}).get('right'):
|
||||
items.extend(draw_boundary(last_measure, None, width - 3, -style.space * (index.state(end, before=True)['staff_details']['lines'] - 1), fonts, style))
|
||||
items = draw_beams(index, anchors, items, start, end, content_left, right, style)
|
||||
items = draw_spans(index, anchors, items, start, end, content_left, right, fonts, style)
|
||||
items = place_text_lanes(items, fonts, style)
|
||||
items = draw_lyric_spans(index, items, anchors, start, end, content_left, right, fonts)
|
||||
items = draw_endings(index, items, start, end, boundary_positions, fonts, style)
|
||||
# Staff lines are interrupted behind TAB numerals, not painted through them.
|
||||
changes = [(0, state['staff_details']['lines'])]
|
||||
for time, x in placements:
|
||||
@@ -151,11 +134,10 @@ def make_system(index: ScoreIndex, start: Fraction, end: Fraction, fonts: Fonts,
|
||||
staff_lines.append(line('staff', (), ((cursor, y), (a, y)), style.space * 0.13))
|
||||
cursor = max(cursor, b)
|
||||
items = staff_lines + items
|
||||
if end in index.boundaries and not last_measure.get('repeats', {}).get('right'):
|
||||
items.extend(draw_boundary(last_measure, None, width - (3 if last_measure.get('repeats') else 1), -style.space * (index.state(end, before=True)['staff_details']['lines'] - 1), fonts, style))
|
||||
if end in index.boundaries:
|
||||
items.extend(barline(last_measure.get('barlines', {}).get('right', 'regular'), width - 1, -style.space * (index.state(end, before=True)['staff_details']['lines'] - 1), last_measure['id']))
|
||||
if start in index.boundaries:
|
||||
if not measure.get('repeats', {}).get('left'):
|
||||
items.extend(barline(measure.get('barlines', {}).get('left', 'none'), 0, -style.space * (state['staff_details']['lines'] - 1), measure['id']))
|
||||
items.extend(barline(measure.get('barlines', {}).get('left', 'none'), 0, -style.space * (state['staff_details']['lines'] - 1), measure['id']))
|
||||
else:
|
||||
items.append(fonts.text(str(measure['number']) + ' (cont.)', 0, min(p.box.top for p in items) - 6, 12, 'continuation', (measure['id'],)))
|
||||
return System(start, end, tuple(items), tuple(anchors), tuple(fragments), bounds(items))
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
from generator.engraving.attributes import barline
|
||||
from generator.engraving.types import line
|
||||
from generator.repeats import analyze
|
||||
|
||||
def draw_boundary(previous, following, x, top, fonts, style):
|
||||
backward = previous.get('repeats', {}).get('right') if previous else None
|
||||
forward = following.get('repeats', {}).get('left') if following else None
|
||||
source = previous or following
|
||||
if not backward and not forward:
|
||||
value = previous.get('barlines', {}).get('right', 'regular') if previous else following.get('barlines', {}).get('left', 'none')
|
||||
return barline(value, x, top, source['id'])
|
||||
refs = tuple(m['id'] for m in (previous, following) if m)
|
||||
result = [line('repeat-bar', refs, ((x, top), (x, 0)), 4)]
|
||||
for marker, direction in ((backward, -1), (forward, 1)):
|
||||
if not marker:
|
||||
continue
|
||||
result.append(line('repeat-bar', refs, ((x + direction * 6, top), (x + direction * 6, 0)), 1.4))
|
||||
for offset in (-0.5, 0.5):
|
||||
result.append(fonts.glyph('augmentationDot', x + direction * 13 - 2, top / 2 + style.space * offset, style.space, 'repeat-dot', refs))
|
||||
if backward and backward.get('times', 2) != 2:
|
||||
result.append(fonts.text(f'{backward["times"]} times', x - 10, top - 12, 14, 'repeat-count', refs, True))
|
||||
return result
|
||||
|
||||
def draw_endings(index, items, start, end, positions, fonts, style):
|
||||
_, endings = analyze(index.staff['measures'])
|
||||
result = list(items)
|
||||
boundaries = [a for a, _, _ in index.measures] + [index.end]
|
||||
top = min((p.box.top for p in items), default=0) - 25
|
||||
for ending in endings:
|
||||
low, high = boundaries[ending.start], boundaries[ending.end]
|
||||
if not ending.visible or high <= start or low >= end:
|
||||
continue
|
||||
x1 = positions[max(low, start)]
|
||||
x2 = positions[min(high, end)]
|
||||
refs = (index.measures[ending.start][2]['id'] + ':ending',)
|
||||
result.append(line('ending', refs, ((x1, top), (x2, top))))
|
||||
if low >= start:
|
||||
result.append(line('ending', refs, ((x1, top), (x1, top + 10))))
|
||||
result.append(fonts.text(ending.text, x1 + 5, top - 4, 16, 'ending-label', refs))
|
||||
if high <= end and ending.closed:
|
||||
result.append(line('ending', refs, ((x2, top), (x2, top + 10))))
|
||||
return result
|
||||
@@ -101,20 +101,6 @@ def draw_spans(index: ScoreIndex, anchors: list[Anchor], items: list[Primitive],
|
||||
target = tone.get('tie_to')
|
||||
tone_end = event.start + fraction(tone['duration'])
|
||||
anchor = by_tone.get(identifier)
|
||||
if tone.get('printed_ties'):
|
||||
for action in tone['printed_ties']:
|
||||
candidates = [(other_id, other) for other_id, (other_event, other) in index.tones.items() if other_event.voice == event.voice and other_event.start == tone_end and 'stop' in other.get('printed_ties', []) and all(other.get(k) == tone.get(k) for k in ('letter', 'alter', 'octave', 'unpitched'))]
|
||||
if action == 'start' and candidates:
|
||||
target = candidates[0][0]
|
||||
continue
|
||||
if action == 'stop' and any(other_event.voice == event.voice and other_event.start + fraction(other['duration']) == event.start and 'start' in other.get('printed_ties', []) and all(other.get(k) == tone.get(k) for k in ('letter', 'alter', 'octave', 'unpitched')) for other_event, other in index.tones.values()):
|
||||
continue
|
||||
if anchor is None:
|
||||
continue
|
||||
x1, x2 = (anchor.x + style.space * 1.4, min(right, anchor.x + 36)) if action == 'start' else (max(left, anchor.x - 24), anchor.x - 2)
|
||||
y = anchor.y - anchor.direction * 8
|
||||
if x2 > x1:
|
||||
result.append(curve('tie', (identifier,), ((x1, y), (x1 + (x2 - x1) / 3, y - anchor.direction * 9), (x2 - (x2 - x1) / 3, y - anchor.direction * 9), (x2, y)), 1.6))
|
||||
if anchor and anchor.fragment.end < tone_end:
|
||||
target = identifier
|
||||
if target:
|
||||
|
||||
Reference in New Issue
Block a user