UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

119 lines (118 loc) 4.39 kB
import { Element } from './element.js'; import { RuntimeError } from './util.js'; export var CurvePosition; (function (CurvePosition) { CurvePosition[CurvePosition["NEAR_HEAD"] = 1] = "NEAR_HEAD"; CurvePosition[CurvePosition["NEAR_TOP"] = 2] = "NEAR_TOP"; })(CurvePosition || (CurvePosition = {})); export class Curve extends Element { static get CATEGORY() { return "Curve"; } static get Position() { return CurvePosition; } static get PositionString() { return { nearHead: CurvePosition.NEAR_HEAD, nearTop: CurvePosition.NEAR_TOP, }; } constructor(from, to, options) { super(); this.render_options = Object.assign({ thickness: 2, x_shift: 0, y_shift: 10, position: CurvePosition.NEAR_HEAD, position_end: CurvePosition.NEAR_HEAD, invert: false, cps: [ { x: 0, y: 10 }, { x: 0, y: 10 }, ] }, options); this.from = from; this.to = to; } setNotes(from, to) { if (!from && !to) { throw new RuntimeError('BadArguments', 'Curve needs to have either `from` or `to` set.'); } this.from = from; this.to = to; return this; } isPartial() { return !this.from || !this.to; } renderCurve(params) { const ctx = this.checkContext(); const x_shift = this.render_options.x_shift; const y_shift = this.render_options.y_shift * params.direction; const first_x = params.first_x + x_shift; const first_y = params.first_y + y_shift; const last_x = params.last_x - x_shift; const last_y = params.last_y + y_shift; const thickness = this.render_options.thickness; const cps = this.render_options.cps; const { x: cp0x, y: cp0y } = cps[0]; const { x: cp1x, y: cp1y } = cps[1]; const cp_spacing = (last_x - first_x) / (cps.length + 2); ctx.beginPath(); ctx.moveTo(first_x, first_y); ctx.bezierCurveTo(first_x + cp_spacing + cp0x, first_y + cp0y * params.direction, last_x - cp_spacing + cp1x, last_y + cp1y * params.direction, last_x, last_y); ctx.bezierCurveTo(last_x - cp_spacing + cp1x, last_y + (cp1y + thickness) * params.direction, first_x + cp_spacing + cp0x, first_y + (cp0y + thickness) * params.direction, first_x, first_y); ctx.stroke(); ctx.closePath(); ctx.fill(); } draw() { this.checkContext(); this.setRendered(); const first_note = this.from; const last_note = this.to; let first_x; let last_x; let first_y; let last_y; let stem_direction = 0; let metric = 'baseY'; let end_metric = 'baseY'; function getPosition(position) { return typeof position === 'string' ? Curve.PositionString[position] : position; } const position = getPosition(this.render_options.position); const position_end = getPosition(this.render_options.position_end); if (position === CurvePosition.NEAR_TOP) { metric = 'topY'; end_metric = 'topY'; } if (position_end === CurvePosition.NEAR_HEAD) { end_metric = 'baseY'; } else if (position_end === CurvePosition.NEAR_TOP) { end_metric = 'topY'; } if (first_note) { first_x = first_note.getTieRightX(); stem_direction = first_note.getStemDirection(); first_y = first_note.getStemExtents()[metric]; } else { const stave = last_note.checkStave(); first_x = stave.getTieStartX(); first_y = last_note.getStemExtents()[metric]; } if (last_note) { last_x = last_note.getTieLeftX(); stem_direction = last_note.getStemDirection(); last_y = last_note.getStemExtents()[end_metric]; } else { const stave = first_note.checkStave(); last_x = stave.getTieEndX(); last_y = first_note.getStemExtents()[end_metric]; } this.renderCurve({ first_x, last_x, first_y, last_y, direction: stem_direction * (this.render_options.invert === true ? -1 : 1), }); return true; } }