VexFlow - Copyright (c) Mohit Muthanna Cheppudira 2013. Co-author: Benjamin W. Bohl
This file implements various types of clefs that can be rendered on a stave.
See tests/clef_tests.js
for usage examples.
import { Vex } from './vex';
import { StaveModifier } from './stavemodifier';
import { Glyph } from './glyph';
To enable logging for this class, set Vex.Flow.Clef.DEBUG
to true
.
function L(...args) { if (Clef.DEBUG) Vex.L('Vex.Flow.Clef', args); }
export class Clef extends StaveModifier {
static get CATEGORY() { return 'clefs'; }
Every clef name is associated with a glyph code from the font file and a default stave line number.
static get types() {
return {
'treble': {
code: 'v83',
line: 3,
},
'bass': {
code: 'v79',
line: 1,
},
'alto': {
code: 'vad',
line: 2,
},
'tenor': {
code: 'vad',
line: 1,
},
'percussion': {
code: 'v59',
line: 2,
},
'soprano': {
code: 'vad',
line: 4,
},
'mezzo-soprano': {
code: 'vad',
line: 3,
},
'baritone-c': {
code: 'vad',
line: 0,
},
'baritone-f': {
code: 'v79',
line: 2,
},
'subbass': {
code: 'v79',
line: 0,
},
'french': {
code: 'v83',
line: 4,
},
'tab': {
code: 'v2f',
},
};
}
Sizes affect the point-size of the clef.
static get sizes() {
return {
'default': 40,
'small': 32,
};
}
Annotations attach to clefs – such as “8” for octave up or down.
static get annotations() {
return {
'8va': {
code: 'v8',
sizes: {
'default': {
point: 20,
attachments: {
'treble': {
line: -1.2,
x_shift: 11,
},
},
},
'small': {
point: 18,
attachments: {
'treble': {
line: -0.4,
x_shift: 8,
},
},
},
},
},
'8vb': {
code: 'v8',
sizes: {
'default': {
point: 20,
attachments: {
'treble': {
line: 6.3,
x_shift: 10,
},
'bass': {
line: 4,
x_shift: 1,
},
},
},
'small': {
point: 18,
attachments: {
'treble': {
line: 5.8,
x_shift: 6,
},
'bass': {
line: 3.5,
x_shift: 0.5,
},
},
},
},
},
};
}
Create a new clef. The parameter clef
must be a key from
Clef.types
.
constructor(type, size, annotation) {
super();
this.attrs.type = 'Clef';
this.setPosition(StaveModifier.Position.BEGIN);
this.setType(type, size, annotation);
this.setWidth(this.glyph.getMetrics().width);
L('Creating clef:', type);
}
getCategory() { return Clef.CATEGORY; }
setType(type, size, annotation) {
this.type = type;
this.clef = Clef.types[type];
if (size === undefined) {
this.size = 'default';
} else {
this.size = size;
}
this.clef.point = Clef.sizes[this.size];
this.glyph = new Glyph(this.clef.code, this.clef.point);
If an annotation, such as 8va, is specified, add it to the Clef object.
if (annotation !== undefined) {
const anno_dict = Clef.annotations[annotation];
this.annotation = {
code: anno_dict.code,
point: anno_dict.sizes[this.size].point,
line: anno_dict.sizes[this.size].attachments[this.type].line,
x_shift: anno_dict.sizes[this.size].attachments[this.type].x_shift,
};
this.attachment = new Glyph(this.annotation.code, this.annotation.point);
this.attachment.metrics.x_max = 0;
this.attachment.setXShift(this.annotation.x_shift);
} else {
this.annotation = undefined;
}
return this;
}
getWidth() {
if (this.type === 'tab' && !this.stave) {
throw new Vex.RERR('ClefError', "Can't get width without stave.");
}
return this.width;
}
setStave(stave) {
this.stave = stave;
if (this.type !== 'tab') return this;
let glyphScale;
let glyphOffset;
const numLines = this.stave.getOptions().num_lines;
switch (numLines) {
case 8:
glyphScale = 55;
glyphOffset = 14;
break;
case 7:
glyphScale = 47;
glyphOffset = 8;
break;
case 6:
glyphScale = 40;
glyphOffset = 1;
break;
case 5:
glyphScale = 30;
glyphOffset = -6;
break;
case 4:
glyphScale = 23;
glyphOffset = -12;
break;
default:
throw new Vex.RERR('ClefError', `Invalid number of lines: ${numLines}`);
}
this.glyph.setPoint(glyphScale);
this.glyph.setYShift(glyphOffset);
return this;
}
draw() {
if (!this.x) throw new Vex.RERR('ClefError', "Can't draw clef without x.");
if (!this.stave) throw new Vex.RERR('ClefError', "Can't draw clef without stave.");
this.glyph.setStave(this.stave);
this.glyph.setContext(this.stave.context);
if (this.clef.line !== undefined) {
this.placeGlyphOnLine(this.glyph, this.stave, this.clef.line);
}
this.glyph.renderToStave(this.x);
if (this.annotation !== undefined) {
this.placeGlyphOnLine(this.attachment, this.stave, this.annotation.line);
this.attachment.setStave(this.stave);
this.attachment.setContext(this.stave.context);
this.attachment.renderToStave(this.x);
}
}
}