VexFlow - 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;
}
}