UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

196 lines (195 loc) 6.64 kB
import { Glyph } from './glyph.js'; import { Note } from './note.js'; import { Stem } from './stem.js'; import { Tables } from './tables.js'; import { RuntimeError } from './util.js'; export class StemmableNote extends Note { static get CATEGORY() { return "StemmableNote"; } constructor(noteStruct) { super(noteStruct); } getStem() { return this.stem; } checkStem() { if (!this.stem) { throw new RuntimeError('NoStem', 'No stem attached to instance'); } return this.stem; } setStem(stem) { this.stem = stem; this.addChildElement(stem); return this; } buildStem() { const stem = new Stem(); this.setStem(stem); return this; } buildFlag(category = 'flag') { const { glyphProps } = this; if (this.hasFlag()) { const flagCode = this.getStemDirection() === Stem.DOWN ? glyphProps.code_flag_downstem : glyphProps.code_flag_upstem; if (flagCode) this.flag = new Glyph(flagCode, this.render_options.glyph_font_scale, { category }); } } getBaseCustomNoteHeadGlyphProps() { if (this.getStemDirection() === Stem.DOWN) { return this.customGlyphs[this.customGlyphs.length - 1]; } else { return this.customGlyphs[0]; } } getStemLength() { return Stem.HEIGHT + this.getStemExtension(); } getBeamCount() { const glyphProps = this.getGlyphProps(); if (glyphProps) { return glyphProps.beam_count; } else { return 0; } } getStemMinimumLength() { const frac = Tables.durationToFraction(this.duration); let length = frac.value() <= 1 ? 0 : 20; switch (this.duration) { case '8': if (this.beam == undefined) length = 35; break; case '16': length = this.beam == undefined ? 35 : 25; break; case '32': length = this.beam == undefined ? 45 : 35; break; case '64': length = this.beam == undefined ? 50 : 40; break; case '128': length = this.beam == undefined ? 55 : 45; break; default: break; } return length; } getStemDirection() { if (!this.stem_direction) throw new RuntimeError('NoStem', 'No stem attached to this note.'); return this.stem_direction; } setStemDirection(direction) { if (!direction) direction = Stem.UP; if (direction !== Stem.UP && direction !== Stem.DOWN) { throw new RuntimeError('BadArgument', `Invalid stem direction: ${direction}`); } this.stem_direction = direction; this.reset(); if (this.hasFlag()) { this.buildFlag(); } this.beam = undefined; if (this.stem) { this.stem.setDirection(direction); this.stem.setExtension(this.getStemExtension()); const glyphProps = this.getBaseCustomNoteHeadGlyphProps() || this.getGlyphProps(); const offsets = Tables.currentMusicFont().lookupMetric(`stem.noteHead.${glyphProps.code_head}`, { offsetYBaseStemUp: 0, offsetYTopStemUp: 0, offsetYBaseStemDown: 0, offsetYTopStemDown: 0, }); this.stem.setOptions({ stem_up_y_offset: offsets.offsetYTopStemUp, stem_down_y_offset: offsets.offsetYTopStemDown, stem_up_y_base_offset: offsets.offsetYBaseStemUp, stem_down_y_base_offset: offsets.offsetYBaseStemDown, }); } if (this.preFormatted) { this.preFormat(); } return this; } getStemX() { const x_begin = this.getAbsoluteX() + this.x_shift; const x_end = this.getAbsoluteX() + this.x_shift + this.getGlyphWidth(); const stem_x = this.stem_direction === Stem.DOWN ? x_begin : x_end; return stem_x; } getCenterGlyphX() { return this.getAbsoluteX() + this.x_shift + this.getGlyphWidth() / 2; } getStemExtension() { const glyphProps = this.getGlyphProps(); if (this.stem_extension_override != undefined) { return this.stem_extension_override; } if (this.beam) { return glyphProps.stem_beam_extension; } if (glyphProps) { return this.getStemDirection() === Stem.UP ? glyphProps.stem_up_extension : glyphProps.stem_down_extension; } return 0; } setStemLength(height) { this.stem_extension_override = height - Stem.HEIGHT; return this; } getStemExtents() { if (!this.stem) throw new RuntimeError('NoStem', 'No stem attached to this note.'); return this.stem.getExtents(); } getYForTopText(textLine) { const stave = this.checkStave(); if (this.hasStem()) { const extents = this.getStemExtents(); if (!extents) throw new RuntimeError('InvalidState', 'Stem does not have extents.'); return Math.min(stave.getYForTopText(textLine), extents.topY - this.render_options.annotation_spacing * (textLine + 1)); } else { return stave.getYForTopText(textLine); } } getYForBottomText(textLine) { const stave = this.checkStave(); if (this.hasStem()) { const extents = this.getStemExtents(); if (!extents) throw new RuntimeError('InvalidState', 'Stem does not have extents.'); return Math.max(stave.getYForTopText(textLine), extents.baseY + this.render_options.annotation_spacing * textLine); } else { return stave.getYForBottomText(textLine); } } hasFlag() { return Tables.getGlyphProps(this.duration).flag == true && !this.beam; } postFormat() { var _a; (_a = this.beam) === null || _a === void 0 ? void 0 : _a.postFormat(); this.postFormatted = true; return this; } drawStem(stemOptions) { var _a; this.checkContext(); this.setRendered(); this.setStem(new Stem(stemOptions)); (_a = this.stem) === null || _a === void 0 ? void 0 : _a.setContext(this.getContext()).draw(); } }