VexFlow - Copyright (c) Mohit Muthanna 2010. Author: Larry Kuhns.
This file implements articulations and accents as modifiers that can be
attached to notes. The complete list of articulations is available in
tables.js under Vex.Flow.articulationCodes.
See tests/articulation_tests.js for usage examples.
Vex.Flow.Articulation = (function() {
function Articulation(type) {
if (arguments.length > 0) this.init(type);
}
Articulation.CATEGORY = "articulations";To enable logging for this class. Set Vex.Flow.Articulation.DEBUG to true.
function L() { if (Articulation.DEBUG) Vex.L("Vex.Flow.Articulation", arguments); }
var Modifier = Vex.Flow.Modifier;Arrange articulations inside ModifierContext
Articulation.format = function(articulations, state) {
if (!articulations || articulations.length === 0) return false;
var width = 0;
for (var i = 0; i < articulations.length; ++i) {
var increment = 1;
var articulation = articulations[i];
width = Math.max(articulation.getWidth(), width);
var type = Vex.Flow.articulationCodes(articulation.type);
if (!type.between_lines) increment += 1.5;
if (articulation.getPosition() === Modifier.Position.ABOVE) {
articulation.setTextLine(state.top_text_line);
state.top_text_line += increment;
} else {
articulation.setTextLine(state.text_line);
state.text_line += increment;
}
}
state.left_shift += width / 2;
state.right_shift += width / 2;
return true;
}; Vex.Inherit(Articulation, Modifier, {Create a new articulation of type type, which is an entry in
Vex.Flow.articulationCodes in tables.js.
init: function(type) {
Articulation.superclass.init.call(this);
this.note = null;
this.index = null;
this.type = type;
this.position = Modifier.Position.BELOW;
this.render_options = {
font_scale: 38
};
this.articulation = Vex.Flow.articulationCodes(this.type);
if (!this.articulation) throw new Vex.RERR("ArgumentError",
"Articulation not found: '" + this.type + "'");Default width comes from articulation table.
this.setWidth(this.articulation.width);
},Render articulation in position next to note.
draw: function() {
if (!this.context) throw new Vex.RERR("NoContext",
"Can't draw Articulation without a context.");
if (!(this.note && (this.index !== null))) throw new Vex.RERR("NoAttachedNote",
"Can't draw Articulation without a note and index.");
var stem_direction = this.note.getStemDirection();
var stave = this.note.getStave();
var is_on_head = (this.position === Modifier.Position.ABOVE &&
stem_direction === Vex.Flow.StaveNote.STEM_DOWN) ||
(this.position === Modifier.Position.BELOW &&
stem_direction === Vex.Flow.StaveNote.STEM_UP);
var needsLineAdjustment = function(articulation, note_line, line_spacing) {
var offset_direction = (articulation.position === Modifier.Position.ABOVE) ? 1 : -1;
var duration = articulation.getNote().getDuration();
if(!is_on_head && Vex.Flow.durationToNumber(duration) <= 1){Add stem length, unless it’s on a whole note.
note_line += offset_direction * 3.5;
}
var articulation_line = note_line + (offset_direction * line_spacing);
if(articulation_line >= 1 &&
articulation_line <= 5 &&
articulation_line % 1 === 0){
return true;
}
return false;
};Articulations are centered over/under the note head.
var start = this.note.getModifierStartXY(this.position, this.index);
var glyph_y = start.y;
var shiftY = 0;
var line_spacing = 1;
var spacing = stave.getSpacingBetweenLines();
var is_tabnote = this.note.getCategory() === 'tabnotes';
var stem_ext = this.note.getStem().getExtents();
var top = stem_ext.topY;
var bottom = stem_ext.baseY;
if (stem_direction === Vex.Flow.StaveNote.STEM_DOWN) {
top = stem_ext.baseY;
bottom = stem_ext.topY;
}TabNotes don’t have stems attached to them. Tab stems are rendered outside the stave.
if (is_tabnote) {
if (this.note.hasStem()){
if (stem_direction === Vex.Flow.StaveNote.STEM_UP) {
bottom = stave.getYForBottomText(this.text_line - 2);
} else if (stem_direction === Vex.Flow.StaveNote.STEM_DOWN ) {
top = stave.getYForTopText(this.text_line - 1.5);
}
} else { // Without a stem
top = stave.getYForTopText(this.text_line - 1);
bottom = stave.getYForBottomText(this.text_line - 2);
}
}
var is_above = (this.position === Modifier.Position.ABOVE) ? true : false;
var note_line = this.note.getLineNumber(is_above);Beamed stems are longer than quarter note stems.
if (!is_on_head && this.note.beam) line_spacing += 0.5;If articulation will overlap a line, reposition it.
if (needsLineAdjustment(this, note_line, line_spacing)) line_spacing += 0.5;
var glyph_y_between_lines;
if (this.position === Modifier.Position.ABOVE) {
shiftY = this.articulation.shift_up;
glyph_y_between_lines = (top - 7) - (spacing * (this.text_line + line_spacing));
if (this.articulation.between_lines) {
glyph_y = glyph_y_between_lines;
} else {
glyph_y = Math.min(stave.getYForTopText(this.text_line) - 3, glyph_y_between_lines);
}
} else {
shiftY = this.articulation.shift_down - 10;
glyph_y_between_lines = bottom + 10 + spacing * (this.text_line + line_spacing);
if (this.articulation.between_lines) {
glyph_y = glyph_y_between_lines;
} else {
glyph_y = Math.max(stave.getYForBottomText(this.text_line), glyph_y_between_lines);
}
}
var glyph_x = start.x + this.articulation.shift_right;
glyph_y += shiftY + this.y_shift;
L("Rendering articulation: ", this.articulation, glyph_x, glyph_y);
Vex.Flow.renderGlyph(this.context, glyph_x, glyph_y,
this.render_options.font_scale, this.articulation.code);
}
});
return Articulation;
}());