vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
175 lines (174 loc) • 7.25 kB
JavaScript
import { Element } from './element.js';
import { Glyph } from './glyph.js';
import { NoteHead } from './notehead.js';
import { StaveModifierPosition } from './stavemodifier.js';
import { Tables } from './tables.js';
import { TimeSignature } from './timesignature.js';
import { isBarline } from './typeguard.js';
import { defined } from './util.js';
let semibreve_rest;
function get_semibreve_rest() {
if (!semibreve_rest) {
const noteHead = new NoteHead({ duration: 'w', note_type: 'r' });
semibreve_rest = {
glyph_font_scale: noteHead.render_options.glyph_font_scale,
glyph_code: noteHead.glyph_code,
width: noteHead.getWidth(),
};
}
return semibreve_rest;
}
export class MultiMeasureRest extends Element {
static get CATEGORY() {
return "MultiMeasureRest";
}
constructor(number_of_measures, options) {
var _a;
super();
this.xs = { left: NaN, right: NaN };
this.hasPaddingLeft = false;
this.hasPaddingRight = false;
this.hasLineThickness = false;
this.hasSymbolSpacing = false;
this.number_of_measures = number_of_measures;
this.hasPaddingLeft = typeof options.padding_left === 'number';
this.hasPaddingRight = typeof options.padding_right === 'number';
this.hasLineThickness = typeof options.line_thickness === 'number';
this.hasSymbolSpacing = typeof options.symbol_spacing === 'number';
const musicFont = Tables.currentMusicFont();
this.render_options = Object.assign({ use_symbols: false, show_number: true, number_line: -0.5, number_glyph_point: (_a = musicFont.lookupMetric('digits.point')) !== null && _a !== void 0 ? _a : Tables.NOTATION_FONT_SCALE, line: 2, spacing_between_lines_px: Tables.STAVE_LINE_DISTANCE, serif_thickness: 2, semibreve_rest_glyph_scale: Tables.NOTATION_FONT_SCALE, padding_left: 0, padding_right: 0, line_thickness: 5, symbol_spacing: 0 }, options);
const fontLineShift = musicFont.lookupMetric('digits.shiftLine', 0);
this.render_options.number_line += fontLineShift;
}
getXs() {
return this.xs;
}
setStave(stave) {
this.stave = stave;
return this;
}
getStave() {
return this.stave;
}
checkStave() {
return defined(this.stave, 'NoStave', 'No stave attached to instance.');
}
drawLine(stave, ctx, left, right, spacingBetweenLines) {
const options = this.render_options;
const y = stave.getYForLine(options.line);
const padding = (right - left) * 0.1;
left += padding;
right -= padding;
let lineThicknessHalf;
if (this.hasLineThickness) {
lineThicknessHalf = options.line_thickness * 0.5;
}
else {
lineThicknessHalf = spacingBetweenLines * 0.25;
}
const serifThickness = options.serif_thickness;
const top = y - spacingBetweenLines;
const bot = y + spacingBetweenLines;
const leftIndented = left + serifThickness;
const rightIndented = right - serifThickness;
const lineTop = y - lineThicknessHalf;
const lineBottom = y + lineThicknessHalf;
ctx.save();
ctx.beginPath();
ctx.moveTo(left, top);
ctx.lineTo(leftIndented, top);
ctx.lineTo(leftIndented, lineTop);
ctx.lineTo(rightIndented, lineTop);
ctx.lineTo(rightIndented, top);
ctx.lineTo(right, top);
ctx.lineTo(right, bot);
ctx.lineTo(rightIndented, bot);
ctx.lineTo(rightIndented, lineBottom);
ctx.lineTo(leftIndented, lineBottom);
ctx.lineTo(leftIndented, bot);
ctx.lineTo(left, bot);
ctx.closePath();
ctx.fill();
}
drawSymbols(stave, ctx, left, right, spacingBetweenLines) {
const n4 = Math.floor(this.number_of_measures / 4);
const n = this.number_of_measures % 4;
const n2 = Math.floor(n / 2);
const n1 = n % 2;
const options = this.render_options;
semibreve_rest = undefined;
const rest = get_semibreve_rest();
const rest_scale = options.semibreve_rest_glyph_scale;
const rest_width = rest.width * (rest_scale / rest.glyph_font_scale);
const glyphs = {
2: {
width: rest_width * 0.5,
height: spacingBetweenLines,
},
1: {
width: rest_width,
},
};
const spacing = this.hasSymbolSpacing ? options.symbol_spacing : 10;
const width = n4 * glyphs[2].width + n2 * glyphs[2].width + n1 * glyphs[1].width + (n4 + n2 + n1 - 1) * spacing;
let x = left + (right - left) * 0.5 - width * 0.5;
const line = options.line;
const yTop = stave.getYForLine(line - 1);
const yMiddle = stave.getYForLine(line);
const yBottom = stave.getYForLine(line + 1);
ctx.save();
ctx.setStrokeStyle('none');
ctx.setLineWidth(0);
for (let i = 0; i < n4; ++i) {
ctx.fillRect(x, yMiddle - glyphs[2].height, glyphs[2].width, glyphs[2].height);
ctx.fillRect(x, yBottom - glyphs[2].height, glyphs[2].width, glyphs[2].height);
x += glyphs[2].width + spacing;
}
for (let i = 0; i < n2; ++i) {
ctx.fillRect(x, yMiddle - glyphs[2].height, glyphs[2].width, glyphs[2].height);
x += glyphs[2].width + spacing;
}
for (let i = 0; i < n1; ++i) {
Glyph.renderGlyph(ctx, x, yTop, rest_scale, rest.glyph_code);
x += glyphs[1].width + spacing;
}
ctx.restore();
}
draw() {
const ctx = this.checkContext();
this.setRendered();
const stave = this.checkStave();
let left = stave.getNoteStartX();
let right = stave.getNoteEndX();
const begModifiers = stave.getModifiers(StaveModifierPosition.BEGIN);
if (begModifiers.length === 1 && isBarline(begModifiers[0])) {
left -= begModifiers[0].getWidth();
}
const options = this.render_options;
if (this.hasPaddingLeft) {
left = stave.getX() + options.padding_left;
}
if (this.hasPaddingRight) {
right = stave.getX() + stave.getWidth() - options.padding_right;
}
this.xs.left = left;
this.xs.right = right;
const spacingBetweenLines = options.spacing_between_lines_px;
if (options.use_symbols) {
this.drawSymbols(stave, ctx, left, right, spacingBetweenLines);
}
else {
this.drawLine(stave, ctx, left, right, spacingBetweenLines);
}
if (options.show_number) {
const timeSpec = '/' + this.number_of_measures;
const timeSig = new TimeSignature(timeSpec, 0, false);
timeSig.point = options.number_glyph_point;
timeSig.setTimeSig(timeSpec);
timeSig.setStave(stave);
timeSig.setX(left + (right - left) * 0.5 - timeSig.getInfo().glyph.getMetrics().width * 0.5);
timeSig.bottomLine = options.number_line;
timeSig.setContext(ctx).draw();
}
}
}