vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
206 lines (183 loc) • 6.02 kB
text/typescript
// [VexFlow](https://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
// Author Larry Kuhns 2011
import { Font, FontInfo, FontStyle, FontWeight } from './font';
import { Glyph } from './glyph';
import { Stave } from './stave';
import { StaveModifier } from './stavemodifier';
import { Tables } from './tables';
import { Category } from './typeguard';
export class Repetition extends StaveModifier {
static get CATEGORY(): string {
return Category.Repetition;
}
static TEXT_FONT: Required<FontInfo> = {
family: Font.SERIF,
size: Tables.NOTATION_FONT_SCALE / 3,
weight: FontWeight.BOLD,
style: FontStyle.NORMAL,
};
static readonly type = {
NONE: 1, // no coda or segno
CODA_LEFT: 2, // coda at beginning of stave
CODA_RIGHT: 3, // coda at end of stave
SEGNO_LEFT: 4, // segno at beginning of stave
SEGNO_RIGHT: 5, // segno at end of stave
DC: 6, // D.C. at end of stave
DC_AL_CODA: 7, // D.C. al coda at end of stave
DC_AL_FINE: 8, // D.C. al Fine end of stave
DS: 9, // D.S. at end of stave
DS_AL_CODA: 10, // D.S. al coda at end of stave
DS_AL_FINE: 11, // D.S. al Fine at end of stave
FINE: 12, // Fine at end of stave
TO_CODA: 13, // To Coda at end of stave
};
protected symbol_type: number;
protected x_shift: number;
protected y_shift: number;
constructor(type: number, x: number, y_shift: number) {
super();
this.symbol_type = type;
this.x = x;
this.x_shift = 0;
this.y_shift = y_shift;
this.resetFont();
}
setShiftX(x: number): this {
this.x_shift = x;
return this;
}
setShiftY(y: number): this {
this.y_shift = y;
return this;
}
draw(stave: Stave, x: number): this {
this.setRendered();
switch (this.symbol_type) {
case Repetition.type.CODA_RIGHT:
this.drawCodaFixed(stave, x + stave.getWidth());
break;
case Repetition.type.CODA_LEFT:
this.drawSymbolText(stave, x, 'Coda', true);
break;
case Repetition.type.SEGNO_LEFT:
this.drawSignoFixed(stave, x);
break;
case Repetition.type.SEGNO_RIGHT:
this.drawSignoFixed(stave, x + stave.getWidth());
break;
case Repetition.type.DC:
this.drawSymbolText(stave, x, 'D.C.', false);
break;
case Repetition.type.DC_AL_CODA:
this.drawSymbolText(stave, x, 'D.C. al', true);
break;
case Repetition.type.DC_AL_FINE:
this.drawSymbolText(stave, x, 'D.C. al Fine', false);
break;
case Repetition.type.DS:
this.drawSymbolText(stave, x, 'D.S.', false);
break;
case Repetition.type.DS_AL_CODA:
this.drawSymbolText(stave, x, 'D.S. al', true);
break;
case Repetition.type.DS_AL_FINE:
this.drawSymbolText(stave, x, 'D.S. al Fine', false);
break;
case Repetition.type.FINE:
this.drawSymbolText(stave, x, 'Fine', false);
break;
case Repetition.type.TO_CODA:
this.drawSymbolText(stave, x, 'To', true);
break;
default:
break;
}
return this;
}
drawCodaFixed(stave: Stave, x: number): this {
const y = stave.getYForTopText(stave.getNumLines()) + this.y_shift;
Glyph.renderGlyph(
stave.checkContext(),
this.x + x + this.x_shift,
y + Tables.currentMusicFont().lookupMetric('staveRepetition.coda.offsetY'),
40,
'coda',
{ category: 'coda' }
);
return this;
}
drawSignoFixed(stave: Stave, x: number): this {
const y = stave.getYForTopText(stave.getNumLines()) + this.y_shift;
Glyph.renderGlyph(
stave.checkContext(),
this.x + x + this.x_shift,
y + Tables.currentMusicFont().lookupMetric('staveRepetition.segno.offsetY'),
30,
'segno',
{ category: 'segno' }
);
return this;
}
drawSymbolText(stave: Stave, x: number, text: string, draw_coda: boolean): this {
const ctx = stave.checkContext();
ctx.save();
ctx.setFont(this.textFont);
let text_x = 0;
let symbol_x = 0;
const modifierWidth = stave.getNoteStartX() - this.x;
switch (this.symbol_type) {
// To the left with symbol
case Repetition.type.CODA_LEFT:
// Offset Coda text to right of stave beginning
text_x = this.x + stave.getVerticalBarWidth();
symbol_x =
text_x +
ctx.measureText(text).width +
Tables.currentMusicFont().lookupMetric('staveRepetition.symbolText.offsetX');
break;
// To the right without symbol
case Repetition.type.DC:
case Repetition.type.DC_AL_FINE:
case Repetition.type.DS:
case Repetition.type.DS_AL_FINE:
case Repetition.type.FINE:
text_x =
this.x +
x +
this.x_shift +
stave.getWidth() -
Tables.currentMusicFont().lookupMetric('staveRepetition.symbolText.spacing') -
modifierWidth -
ctx.measureText(text).width;
break;
// To the right with symbol
default:
text_x =
this.x +
x +
this.x_shift +
stave.getWidth() -
Tables.currentMusicFont().lookupMetric('staveRepetition.symbolText.spacing') -
modifierWidth -
ctx.measureText(text).width -
Tables.currentMusicFont().lookupMetric('staveRepetition.symbolText.offsetX');
symbol_x =
text_x +
ctx.measureText(text).width +
Tables.currentMusicFont().lookupMetric('staveRepetition.symbolText.offsetX');
break;
}
const y =
stave.getYForTopText(stave.getNumLines()) +
this.y_shift +
Tables.currentMusicFont().lookupMetric('staveRepetition.symbolText.offsetY');
if (draw_coda) {
Glyph.renderGlyph(ctx, symbol_x, y, Font.convertSizeToPointValue(this.textFont?.size) * 2, 'coda', {
category: 'coda',
});
}
ctx.fillText(text, text_x, y + 5);
ctx.restore();
return this;
}
}