VexFlow - Copyright (c) Mohit Muthanna 2010.
This file implements GraceNoteGroup which is used to format and
render grace notes.
Vex.Flow.GraceNoteGroup = (function(){
function GraceNoteGroup(grace_notes, config) {
if (arguments.length > 0) this.init(grace_notes, config);
}
GraceNoteGroup.CATEGORY = "gracenotegroups";To enable logging for this class. Set Vex.Flow.GraceNoteGroup.DEBUG to true.
function L() { if (GraceNoteGroup.DEBUG) Vex.L("Vex.Flow.GraceNoteGroup", arguments); }Arrange groups inside a ModifierContext
GraceNoteGroup.format = function(gracenote_groups, state) {
var gracenote_spacing = 4;
if (!gracenote_groups || gracenote_groups.length === 0) return false;
var group_list = [];
var hasStave = false;
var prev_note = null;
var shiftL = 0;
var i, gracenote_group, props_tmp;
for (i = 0; i < gracenote_groups.length; ++i) {
gracenote_group = gracenote_groups[i];
var note = gracenote_group.getNote();
var stave = note.getStave();
if (note != prev_note) {Iterate through all notes to get the displaced pixels
for (var n = 0; n < note.keys.length; ++n) {
props_tmp = note.getKeyProps()[n];
shiftL = (props_tmp.displaced ? note.getExtraLeftPx() : shiftL);
}
prev_note = note;
}
if (stave != null) {
hasStave = true;
group_list.push({shift: shiftL, gracenote_group: gracenote_group});
} else {
group_list.push({shift: shiftL, gracenote_group: gracenote_group });
}
}If first note left shift in case it is displaced
var group_shift = group_list[0].shift;
var formatWidth;
for (i = 0; i < group_list.length; ++i) {
gracenote_group = group_list[i].gracenote_group;
gracenote_group.preFormat();
formatWidth = gracenote_group.getWidth() + gracenote_spacing;
group_shift = Math.max(formatWidth, group_shift);
}
for (i = 0; i < group_list.length; ++i) {
gracenote_group = group_list[i].gracenote_group;
formatWidth = gracenote_group.getWidth() + gracenote_spacing;
gracenote_group.setSpacingFromNextModifier(group_shift - Math.min(formatWidth, group_shift));
}
state.left_shift += group_shift;
return true;
};GraceNoteGroup inherits from Modifier and is placed inside a
ModifierContext.
Vex.Inherit(GraceNoteGroup, Vex.Flow.Modifier, {
init: function(grace_notes, show_slur) {
var superclass = GraceNoteGroup.superclass;
superclass.init.call(this);
this.note = null;
this.index = null;
this.position = Vex.Flow.Modifier.Position.LEFT;
this.grace_notes = grace_notes;
this.width = 0;
this.preFormatted = false;
this.show_slur = show_slur;
this.slur = null;
this.formatter = new Vex.Flow.Formatter();
this.voice = new Vex.Flow.Voice({
num_beats: 4,
beat_value: 4,
resolution: Vex.Flow.RESOLUTION
}).setStrict(false);
this.voice.addTickables(this.grace_notes);
return this;
},
preFormat: function(){
if (this.preFormatted) return;
this.formatter.joinVoices([this.voice]).format([this.voice], 0);
this.setWidth(this.formatter.getMinTotalWidth());
this.preFormatted = true;
},
beamNotes: function(){
if (this.grace_notes.length > 1) {
var beam = new Vex.Flow.Beam(this.grace_notes);
beam.render_options.beam_width = 3;
beam.render_options.partial_beam_length = 4;
this.beam = beam;
}
return this;
},
setNote: function(note) {
this.note = note;
},
setWidth: function(width){
this.width = width;
},
getWidth: function(){
return this.width;
},
draw: function() {
if (!this.context) {
throw new Vex.RuntimeError("NoContext",
"Can't draw Grace note without a context.");
}
var note = this.getNote();
L("Drawing grace note group for:", note);
if (!(note && (this.index !== null))) {
throw new Vex.RuntimeError("NoAttachedNote",
"Can't draw grace note without a parent note and parent note index.");
}
var that = this;
function alignGraceNotesWithNote(grace_notes, note, groupWidth) {Shift over the tick contexts of each note So that th aligned with the note
var tickContext = note.getTickContext();
var extraPx = tickContext.getExtraPx();
var x = tickContext.getX() - extraPx.left - extraPx.extraLeft + that.getSpacingFromNextModifier();
grace_notes.forEach(function(graceNote) {
var tick_context = graceNote.getTickContext();
var x_offset = tick_context.getX();
graceNote.setStave(note.stave);
tick_context.setX(x + x_offset);
});
}
alignGraceNotesWithNote(this.grace_notes, note, this.width);Draw notes
this.grace_notes.forEach(function(graceNote) {
graceNote.setContext(this.context).draw();
}, this);Draw beam
if (this.beam) {
this.beam.setContext(this.context).draw();
}
if (this.show_slur) {Create and draw slur
this.slur = new Vex.Flow.StaveTie({
last_note: this.grace_notes[0],
first_note: note,
first_indices: [0],
last_indices: [0]
});
this.slur.render_options.cp2 = 12;
this.slur.setContext(this.context).draw();
}
}
});
return GraceNoteGroup;
}());