UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature

162 lines (136 loc) 4.97 kB
// [VexFlow](http://vexflow.com) - Copyright (c) Mohit Muthanna 2010. import { StaveNote } from './stavenote'; import { Stem } from './stem'; import { Flow } from './tables'; export class GraceNote extends StaveNote { static get CATEGORY() { return 'gracenotes'; } static get LEDGER_LINE_OFFSET() { return 2; } static get SCALE() { return 0.66; } constructor(note_struct) { super(Object.assign(note_struct, { glyph_font_scale: Flow.DEFAULT_NOTATION_FONT_SCALE * GraceNote.SCALE, stroke_px: GraceNote.LEDGER_LINE_OFFSET, })); this.setAttribute('type', 'GraceNote'); this.slash = note_struct.slash; this.slur = true; this.buildNoteHeads(); this.width = 3; } getStemExtension() { if (this.stem_extension_override != null) { return this.stem_extension_override; } const glyph = this.getGlyph(); if (glyph) { let ret = super.getStemExtension(); if (glyph.stem) { const staveNoteScale = this.getStaveNoteScale(); ret = ((Stem.HEIGHT + ret) * staveNoteScale) - Stem.HEIGHT; } return ret; } return 0; } getCategory() { return GraceNote.CATEGORY; } // FIXME: move this to more basic class. getStaveNoteScale() { return this.render_options.glyph_font_scale / Flow.DEFAULT_NOTATION_FONT_SCALE; } draw() { super.draw(); this.setRendered(); const stem = this.stem; if (this.slash && stem) { const staveNoteScale = this.getStaveNoteScale(); // some magic numbers are based on the staveNoteScale 0.66. const offsetScale = staveNoteScale / 0.66; let slashBBox = undefined; const beam = this.beam; if (beam) { // FIXME: should render slash after beam? if (!beam.postFormatted) { beam.postFormat(); } slashBBox = this.calcBeamedNotesSlashBBox(8 * offsetScale, 8 * offsetScale, { stem: 6 * offsetScale, beam: 5 * offsetScale, }); } else { const stem_direction = this.getStemDirection(); const noteHeadBounds = this.getNoteHeadBounds(); const noteStemHeight = stem.getHeight(); let x = this.getAbsoluteX(); let y = stem_direction === Flow.Stem.DOWN ? noteHeadBounds.y_top - noteStemHeight : noteHeadBounds.y_bottom - noteStemHeight; const defaultStemExtention = stem_direction === Flow.Stem.DOWN ? this.glyph.stem_down_extension : this.glyph.stem_up_extension; let defaultOffsetY = Flow.STEM_HEIGHT; defaultOffsetY -= (defaultOffsetY / 2.8); defaultOffsetY += defaultStemExtention; y += ((defaultOffsetY * staveNoteScale) * stem_direction); const offsets = stem_direction === Flow.Stem.UP ? { x1: 1, y1: 0, x2: 13, y2: -9, } : { x1: -4, y1: 1, x2: 13, y2: 9, }; x += (offsets.x1 * offsetScale); y += (offsets.y1 * offsetScale); slashBBox = { x1: x, y1: y, x2: x + (offsets.x2 * offsetScale), y2: y + (offsets.y2 * offsetScale), }; } // FIXME: avoide staff lines, leadger lines or others. const ctx = this.context; ctx.save(); ctx.setLineWidth(1 * offsetScale); // FIXME: use more appropriate value. ctx.beginPath(); ctx.moveTo(slashBBox.x1, slashBBox.y1); ctx.lineTo(slashBBox.x2, slashBBox.y2); ctx.closePath(); ctx.stroke(); ctx.restore(); } } calcBeamedNotesSlashBBox(slashStemOffset, slashBeamOffset, protrusions) { const beam = this.beam; const beam_slope = beam.slope; const isBeamEndNote = (beam.notes[beam.notes.length - 1] === this); const scaleX = isBeamEndNote ? -1 : 1; const beam_angle = Math.atan(beam_slope * scaleX); // slash line intersecting point on beam. const iPointOnBeam = { dx: Math.cos(beam_angle) * slashBeamOffset, dy: Math.sin(beam_angle) * slashBeamOffset, }; slashStemOffset *= this.getStemDirection(); const slash_angle = Math.atan((iPointOnBeam.dy - slashStemOffset) / iPointOnBeam.dx); const protrusion_stem_dx = Math.cos(slash_angle) * protrusions.stem * scaleX; const protrusion_stem_dy = Math.sin(slash_angle) * protrusions.stem; const protrusion_beam_dx = Math.cos(slash_angle) * protrusions.beam * scaleX; const protrusion_beam_dy = Math.sin(slash_angle) * protrusions.beam; const stemX = this.getStemX(); const stem0X = beam.notes[0].getStemX(); const stemY = this.beam.getBeamYToDraw() + ((stemX - stem0X) * beam_slope); const ret = { x1: stemX - protrusion_stem_dx, y1: (stemY + slashStemOffset - protrusion_stem_dy), x2: stemX + (iPointOnBeam.dx * scaleX) + protrusion_beam_dx, y2: stemY + iPointOnBeam.dy + protrusion_beam_dy, }; return ret; } }