UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

131 lines (130 loc) 4.87 kB
import { Beam } from './beam.js'; import { Formatter } from './formatter.js'; import { Modifier } from './modifier.js'; import { StaveNote } from './stavenote.js'; import { StaveTie } from './stavetie.js'; import { Tables } from './tables.js'; import { TabTie } from './tabtie.js'; import { isStaveNote } from './typeguard.js'; import { log } from './util.js'; import { Voice } from './voice.js'; function L(...args) { if (GraceNoteGroup.DEBUG) log('Vex.Flow.GraceNoteGroup', args); } class GraceNoteGroup extends Modifier { static get CATEGORY() { return "GraceNoteGroup"; } static format(gracenote_groups, state) { const group_spacing_stave = 4; const group_spacing_tab = 0; if (!gracenote_groups || gracenote_groups.length === 0) return false; const group_list = []; let prev_note = null; let shiftL = 0; for (let i = 0; i < gracenote_groups.length; ++i) { const gracenote_group = gracenote_groups[i]; const note = gracenote_group.getNote(); const is_stavenote = isStaveNote(note); const spacing = is_stavenote ? group_spacing_stave : group_spacing_tab; if (is_stavenote && note !== prev_note) { for (let n = 0; n < note.keys.length; ++n) { shiftL = Math.max(note.getLeftDisplacedHeadPx(), shiftL); } prev_note = note; } group_list.push({ shift: shiftL, gracenote_group, spacing }); } let group_shift = group_list[0].shift; let formatWidth; for (let i = 0; i < group_list.length; ++i) { const gracenote_group = group_list[i].gracenote_group; gracenote_group.preFormat(); formatWidth = gracenote_group.getWidth() + group_list[i].spacing; group_shift = Math.max(formatWidth, group_shift); } for (let i = 0; i < group_list.length; ++i) { const gracenote_group = group_list[i].gracenote_group; formatWidth = gracenote_group.getWidth() + group_list[i].spacing; gracenote_group.setSpacingFromNextModifier(group_shift - Math.min(formatWidth, group_shift) + StaveNote.minNoteheadPadding); } state.left_shift += group_shift; return true; } constructor(grace_notes, show_slur) { super(); this.preFormatted = false; this.position = Modifier.Position.LEFT; this.grace_notes = grace_notes; this.width = 0; this.show_slur = show_slur; this.slur = undefined; this.voice = new Voice({ num_beats: 4, beat_value: 4, resolution: Tables.RESOLUTION, }).setStrict(false); this.render_options = { slur_y_shift: 0, }; this.beams = []; this.voice.addTickables(this.grace_notes); return this; } preFormat() { if (this.preFormatted) return; if (!this.formatter) { this.formatter = new Formatter(); } this.formatter.joinVoices([this.voice]).format([this.voice], 0, {}); this.setWidth(this.formatter.getMinTotalWidth()); this.preFormatted = true; } beamNotes(grace_notes) { grace_notes = grace_notes || this.grace_notes; if (grace_notes.length > 1) { const beam = new Beam(grace_notes); beam.render_options.beam_width = 3; beam.render_options.partial_beam_length = 4; this.beams.push(beam); } return this; } setWidth(width) { this.width = width; return this; } getWidth() { return this.width + StaveNote.minNoteheadPadding; } getGraceNotes() { return this.grace_notes; } draw() { const ctx = this.checkContext(); const note = this.checkAttachedNote(); this.setRendered(); L('Drawing grace note group for:', note); this.alignSubNotesWithNote(this.getGraceNotes(), note); this.grace_notes.forEach((graceNote) => graceNote.setContext(ctx).draw()); this.beams.forEach((beam) => beam.setContext(ctx).draw()); if (this.show_slur) { const is_stavenote = isStaveNote(note); const TieClass = is_stavenote ? StaveTie : TabTie; this.slur = new TieClass({ last_note: this.grace_notes[0], first_note: note, first_indices: [0], last_indices: [0], }); this.slur.render_options.cp2 = 12; this.slur.render_options.y_shift = (is_stavenote ? 7 : 5) + this.render_options.slur_y_shift; this.slur.setContext(ctx).draw(); } } } GraceNoteGroup.DEBUG = false; export { GraceNoteGroup };