UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

170 lines (169 loc) 6.31 kB
import { BoundingBox } from './boundingbox.js'; import { Glyph } from './glyph.js'; import { Note } from './note.js'; import { Stem } from './stem.js'; import { Tables } from './tables.js'; import { defined, log } from './util.js'; function L(...args) { if (NoteHead.DEBUG) log('Vex.Flow.NoteHead', args); } function drawSlashNoteHead(ctx, duration, x, y, stem_direction, staveSpace) { const width = Tables.SLASH_NOTEHEAD_WIDTH; ctx.save(); ctx.setLineWidth(Tables.STEM_WIDTH); let fill = false; if (Tables.durationToNumber(duration) > 2) { fill = true; } if (!fill) x -= (Tables.STEM_WIDTH / 2) * stem_direction; ctx.beginPath(); ctx.moveTo(x, y + staveSpace); ctx.lineTo(x, y + 1); ctx.lineTo(x + width, y - staveSpace); ctx.lineTo(x + width, y); ctx.lineTo(x, y + staveSpace); ctx.closePath(); if (fill) { ctx.fill(); } else { ctx.stroke(); } if (Tables.durationToFraction(duration).equals(0.5)) { const breve_lines = [-3, -1, width + 1, width + 3]; for (let i = 0; i < breve_lines.length; i++) { ctx.beginPath(); ctx.moveTo(x + breve_lines[i], y - 10); ctx.lineTo(x + breve_lines[i], y + 11); ctx.stroke(); } } ctx.restore(); } class NoteHead extends Note { static get CATEGORY() { return "NoteHead"; } constructor(noteStruct) { super(noteStruct); this.custom_glyph = false; this.stem_up_x_offset = 0; this.stem_down_x_offset = 0; this.index = noteStruct.index; this.x = noteStruct.x || 0; this.y = noteStruct.y || 0; if (noteStruct.note_type) this.noteType = noteStruct.note_type; this.displaced = noteStruct.displaced || false; this.stem_direction = noteStruct.stem_direction || Stem.UP; this.line = noteStruct.line || 0; this.glyphProps = Tables.getGlyphProps(this.duration, this.noteType); defined(this.glyphProps, 'BadArguments', `No glyph found for duration '${this.duration}' and type '${this.noteType}'`); if ((this.line > 5 || this.line < 0) && this.glyphProps.ledger_code_head) { this.glyphProps.code_head = this.glyphProps.ledger_code_head; } this.glyph_code = this.glyphProps.code_head; this.x_shift = noteStruct.x_shift || 0; if (noteStruct.custom_glyph_code) { this.custom_glyph = true; this.glyph_code = noteStruct.custom_glyph_code; this.stem_up_x_offset = noteStruct.stem_up_x_offset || 0; this.stem_down_x_offset = noteStruct.stem_down_x_offset || 0; } this.setStyle(noteStruct.style); this.slashed = noteStruct.slashed || false; this.render_options = Object.assign(Object.assign({}, this.render_options), { glyph_font_scale: noteStruct.glyph_font_scale || Tables.NOTATION_FONT_SCALE }); this.setWidth(this.custom_glyph && !this.glyph_code.startsWith('noteheadSlashed') && !this.glyph_code.startsWith('noteheadCircled') ? Glyph.getWidth(this.glyph_code, this.render_options.glyph_font_scale) : this.glyphProps.getWidth(this.render_options.glyph_font_scale)); } getWidth() { return this.width; } isDisplaced() { return this.displaced === true; } setX(x) { this.x = x; return this; } getY() { return this.y; } setY(y) { this.y = y; return this; } getLine() { return this.line; } setLine(line) { this.line = line; return this; } getAbsoluteX() { const x = !this.preFormatted ? this.x : super.getAbsoluteX(); const displacementStemAdjustment = Stem.WIDTH / 2; const musicFont = Tables.currentMusicFont(); const fontShift = musicFont.lookupMetric('notehead.shiftX', 0) * this.stem_direction; const displacedFontShift = musicFont.lookupMetric('noteHead.displacedShiftX', 0) * this.stem_direction; return (x + fontShift + (this.displaced ? (this.width - displacementStemAdjustment) * this.stem_direction + displacedFontShift : 0)); } getBoundingBox() { const spacing = this.checkStave().getSpacingBetweenLines(); const half_spacing = spacing / 2; const min_y = this.y - half_spacing; return new BoundingBox(this.getAbsoluteX(), min_y, this.width, spacing); } setStave(stave) { const line = this.getLine(); this.stave = stave; if (this.stave) { this.setY(this.stave.getYForNote(line)); this.setContext(this.stave.getContext()); } return this; } preFormat() { if (this.preFormatted) return this; const width = this.getWidth() + this.leftDisplacedHeadPx + this.rightDisplacedHeadPx; this.setWidth(width); this.preFormatted = true; return this; } draw() { const ctx = this.checkContext(); this.setRendered(); let head_x = this.getAbsoluteX(); if (this.custom_glyph) { head_x += this.stem_direction === Stem.UP ? this.stem_up_x_offset + (this.glyphProps.stem ? this.glyphProps.getWidth(this.render_options.glyph_font_scale) - this.width : 0) : this.stem_down_x_offset; } const y = this.y; L("Drawing note head '", this.noteType, this.duration, "' at", head_x, y); const stem_direction = this.stem_direction; const glyph_font_scale = this.render_options.glyph_font_scale; const categorySuffix = `${this.glyph_code}Stem${stem_direction === Stem.UP ? 'Up' : 'Down'}`; if (this.noteType === 's') { const staveSpace = this.checkStave().getSpacingBetweenLines(); drawSlashNoteHead(ctx, this.duration, head_x, y, stem_direction, staveSpace); } else { Glyph.renderGlyph(ctx, head_x, y, glyph_font_scale, this.glyph_code, { category: `noteHead.${categorySuffix}`, }); } } } NoteHead.DEBUG = false; export { NoteHead };