UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

167 lines (166 loc) 6.46 kB
import { BoundingBox } from './boundingbox.js'; import { Element } from './element.js'; import { Formatter } from './formatter.js'; import { Note } from './note.js'; import { Stave } from './stave.js'; import { RuntimeError } from './util.js'; export class System extends Element { static get CATEGORY() { return "System"; } constructor(params = {}) { super(); this.setOptions(params); this.partStaves = []; this.partStaveInfos = []; this.partVoices = []; } setOptions(options = {}) { if (!options.factory) { throw new RuntimeError('NoFactory', 'System.setOptions(options) requires a factory.'); } this.factory = options.factory; this.options = Object.assign(Object.assign({ factory: this.factory, x: 10, y: 10, width: 500, spaceBetweenStaves: 12, autoWidth: false, noJustification: false, debugFormatter: false, formatIterations: 0, noPadding: false }, options), { details: Object.assign({ alpha: 0.5 }, options.details), formatOptions: Object.assign({}, options.formatOptions) }); if (this.options.noJustification === false && typeof options.width === 'undefined') { this.options.autoWidth = true; } } getX() { return this.options.x; } setX(x) { this.options.x = x; this.partStaves.forEach((s) => { s.setX(x); }); return this; } getY() { return this.options.y; } setY(y) { this.options.y = y; this.partStaves.forEach((s) => { s.setY(y); }); return this; } getStaves() { return this.partStaves; } getVoices() { return this.partVoices; } setContext(context) { super.setContext(context); this.factory.setContext(context); return this; } addConnector(type = 'double') { this.connector = this.factory.StaveConnector({ topStave: this.partStaves[0], bottomStave: this.partStaves[this.partStaves.length - 1], type, }); return this.connector; } addStave(params) { var _a; const staveOptions = Object.assign({ leftBar: false }, params.options); const stave = (_a = params.stave) !== null && _a !== void 0 ? _a : this.factory.Stave({ x: this.options.x, y: this.options.y, width: this.options.width, options: staveOptions }); const p = Object.assign(Object.assign({ spaceAbove: 0, spaceBelow: 0, debugNoteMetrics: false, noJustification: false }, params), { options: staveOptions }); const ctx = this.getContext(); p.voices.forEach((voice) => { voice .setContext(ctx) .setStave(stave) .getTickables() .forEach((tickable) => tickable.setStave(stave)); this.partVoices.push(voice); }); this.partStaves.push(stave); this.partStaveInfos.push(p); return stave; } addVoices(voices) { const ctx = this.getContext(); voices.forEach((voice) => { voice.setContext(ctx); this.partVoices.push(voice); }); } format() { const optionsDetails = this.options.details; let justifyWidth = 0; const formatter = new Formatter(optionsDetails); this.formatter = formatter; let y = this.options.y; let startX = 0; const debugNoteMetricsYs = []; this.partStaves.forEach((part, index) => { y = y + part.space(this.partStaveInfos[index].spaceAbove); part.setY(y); y = y + part.space(this.partStaveInfos[index].spaceBelow); y = y + part.space(this.options.spaceBetweenStaves); if (this.partStaveInfos[index].debugNoteMetrics) { debugNoteMetricsYs.push({ y, stave: part }); y += 15; } startX = Math.max(startX, part.getNoteStartX()); }); this.partVoices.forEach((voice) => { voice.getTickables().forEach((tickable) => { const stave = tickable.getStave(); if (stave) tickable.setStave(stave); }); }); formatter.joinVoices(this.partVoices); this.partStaves.forEach((part) => part.setNoteStartX(startX)); if (this.options.autoWidth && this.partVoices.length > 0) { justifyWidth = formatter.preCalculateMinTotalWidth(this.partVoices); this.options.width = justifyWidth + Stave.rightPadding + (startX - this.options.x); this.partStaves.forEach((part) => { part.setWidth(this.options.width); }); } else { justifyWidth = this.options.noPadding ? this.options.width - (startX - this.options.x) : this.options.width - (startX - this.options.x) - Stave.defaultPadding; } if (this.partVoices.length > 0) { formatter.format(this.partVoices, this.options.noJustification ? 0 : justifyWidth, this.options.formatOptions); } formatter.postFormat(); for (let i = 0; i < this.options.formatIterations; i++) { formatter.tune(optionsDetails); } this.startX = startX; this.debugNoteMetricsYs = debugNoteMetricsYs; this.lastY = y; Stave.formatBegModifiers(this.partStaves); } getBoundingBox() { var _a; return new BoundingBox(this.options.x, this.options.y, this.options.width, ((_a = this.lastY) !== null && _a !== void 0 ? _a : 0) - this.options.y); } draw() { const ctx = this.checkContext(); if (!this.formatter || !this.startX || !this.lastY || !this.debugNoteMetricsYs) { throw new RuntimeError('NoFormatter', 'format() must be called before draw()'); } this.setRendered(); if (this.options.debugFormatter) { Formatter.plotDebugging(ctx, this.formatter, this.startX, this.options.y, this.lastY); } this.debugNoteMetricsYs.forEach((d) => { this.partVoices.forEach((voice) => { voice.getTickables().forEach((tickable) => { if (tickable.getStave() === d.stave) Note.plotMetrics(ctx, tickable, d.y); }); }); }); } }