104 lines
6.0 KiB
Python
104 lines
6.0 KiB
Python
from __future__ import annotations
|
|
|
|
import fractions
|
|
|
|
import generator.engraving.fonts
|
|
import generator.engraving.notation
|
|
import generator.engraving.types
|
|
import generator.model
|
|
|
|
def staff_attributes(index: generator.engraving.notation.ScoreIndex, state: dict, time: fractions.Fraction, fonts: generator.engraving.fonts.Fonts, space: float, source: str, fields: tuple[str, ...]) -> list[generator.engraving.types.Primitive]:
|
|
result = []
|
|
x = 0
|
|
references = (source,)
|
|
clef = state['clef']
|
|
lines = state['staff_details']['lines']
|
|
if 'clef' in fields and clef['sign'] != 'none':
|
|
sign = clef['sign']
|
|
name = {'G': 'gClef', 'F': 'fClef', 'C': 'cClef', 'percussion': 'unpitchedPercussionClef1'}[sign]
|
|
y = -space * (clef.get('line', 2) - 1) if sign in ('G', 'F', 'C') else -space * (lines - 1) / 2
|
|
glyph = fonts.glyph(name, x, y, space, 'clef', references)
|
|
result.append(glyph)
|
|
if octave := clef.get('octave_change', 0):
|
|
label = str(abs(octave) * 7 + 1)
|
|
result.append(fonts.text(label, (glyph.box.left + glyph.box.right) / 2, glyph.box.top - 2 if octave > 0 else glyph.box.bottom + 14, 14, 'clef-octave', references, True))
|
|
x = generator.engraving.types.bounds(result).right + space
|
|
if 'key' in fields and clef['sign'] != 'percussion':
|
|
key = state['key']
|
|
steps = key.get('steps')
|
|
if steps is None:
|
|
order = 'fcgdaeb' if key.get('fifths', 0) >= 0 else 'beadgcf'
|
|
steps = [{'letter': c, 'alter': key['alterations'][generator.model.LETTERS.index(c)]} for c in order if generator.model.alteration(key['alterations'][generator.model.LETTERS.index(c)])]
|
|
cancellation = key.get('cancel', 0)
|
|
if cancellation:
|
|
order = 'fcgdaeb' if cancellation > 0 else 'beadgcf'
|
|
old = generator.model.key_from_fifths(cancellation)['alterations']
|
|
steps = [{'letter': c, 'alter': 0} for c in order if generator.model.alteration(old[generator.model.LETTERS.index(c)]) and generator.model.alteration(old[generator.model.LETTERS.index(c)]) != generator.model.alteration(key['alterations'][generator.model.LETTERS.index(c)])] + steps
|
|
for step in steps:
|
|
pitch = {'letter': step['letter'], 'octave': step.get('octave', 5), 'id': source}
|
|
position = index.pitch_position(pitch, time)
|
|
if 'octave' not in step:
|
|
while position > 9:
|
|
position -= 7
|
|
while position < 2:
|
|
position += 7
|
|
items = generator.engraving.notation.accidental(fonts, generator.model.alteration(step['alter']), {'name': step.get('accidental', '')}, space, references)
|
|
result.extend(p.moved(x, -position * space / 2) for p in items)
|
|
x += generator.engraving.types.bounds(items).right - generator.engraving.types.bounds(items).left + 4
|
|
if steps:
|
|
x += space / 2
|
|
if 'meter' in fields:
|
|
meter = state['meter']
|
|
if meter.get('unmetered'):
|
|
result.append(fonts.text('senza misura', x, -space * 2, 16, 'meter', references))
|
|
elif meter.get('symbol') in ('common', 'cut'):
|
|
result.append(fonts.glyph('timeSigCommon' if meter['symbol'] == 'common' else 'timeSigCutCommon', x, -space * 2, space, 'meter', references))
|
|
else:
|
|
for gi, group in enumerate(meter['groups']):
|
|
if gi:
|
|
plus = fonts.glyph('timeSigPlus', x, -space * 2, space, 'meter', references)
|
|
result.append(plus)
|
|
x = plus.box.right + 7
|
|
numerator = '+'.join(map(str, group['beats']))
|
|
denominator = str(group['unit'])
|
|
rows = [(numerator, -space * 2)] if meter.get('symbol') == 'single-number' else [(numerator, -space * 3), (denominator, -space)]
|
|
groups = []
|
|
for value, y in rows:
|
|
items = []
|
|
cursor = 0
|
|
if y == -space and meter.get('symbol') in ('note', 'dotted-note'):
|
|
unit = fractions.Fraction(4, group['unit'])
|
|
kind = next((name for name, length in generator.model.TYPE_LENGTHS.items() if length == unit), None)
|
|
glyph_name = {'whole': 'noteWhole', 'half': 'noteHalfUp', 'quarter': 'noteQuarterUp', 'eighth': 'note8thUp'}.get(kind, 'note' + str(kind) + 'Up')
|
|
if kind and glyph_name in fonts.names:
|
|
glyph = fonts.glyph(glyph_name, 0, y + space, space * 0.55, 'meter', references)
|
|
items.append(glyph)
|
|
if meter['symbol'] == 'dotted-note':
|
|
items.append(fonts.glyph('augmentationDot', glyph.box.right + 3, y + space / 2, space * 0.55, 'meter', references))
|
|
groups.append(items)
|
|
continue
|
|
for char in value:
|
|
glyph = fonts.glyph('timeSigPlus' if char == '+' else 'timeSig' + char, cursor, y, space, 'meter', references)
|
|
items.append(glyph)
|
|
cursor = glyph.box.right + 1
|
|
groups.append(items)
|
|
width = max(generator.engraving.types.bounds(items).right for items in groups)
|
|
for items in groups:
|
|
result.extend(p.moved(x + (width - generator.engraving.types.bounds(items).right) / 2, 0) for p in items)
|
|
x += width + 6
|
|
if 'staff_details' in fields and state['staff_details'].get('capo'):
|
|
result.append(fonts.text(f'Capo {state["staff_details"]["capo"]}', x, -space * (lines + 1), 16, 'staff-details', references))
|
|
return result
|
|
|
|
def barline(style: str, x: float, top: float, source: str) -> list[generator.engraving.types.Primitive]:
|
|
if style == 'none':
|
|
return []
|
|
references = (source,)
|
|
if style in ('dashed', 'dotted'):
|
|
dash, gap = (5, 4) if style == 'dashed' else (1, 4)
|
|
return [generator.engraving.types.line('barline', references, ((x, y), (x, min(y + dash, 0)))) for y in range(round(top), 0, dash + gap)]
|
|
if style in ('tick', 'short'):
|
|
return [generator.engraving.types.line('barline', references, ((x, top - 4 if style == 'tick' else top * 0.75), (x, top + 5 if style == 'tick' else top * 0.25)))]
|
|
weights = {'regular': [1.4], 'heavy': [4], 'light-light': [1.4, 1.4], 'light-heavy': [1.4, 4], 'heavy-light': [4, 1.4], 'heavy-heavy': [4, 4]}[style]
|
|
return [generator.engraving.types.line('barline', references, ((x - (len(weights) - 1 - i) * 5, top), (x - (len(weights) - 1 - i) * 5, 0)), width) for i, width in enumerate(weights)]
|