@@ -78,15 +78,19 @@ def audit_layout(document: dict, layout: generator.engraving.types.LayoutDocumen
|
||||
fragments.setdefault(fragment.source, []).append(fragment)
|
||||
# Staff, ledger, stem, beam and tie contacts are intentional. Distinct
|
||||
# noteheads, accidentals and text in the same lane must remain separate.
|
||||
checked = [p for p in system.primitives if p.role in ('head', 'tab', 'accidental', 'harmony', 'lyric')]
|
||||
checked = [p for p in system.primitives if p.role in ('head', 'tab', 'accidental', 'harmony', 'lyric', 'scale', 'section')]
|
||||
for i, item in enumerate(checked):
|
||||
for other in checked[i + 1:]:
|
||||
if set(item.sources) & set(other.sources) and (item.role != 'tab' or other.role != 'tab'):
|
||||
continue
|
||||
if item.role != other.role and {item.role, other.role} - {'head', 'tab', 'accidental'}:
|
||||
if item.role != other.role and 'scale' not in (item.role, other.role) and {item.role, other.role} - {'head', 'tab', 'accidental'}:
|
||||
continue
|
||||
if item.box.overlaps(other.box, 0.1 * system.scale):
|
||||
raise generator.engraving.types.EngravingError(f'{item.sources} and {other.sources}: colliding {item.role}/{other.role}')
|
||||
expected_scales = [span for span in index.scale_spans if generator.model.fraction(span['start']) < system.end and generator.model.fraction(span['end']) > system.start]
|
||||
drawn_scales = [(p.sources[0], p.value) for p in system.primitives if p.role == 'scale']
|
||||
if drawn_scales != [(span['id'], span['label']) for span in expected_scales]:
|
||||
raise generator.engraving.types.EngravingError('solo scale labels are missing, duplicated, or changed')
|
||||
if previous_end != index.end:
|
||||
raise generator.engraving.types.EngravingError('layout omits the end of the score')
|
||||
for event in index.events:
|
||||
|
||||
@@ -11,6 +11,7 @@ import generator.engraving.audit
|
||||
import generator.engraving.fonts
|
||||
import generator.engraving.notation
|
||||
import generator.engraving.notes
|
||||
import generator.engraving.scales
|
||||
import generator.engraving.spans
|
||||
import generator.engraving.tablature
|
||||
import generator.engraving.types
|
||||
@@ -54,6 +55,7 @@ def column(index: generator.engraving.notation.ScoreIndex, time: fractions.Fract
|
||||
for owner, _, item in index.harmonies_by_onset.get(time, []):
|
||||
if owner is measure:
|
||||
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'):
|
||||
|
||||
@@ -57,6 +57,9 @@ class ScoreIndex:
|
||||
bucket.setdefault(onset, []).append((measure, index, item))
|
||||
time = end
|
||||
self.end = time
|
||||
self.scale_spans = self.staff.get('scale_spans', [])
|
||||
self.scale_starts = [generator.model.fraction(span['start']) for span in self.scale_spans]
|
||||
self.columns.update(self.scale_starts)
|
||||
self.measure_starts = [item[0] for item in self.measures]
|
||||
self.measure_starts_by_id = {measure['id']: start for start, _, measure in self.measures}
|
||||
self.events.sort(key=lambda e: (e.start, not bool(e.value.get('grace')), e.lane))
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import bisect
|
||||
|
||||
import generator.model
|
||||
|
||||
def column(index, time, system_start, fonts):
|
||||
position = bisect.bisect_right(index.scale_starts, time) - 1
|
||||
if position < 0:
|
||||
return []
|
||||
span = index.scale_spans[position]
|
||||
if time >= generator.model.fraction(span['end']) or time not in (system_start, index.scale_starts[position]):
|
||||
return []
|
||||
return [fonts.text(span['label'], 0, 0, 16, 'scale', (span['id'],))]
|
||||
@@ -123,7 +123,7 @@ def draw_spans(index: generator.engraving.notation.ScoreIndex, anchors: list[gen
|
||||
y = anchor.y - anchor.direction * 8
|
||||
if anchor.x - 2 > left:
|
||||
result.append(generator.engraving.types.curve('tie', (identifier,), ((left, y), (left + 5, y - anchor.direction * 9), (anchor.x - 7, y - anchor.direction * 9), (anchor.x - 2, y)), 1.6))
|
||||
top = min(-style.space * 5, min((p.box.top for p in items if p.role not in ('harmony', 'lyric', 'section', 'tempo')), default=0))
|
||||
top = min(-style.space * 5, min((p.box.top for p in items if p.role not in ('harmony', 'lyric', 'section', 'scale', 'tempo')), default=0))
|
||||
tuplet_lanes = []
|
||||
for span in index.staff.get('tuplets', []):
|
||||
a, b = index.by_id[span['start']], index.by_id[span['end']]
|
||||
@@ -203,11 +203,11 @@ def draw_spans(index: generator.engraving.notation.ScoreIndex, anchors: list[gen
|
||||
return items + result
|
||||
|
||||
def place_text_lanes(items: list[generator.engraving.types.Primitive], fonts: generator.engraving.fonts.Fonts, style: generator.engraving.types.Style) -> list[generator.engraving.types.Primitive]:
|
||||
music = [p for p in items if p.role not in ('harmony', 'lyric', 'section')]
|
||||
music = [p for p in items if p.role not in ('harmony', 'lyric', 'section', 'scale')]
|
||||
box = generator.engraving.types.bounds(music)
|
||||
result = list(music)
|
||||
top = box.top - 12
|
||||
for role in ('harmony', 'section'):
|
||||
for role in ('harmony', 'scale', 'section'):
|
||||
lane = [p for p in items if p.role == role]
|
||||
if lane:
|
||||
extent = generator.engraving.types.bounds(lane)
|
||||
|
||||
Reference in New Issue
Block a user