@stringsync/vexml
Version:
MusicXML to Vexflow
167 lines (166 loc) • 6.73 kB
JavaScript
import * as util from '../util';
import { System } from './system';
import { Label } from './label';
import { Rect } from '../spatial';
import { Pen } from './pen';
import { SystemRenderMover } from './systemrendermover';
import { Curve } from './curve';
import { Wedge } from './wedge';
import { Pedal } from './pedal';
import { OctaveShift } from './octaveshift';
import { RenderRegistry } from './renderregistry';
import { Vibrato } from './vibrato';
/**
* Score is the top-level rendering object that is directly responsible for arranging systems.
*/
export class Score {
config;
log;
document;
width;
constructor(config, log, document, width) {
this.config = config;
this.log = log;
this.document = document;
this.width = width;
}
render() {
const pen = new Pen();
pen.moveBy({ dy: this.config.SCORE_PADDING_TOP });
const titleRender = this.renderTitle(pen);
const systemRenders = this.renderSystems(pen);
const registry = RenderRegistry.create(systemRenders);
const curveRenders = this.renderCurves(registry);
const wedgeRenders = this.renderWedges(registry);
const pedalRenders = this.renderPedals(registry);
const octaveShiftRenders = this.renderOctaveShifts(registry);
const vibratoRenders = this.renderVibratos(registry);
pen.moveBy({ dy: this.config.SCORE_PADDING_BOTTOM });
const width = this.width ?? util.max(systemRenders.map((system) => system.rect.w));
const rect = new Rect(0, 0, width, pen.position().y);
return {
type: 'score',
rect,
titleRender,
systemRenders,
curveRenders,
wedgeRenders,
pedalRenders,
octaveShiftRenders,
vibratoRenders,
};
}
renderTitle(pen) {
const title = this.document.getTitle();
if (!title) {
return null;
}
const position = pen.position();
const padding = this.getTitlePadding();
const font = this.getTitleFont();
let label;
if (this.width) {
label = Label.centerAligned(this.config, this.log, this.width, title, position, padding, font);
}
else {
label = Label.singleLine(this.config, this.log, title, position, padding, font);
}
const rect = label.rect;
pen.moveBy({ dy: rect.h });
return {
type: 'title',
rect,
label,
};
}
renderCurves(registry) {
const curves = this.document.getCurves();
const curveRenders = new Array();
for (let curveIndex = 0; curveIndex < curves.length; curveIndex++) {
const key = { curveIndex };
const noteRenderCount = registry.get(curves[curveIndex].id)?.length ?? 0;
if (noteRenderCount >= 1) {
const curveRender = new Curve(this.config, this.log, this.document, key, registry).render();
curveRenders.push(curveRender);
}
}
return curveRenders;
}
renderWedges(registry) {
const wedges = this.document.getWedges();
const wedgeRenders = new Array();
for (let wedgeIndex = 0; wedgeIndex < wedges.length; wedgeIndex++) {
const key = { wedgeIndex };
const noteRenderCount = registry.get(wedges[wedgeIndex].id)?.length ?? 0;
if (noteRenderCount >= 1) {
const wedgeRender = new Wedge(this.config, this.log, this.document, key, registry).render();
wedgeRenders.push(wedgeRender);
}
}
return wedgeRenders;
}
renderPedals(registry) {
const pedals = this.document.getPedals();
const pedalRenders = new Array();
for (let pedalIndex = 0; pedalIndex < pedals.length; pedalIndex++) {
const key = { pedalIndex };
const noteRenderCount = registry.get(pedals[pedalIndex].id)?.length ?? 0;
if (noteRenderCount >= 1) {
const pedalRender = new Pedal(this.config, this.log, this.document, key, registry).render();
pedalRenders.push(pedalRender);
}
}
return pedalRenders;
}
renderOctaveShifts(registry) {
const octaveShifts = this.document.getOctaveShifts();
const octaveShiftRenders = new Array();
for (let octaveShiftIndex = 0; octaveShiftIndex < octaveShifts.length; octaveShiftIndex++) {
const key = { octaveShiftIndex };
const noteRenderCount = registry.get(octaveShifts[octaveShiftIndex].id)?.length ?? 0;
if (noteRenderCount >= 1) {
const octaveShiftRender = new OctaveShift(this.config, this.log, this.document, key, registry).render();
octaveShiftRenders.push(octaveShiftRender);
}
}
return octaveShiftRenders;
}
renderVibratos(registry) {
const vibratos = this.document.getVibratos();
const vibratoRenders = new Array();
for (let vibratoIndex = 0; vibratoIndex < vibratos.length; vibratoIndex++) {
const key = { vibratoIndex };
const noteRenderCount = registry.get(vibratos[vibratoIndex].id)?.length ?? 0;
if (noteRenderCount >= 1) {
const vibratoRender = new Vibrato(this.config, this.log, this.document, key, registry).render();
vibratoRenders.push(vibratoRender);
}
}
return vibratoRenders;
}
getTitlePadding() {
return { bottom: this.config.TITLE_PADDING_BOTTOM };
}
getTitleFont() {
return {
color: 'black',
family: this.config.TITLE_FONT_FAMILY,
size: this.config.TITLE_FONT_SIZE,
lineHeight: this.config.TITLE_FONT_LINE_HEIGHT_PX,
};
}
renderSystems(pen) {
const systemRenders = new Array();
const systemCount = this.document.getSystemCount();
for (let systemIndex = 0; systemIndex < systemCount; systemIndex++) {
const key = { systemIndex };
const systemRender = new System(this.config, this.log, this.document, key, this.width, pen.position()).render();
systemRenders.push(systemRender);
const excessHeight = util.max(systemRender.measureRenders.flatMap((m) => m.fragmentRenders).flatMap((e) => e.excessHeight));
new SystemRenderMover().moveBy(systemRender, excessHeight);
pen.moveTo(systemRender.rect.bottomLeft());
pen.moveBy({ dy: this.config.SYSTEM_MARGIN_BOTTOM });
}
return systemRenders;
}
}