UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

62 lines (61 loc) 2.77 kB
import * as rendering from '../rendering'; import * as util from '../util'; import { DEFAULT_CONFIG } from '../config'; import { NoopLogger } from '../debug'; import { PanoramicFormatter } from './panoramicformatter'; /** * A formatter that splits the score into systems based on the width of the measures. */ export class DefaultFormatter { config; log; constructor(opts) { this.config = { ...DEFAULT_CONFIG, ...opts?.config }; this.log = opts?.logger ?? new NoopLogger(); util.assertNotNull(this.config.WIDTH, 'WIDTH must be set for DefaultFormatter'); } format(document) { const clone = document.clone(); // First, ensure the document is formatted for infinite x-scrolling. This will allow us to measure the width of the // measures and make decisions on how to group them into systems. const panoramicConfig = { ...this.config, WIDTH: null, HEIGHT: null }; const panoramicFormatter = new PanoramicFormatter({ config: panoramicConfig }); const panoramicDocument = new rendering.Document(panoramicFormatter.format(document)); const panoramicScoreRender = new rendering.Score(panoramicConfig, this.log, panoramicDocument, null).render(); const slices = this.getSystemSlices(this.config, panoramicScoreRender); this.applySystemSlices(clone, slices); return clone; } getSystemSlices(config, scoreRender) { const slices = [{ from: 0, to: 0 }]; let remaining = config.WIDTH; let count = 0; const measureRenders = scoreRender.systemRenders.flatMap((systemRender) => systemRender.measureRenders); for (let measureIndex = 0; measureIndex < measureRenders.length; measureIndex++) { const measure = measureRenders[measureIndex]; const required = measure.rect.w; if (required > remaining && count > 0) { slices.push({ from: measure.absoluteIndex, to: measure.absoluteIndex }); remaining = config.WIDTH; count = 0; } slices.at(-1).to = measure.absoluteIndex; remaining -= required; count++; } this.log.debug(`grouped ${measureRenders.length} measures into ${slices.length} system(s)`); return slices; } applySystemSlices(document, slices) { const measures = document.score.systems.flatMap((s) => s.measures); document.score.systems = []; for (const slice of slices) { const system = { type: 'system', measures: new Array(), }; system.measures = measures.slice(slice.from, slice.to + 1); document.score.systems.push(system); } } }