vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature
2,211 lines • 95.6 kB
HTML
<!DOCTYPE html>
<html>
<head>
<title>stavenote.js</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" media="all" href="public/stylesheets/normalize.css" />
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div class="container">
<div class="page">
<div class="header">
<h1>stavenote.js</h1>
<div class="toc">
<h3>Table of Contents</h3>
<ol>
<li>
<a class="source" href="accidental.html">
accidental.js
</a>
</li>
<li>
<a class="source" href="annotation.html">
annotation.js
</a>
</li>
<li>
<a class="source" href="articulation.html">
articulation.js
</a>
</li>
<li>
<a class="source" href="barnote.html">
barnote.js
</a>
</li>
<li>
<a class="source" href="beam.html">
beam.js
</a>
</li>
<li>
<a class="source" href="bend.html">
bend.js
</a>
</li>
<li>
<a class="source" href="boundingbox.html">
boundingbox.js
</a>
</li>
<li>
<a class="source" href="boundingboxcomputation.html">
boundingboxcomputation.js
</a>
</li>
<li>
<a class="source" href="canvascontext.html">
canvascontext.js
</a>
</li>
<li>
<a class="source" href="clef.html">
clef.js
</a>
</li>
<li>
<a class="source" href="clefnote.html">
clefnote.js
</a>
</li>
<li>
<a class="source" href="crescendo.html">
crescendo.js
</a>
</li>
<li>
<a class="source" href="curve.html">
curve.js
</a>
</li>
<li>
<a class="source" href="dot.html">
dot.js
</a>
</li>
<li>
<a class="source" href="easyscore.html">
easyscore.js
</a>
</li>
<li>
<a class="source" href="element.html">
element.js
</a>
</li>
<li>
<a class="source" href="factory.html">
factory.js
</a>
</li>
<li>
<a class="source" href="formatter.html">
formatter.js
</a>
</li>
<li>
<a class="source" href="fraction.html">
fraction.js
</a>
</li>
<li>
<a class="source" href="frethandfinger.html">
frethandfinger.js
</a>
</li>
<li>
<a class="source" href="ghostnote.html">
ghostnote.js
</a>
</li>
<li>
<a class="source" href="glyph.html">
glyph.js
</a>
</li>
<li>
<a class="source" href="gracenote.html">
gracenote.js
</a>
</li>
<li>
<a class="source" href="gracenotegroup.html">
gracenotegroup.js
</a>
</li>
<li>
<a class="source" href="gracetabnote.html">
gracetabnote.js
</a>
</li>
<li>
<a class="source" href="index.html">
index.js
</a>
</li>
<li>
<a class="source" href="keymanager.html">
keymanager.js
</a>
</li>
<li>
<a class="source" href="keysignature.html">
keysignature.js
</a>
</li>
<li>
<a class="source" href="keysignote.html">
keysignote.js
</a>
</li>
<li>
<a class="source" href="modifier.html">
modifier.js
</a>
</li>
<li>
<a class="source" href="modifiercontext.html">
modifiercontext.js
</a>
</li>
<li>
<a class="source" href="multimeasurerest.html">
multimeasurerest.js
</a>
</li>
<li>
<a class="source" href="music.html">
music.js
</a>
</li>
<li>
<a class="source" href="note.html">
note.js
</a>
</li>
<li>
<a class="source" href="notehead.html">
notehead.js
</a>
</li>
<li>
<a class="source" href="notesubgroup.html">
notesubgroup.js
</a>
</li>
<li>
<a class="source" href="ornament.html">
ornament.js
</a>
</li>
<li>
<a class="source" href="parser.html">
parser.js
</a>
</li>
<li>
<a class="source" href="pedalmarking.html">
pedalmarking.js
</a>
</li>
<li>
<a class="source" href="raphaelcontext.html">
raphaelcontext.js
</a>
</li>
<li>
<a class="source" href="registry.html">
registry.js
</a>
</li>
<li>
<a class="source" href="renderer.html">
renderer.js
</a>
</li>
<li>
<a class="source" href="stave.html">
stave.js
</a>
</li>
<li>
<a class="source" href="stavebarline.html">
stavebarline.js
</a>
</li>
<li>
<a class="source" href="staveconnector.html">
staveconnector.js
</a>
</li>
<li>
<a class="source" href="stavehairpin.html">
stavehairpin.js
</a>
</li>
<li>
<a class="source" href="staveline.html">
staveline.js
</a>
</li>
<li>
<a class="source" href="stavemodifier.html">
stavemodifier.js
</a>
</li>
<li>
<a class="source" href="stavenote.html">
stavenote.js
</a>
</li>
<li>
<a class="source" href="staverepetition.html">
staverepetition.js
</a>
</li>
<li>
<a class="source" href="stavesection.html">
stavesection.js
</a>
</li>
<li>
<a class="source" href="stavetempo.html">
stavetempo.js
</a>
</li>
<li>
<a class="source" href="stavetext.html">
stavetext.js
</a>
</li>
<li>
<a class="source" href="stavetie.html">
stavetie.js
</a>
</li>
<li>
<a class="source" href="stavevolta.html">
stavevolta.js
</a>
</li>
<li>
<a class="source" href="stem.html">
stem.js
</a>
</li>
<li>
<a class="source" href="stemmablenote.html">
stemmablenote.js
</a>
</li>
<li>
<a class="source" href="stringnumber.html">
stringnumber.js
</a>
</li>
<li>
<a class="source" href="strokes.html">
strokes.js
</a>
</li>
<li>
<a class="source" href="svgcontext.html">
svgcontext.js
</a>
</li>
<li>
<a class="source" href="system.html">
system.js
</a>
</li>
<li>
<a class="source" href="tables.html">
tables.js
</a>
</li>
<li>
<a class="source" href="tabnote.html">
tabnote.js
</a>
</li>
<li>
<a class="source" href="tabslide.html">
tabslide.js
</a>
</li>
<li>
<a class="source" href="tabstave.html">
tabstave.js
</a>
</li>
<li>
<a class="source" href="tabtie.html">
tabtie.js
</a>
</li>
<li>
<a class="source" href="textbracket.html">
textbracket.js
</a>
</li>
<li>
<a class="source" href="textdynamics.html">
textdynamics.js
</a>
</li>
<li>
<a class="source" href="textnote.html">
textnote.js
</a>
</li>
<li>
<a class="source" href="tickable.html">
tickable.js
</a>
</li>
<li>
<a class="source" href="tickcontext.html">
tickcontext.js
</a>
</li>
<li>
<a class="source" href="timesignature.html">
timesignature.js
</a>
</li>
<li>
<a class="source" href="timesignote.html">
timesignote.js
</a>
</li>
<li>
<a class="source" href="tremolo.html">
tremolo.js
</a>
</li>
<li>
<a class="source" href="tuning.html">
tuning.js
</a>
</li>
<li>
<a class="source" href="tuplet.html">
tuplet.js
</a>
</li>
<li>
<a class="source" href="vex.html">
vex.js
</a>
</li>
<li>
<a class="source" href="vibrato.html">
vibrato.js
</a>
</li>
<li>
<a class="source" href="vibratobracket.html">
vibratobracket.js
</a>
</li>
<li>
<a class="source" href="voice.html">
voice.js
</a>
</li>
<li>
<a class="source" href="voicegroup.html">
voicegroup.js
</a>
</li>
</ol>
</div>
</div>
<p><a href="http://vexflow.com">VexFlow</a> - Copyright (c) Mohit Muthanna 2010.</p>
<h2 id="description">Description</h2>
<p>This file implements notes for standard notation. This consists of one or
more <code>NoteHeads</code>, an optional stem, and an optional flag.</p>
<p><em>Throughout these comments, a “note” refers to the entire <code>StaveNote</code>,
and a “key” refers to a specific pitch/notehead within a note.</em></p>
<p>See <code>tests/stavenote_tests.js</code> for usage examples.</p>
<div class='highlight'><pre>
<span class="hljs-keyword">import</span> { Vex } <span class="hljs-keyword">from</span> <span class="hljs-string">'./vex'</span>;
<span class="hljs-keyword">import</span> { Flow } <span class="hljs-keyword">from</span> <span class="hljs-string">'./tables'</span>;
<span class="hljs-keyword">import</span> { BoundingBox } <span class="hljs-keyword">from</span> <span class="hljs-string">'./boundingbox'</span>;
<span class="hljs-keyword">import</span> { Stem } <span class="hljs-keyword">from</span> <span class="hljs-string">'./stem'</span>;
<span class="hljs-keyword">import</span> { NoteHead } <span class="hljs-keyword">from</span> <span class="hljs-string">'./notehead'</span>;
<span class="hljs-keyword">import</span> { StemmableNote } <span class="hljs-keyword">from</span> <span class="hljs-string">'./stemmablenote'</span>;
<span class="hljs-keyword">import</span> { Modifier } <span class="hljs-keyword">from</span> <span class="hljs-string">'./modifier'</span>;
<span class="hljs-keyword">import</span> { Dot } <span class="hljs-keyword">from</span> <span class="hljs-string">'./dot'</span>;</pre></div>
<p>To enable logging for this class. Set <code>Vex.Flow.StaveNote.DEBUG</code> to <code>true</code>.</p>
<div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">L</span>(<span class="hljs-params">...args</span>) </span>{ <span class="hljs-keyword">if</span> (StaveNote.DEBUG) Vex.L(<span class="hljs-string">'Vex.Flow.StaveNote'</span>, args); }
<span class="hljs-keyword">const</span> getStemAdjustment = <span class="hljs-function">(<span class="hljs-params">note</span>) =></span> Stem.WIDTH / (<span class="hljs-number">2</span> * -note.getStemDirection());
<span class="hljs-keyword">const</span> isInnerNoteIndex = <span class="hljs-function">(<span class="hljs-params">note, index</span>) =></span>
index === (note.getStemDirection() === Stem.UP ? note.keyProps.length - <span class="hljs-number">1</span> : <span class="hljs-number">0</span>);</pre></div>
<p>Helper methods for rest positioning in ModifierContext.</p>
<div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">shiftRestVertical</span>(<span class="hljs-params">rest, note, dir</span>) </span>{
<span class="hljs-keyword">const</span> delta = (note.isrest ? <span class="hljs-number">0.0</span> : <span class="hljs-number">1.0</span>) * dir;
rest.line += delta;
rest.maxLine += delta;
rest.minLine += delta;
rest.note.setKeyLine(<span class="hljs-number">0</span>, rest.note.getKeyLine(<span class="hljs-number">0</span>) + (delta));
}</pre></div>
<p>Called from formatNotes :: center a rest between two notes</p>
<div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">centerRest</span>(<span class="hljs-params">rest, noteU, noteL</span>) </span>{
<span class="hljs-keyword">const</span> delta = rest.line - Vex.MidLine(noteU.minLine, noteL.maxLine);
rest.note.setKeyLine(<span class="hljs-number">0</span>, rest.note.getKeyLine(<span class="hljs-number">0</span>) - delta);
rest.line -= delta;
rest.maxLine -= delta;
rest.minLine -= delta;
}
<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StaveNote</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StemmableNote</span> </span>{
<span class="hljs-keyword">static</span> get CATEGORY() { <span class="hljs-keyword">return</span> <span class="hljs-string">'stavenotes'</span>; }
<span class="hljs-keyword">static</span> get STEM_UP() { <span class="hljs-keyword">return</span> Stem.UP; }
<span class="hljs-keyword">static</span> get STEM_DOWN() { <span class="hljs-keyword">return</span> Stem.DOWN; }
<span class="hljs-keyword">static</span> get DEFAULT_LEDGER_LINE_OFFSET() { <span class="hljs-keyword">return</span> <span class="hljs-number">3</span>; }</pre></div>
<h2 id="static-methods">Static Methods</h2>
<p>Format notes inside a ModifierContext.</p>
<div class='highlight'><pre> <span class="hljs-keyword">static</span> format(notes, state) {
<span class="hljs-keyword">if</span> (!notes || notes.length < <span class="hljs-number">2</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</pre></div>
<p>FIXME: VexFlow will soon require that a stave be set before formatting.
Which, according to the below condition, means that following branch will
always be taken and the rest of this function is dead code.</p>
<p>Problematically, <code>Formatter#formatByY</code> was not designed to work for more
than 2 voices (although, doesn’t throw on this condition, just tries
to power through).</p>
<p>Based on the above:</p>
<ul>
<li>2 voices can be formatted <em>with or without</em> a stave being set but
the output will be different</li>
<li>3 voices can only be formatted <em>without</em> a stave</li>
</ul>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (notes[<span class="hljs-number">0</span>].getStave()) {
<span class="hljs-keyword">return</span> StaveNote.formatByY(notes, state);
}
<span class="hljs-keyword">const</span> notesList = [];
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < notes.length; i++) {
<span class="hljs-keyword">const</span> props = notes[i].getKeyProps();
<span class="hljs-keyword">const</span> line = props[<span class="hljs-number">0</span>].line;
<span class="hljs-keyword">let</span> minL = props[props.length - <span class="hljs-number">1</span>].line;
<span class="hljs-keyword">const</span> stemDirection = notes[i].getStemDirection();
<span class="hljs-keyword">const</span> stemMax = notes[i].getStemLength() / <span class="hljs-number">10</span>;
<span class="hljs-keyword">const</span> stemMin = notes[i].getStemMinumumLength() / <span class="hljs-number">10</span>;
<span class="hljs-keyword">let</span> maxL;
<span class="hljs-keyword">if</span> (notes[i].isRest()) {
maxL = line + notes[i].glyph.line_above;
minL = line - notes[i].glyph.line_below;
} <span class="hljs-keyword">else</span> {
maxL = stemDirection === <span class="hljs-number">1</span>
? props[props.length - <span class="hljs-number">1</span>].line + stemMax
: props[props.length - <span class="hljs-number">1</span>].line;
minL = stemDirection === <span class="hljs-number">1</span>
? props[<span class="hljs-number">0</span>].line
: props[<span class="hljs-number">0</span>].line - stemMax;
}
notesList.push({
<span class="hljs-attr">line</span>: props[<span class="hljs-number">0</span>].line, <span class="hljs-comment">// note/rest base line</span>
maxLine: maxL, <span class="hljs-comment">// note/rest upper bounds line</span>
minLine: minL, <span class="hljs-comment">// note/rest lower bounds line</span>
isrest: notes[i].isRest(),
stemDirection,
stemMax, <span class="hljs-comment">// Maximum (default) note stem length;</span>
stemMin, <span class="hljs-comment">// minimum note stem length</span>
voice_shift: notes[i].getVoiceShiftWidth(),
<span class="hljs-attr">is_displaced</span>: notes[i].isDisplaced(), <span class="hljs-comment">// note manually displaced</span>
note: notes[i],
});
}
<span class="hljs-keyword">const</span> voices = notesList.length;
<span class="hljs-keyword">let</span> noteU = notesList[<span class="hljs-number">0</span>];
<span class="hljs-keyword">const</span> noteM = voices > <span class="hljs-number">2</span> ? notesList[<span class="hljs-number">1</span>] : <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> noteL = voices > <span class="hljs-number">2</span> ? notesList[<span class="hljs-number">2</span>] : notesList[<span class="hljs-number">1</span>];</pre></div>
<p>for two voice backward compatibility, ensure upper voice is stems up
for three voices, the voices must be in order (upper, middle, lower)</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (voices === <span class="hljs-number">2</span> && noteU.stemDirection === <span class="hljs-number">-1</span> && noteL.stemDirection === <span class="hljs-number">1</span>) {
noteU = notesList[<span class="hljs-number">1</span>];
noteL = notesList[<span class="hljs-number">0</span>];
}
<span class="hljs-keyword">const</span> voiceXShift = <span class="hljs-built_in">Math</span>.max(noteU.voice_shift, noteL.voice_shift);
<span class="hljs-keyword">let</span> xShift = <span class="hljs-number">0</span>;
<span class="hljs-keyword">let</span> stemDelta;</pre></div>
<p>Test for two voice note intersection</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (voices === <span class="hljs-number">2</span>) {
<span class="hljs-keyword">const</span> lineSpacing = noteU.stemDirection === noteL.stemDirection ? <span class="hljs-number">0.0</span> : <span class="hljs-number">0.5</span>;</pre></div>
<p>if top voice is a middle voice, check stem intersection with lower voice</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (noteU.stemDirection === noteL.stemDirection &&
noteU.minLine <= noteL.maxLine) {
<span class="hljs-keyword">if</span> (!noteU.isrest) {
stemDelta = <span class="hljs-built_in">Math</span>.abs(noteU.line - (noteL.maxLine + <span class="hljs-number">0.5</span>));
stemDelta = <span class="hljs-built_in">Math</span>.max(stemDelta, noteU.stemMin);
noteU.minLine = noteU.line - stemDelta;
noteU.note.setStemLength(stemDelta * <span class="hljs-number">10</span>);
}
}
<span class="hljs-keyword">if</span> (noteU.minLine <= noteL.maxLine + lineSpacing) {
<span class="hljs-keyword">if</span> (noteU.isrest) {</pre></div>
<p>shift rest up</p>
<div class='highlight'><pre> shiftRestVertical(noteU, noteL, <span class="hljs-number">1</span>);
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (noteL.isrest) {</pre></div>
<p>shift rest down</p>
<div class='highlight'><pre> shiftRestVertical(noteL, noteU, <span class="hljs-number">-1</span>);
} <span class="hljs-keyword">else</span> {
xShift = voiceXShift;
<span class="hljs-keyword">if</span> (noteU.stemDirection === noteL.stemDirection) {</pre></div>
<p>upper voice is middle voice, so shift it right</p>
<div class='highlight'><pre> noteU.note.setXShift(xShift + <span class="hljs-number">3</span>);
} <span class="hljs-keyword">else</span> {</pre></div>
<p>shift lower voice right</p>
<div class='highlight'><pre> noteL.note.setXShift(xShift);
}
}
}</pre></div>
<p>format complete</p>
<div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}</pre></div>
<p>Check middle voice stem intersection with lower voice</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (noteM !== <span class="hljs-literal">null</span> && noteM.minLine < noteL.maxLine + <span class="hljs-number">0.5</span>) {
<span class="hljs-keyword">if</span> (!noteM.isrest) {
stemDelta = <span class="hljs-built_in">Math</span>.abs(noteM.line - (noteL.maxLine + <span class="hljs-number">0.5</span>));
stemDelta = <span class="hljs-built_in">Math</span>.max(stemDelta, noteM.stemMin);
noteM.minLine = noteM.line - stemDelta;
noteM.note.setStemLength(stemDelta * <span class="hljs-number">10</span>);
}
}</pre></div>
<p>For three voices, test if rests can be repositioned</p>
<p>Special case 1 :: middle voice rest between two notes</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (noteM.isrest && !noteU.isrest && !noteL.isrest) {
<span class="hljs-keyword">if</span> (noteU.minLine <= noteM.maxLine || noteM.minLine <= noteL.maxLine) {
<span class="hljs-keyword">const</span> restHeight = noteM.maxLine - noteM.minLine;
<span class="hljs-keyword">const</span> space = noteU.minLine - noteL.maxLine;
<span class="hljs-keyword">if</span> (restHeight < space) {</pre></div>
<p>center middle voice rest between the upper and lower voices</p>
<div class='highlight'><pre> centerRest(noteM, noteU, noteL);
} <span class="hljs-keyword">else</span> {
xShift = voiceXShift + <span class="hljs-number">3</span>; <span class="hljs-comment">// shift middle rest right</span>
noteM.note.setXShift(xShift);
}</pre></div>
<p>format complete</p>
<div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
}</pre></div>
<p>Special case 2 :: all voices are rests</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (noteU.isrest && noteM.isrest && noteL.isrest) {</pre></div>
<p>Shift upper voice rest up</p>
<div class='highlight'><pre> shiftRestVertical(noteU, noteM, <span class="hljs-number">1</span>);</pre></div>
<p>Shift lower voice rest down</p>
<div class='highlight'><pre> shiftRestVertical(noteL, noteM, <span class="hljs-number">-1</span>);</pre></div>
<p>format complete</p>
<div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}</pre></div>
<p>Test if any other rests can be repositioned</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (noteM.isrest && noteU.isrest && noteM.minLine <= noteL.maxLine) {</pre></div>
<p>Shift middle voice rest up</p>
<div class='highlight'><pre> shiftRestVertical(noteM, noteL, <span class="hljs-number">1</span>);
}
<span class="hljs-keyword">if</span> (noteM.isrest && noteL.isrest && noteU.minLine <= noteM.maxLine) {</pre></div>
<p>Shift middle voice rest down</p>
<div class='highlight'><pre> shiftRestVertical(noteM, noteU, <span class="hljs-number">-1</span>);
}
<span class="hljs-keyword">if</span> (noteU.isrest && noteU.minLine <= noteM.maxLine) {</pre></div>
<p>shift upper voice rest up;</p>
<div class='highlight'><pre> shiftRestVertical(noteU, noteM, <span class="hljs-number">1</span>);
}
<span class="hljs-keyword">if</span> (noteL.isrest && noteM.minLine <= noteL.maxLine) {</pre></div>
<p>shift lower voice rest down</p>
<div class='highlight'><pre> shiftRestVertical(noteL, noteM, <span class="hljs-number">-1</span>);
}</pre></div>
<p>If middle voice intersects upper or lower voice</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> ((!noteU.isrest && !noteM.isrest && noteU.minLine <= noteM.maxLine + <span class="hljs-number">0.5</span>) ||
(!noteM.isrest && !noteL.isrest && noteM.minLine <= noteL.maxLine)) {
xShift = voiceXShift + <span class="hljs-number">3</span>; <span class="hljs-comment">// shift middle note right</span>
noteM.note.setXShift(xShift);
}
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
<span class="hljs-keyword">static</span> formatByY(notes, state) {</pre></div>
<p>NOTE: this function does not support more than two voices per stave
use with care.</p>
<div class='highlight'><pre> <span class="hljs-keyword">let</span> hasStave = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < notes.length; i++) {
hasStave = hasStave && notes[i].getStave() != <span class="hljs-literal">null</span>;
}
<span class="hljs-keyword">if</span> (!hasStave) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(
<span class="hljs-string">'Stave Missing'</span>,
<span class="hljs-string">'All notes must have a stave - Vex.Flow.ModifierContext.formatMultiVoice!'</span>
);
}
<span class="hljs-keyword">let</span> xShift = <span class="hljs-number">0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < notes.length - <span class="hljs-number">1</span>; i++) {
<span class="hljs-keyword">let</span> topNote = notes[i];
<span class="hljs-keyword">let</span> bottomNote = notes[i + <span class="hljs-number">1</span>];
<span class="hljs-keyword">if</span> (topNote.getStemDirection() === Stem.DOWN) {
topNote = notes[i + <span class="hljs-number">1</span>];
bottomNote = notes[i];
}
<span class="hljs-keyword">const</span> topKeys = topNote.getKeyProps();
<span class="hljs-keyword">const</span> bottomKeys = bottomNote.getKeyProps();
<span class="hljs-keyword">const</span> HALF_NOTEHEAD_HEIGHT = <span class="hljs-number">0.5</span>;</pre></div>
<p><code>keyProps</code> and <code>stave.getYForLine</code> have different notions of a <code>line</code>
so we have to convert the keyProps value by subtracting 5.
See <a href="https://github.com/0xfe/vexflow/wiki/Development-Gotchas">https://github.com/0xfe/vexflow/wiki/Development-Gotchas</a></p>
<p>We also extend the y for each note by a half notehead because the
notehead’s origin is centered</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> topNotBottomY = topNote
.getStave()
.getYForLine(<span class="hljs-number">5</span> - topKeys[<span class="hljs-number">0</span>].line + HALF_NOTEHEAD_HEIGHT);
<span class="hljs-keyword">const</span> bottomNoteTopY = bottomNote
.getStave()
.getYForLine(<span class="hljs-number">5</span> - bottomKeys[bottomKeys.length - <span class="hljs-number">1</span>].line - HALF_NOTEHEAD_HEIGHT);
<span class="hljs-keyword">const</span> areNotesColliding = bottomNoteTopY - topNotBottomY < <span class="hljs-number">0</span>;
<span class="hljs-keyword">if</span> (areNotesColliding) {
xShift = topNote.getVoiceShiftWidth() + <span class="hljs-number">2</span>;
bottomNote.setXShift(xShift);
}
}
state.right_shift += xShift;
}
<span class="hljs-keyword">static</span> postFormat(notes) {
<span class="hljs-keyword">if</span> (!notes) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
notes.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =></span> note.postFormat());
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
<span class="hljs-keyword">constructor</span>(noteStruct) {
<span class="hljs-keyword">super</span>(noteStruct);
<span class="hljs-keyword">this</span>.setAttribute(<span class="hljs-string">'type'</span>, <span class="hljs-string">'StaveNote'</span>);
<span class="hljs-keyword">this</span>.keys = noteStruct.keys;
<span class="hljs-keyword">this</span>.clef = noteStruct.clef;
<span class="hljs-keyword">this</span>.octave_shift = noteStruct.octave_shift;
<span class="hljs-keyword">this</span>.beam = <span class="hljs-literal">null</span>;</pre></div>
<p>Pull note rendering properties</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.glyph = Flow.durationToGlyph(<span class="hljs-keyword">this</span>.duration, <span class="hljs-keyword">this</span>.noteType);
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.glyph) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RuntimeError(
<span class="hljs-string">'BadArguments'</span>,
<span class="hljs-string">`Invalid note initialization data (No glyph found): <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(noteStruct)}</span>`</span>
);
}</pre></div>
<p>if true, displace note to right</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.displaced = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">this</span>.dot_shiftY = <span class="hljs-number">0</span>;</pre></div>
<p>per-pitch properties</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.keyProps = [];</pre></div>
<p>for displaced ledger lines</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.use_default_head_x = <span class="hljs-literal">false</span>;</pre></div>
<p>Drawing</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.note_heads = [];
<span class="hljs-keyword">this</span>.modifiers = [];
Vex.Merge(<span class="hljs-keyword">this</span>.render_options, {</pre></div>
<p>font size for note heads and rests</p>
<div class='highlight'><pre> glyph_font_scale: noteStruct.glyph_font_scale || Flow.DEFAULT_NOTATION_FONT_SCALE,</pre></div>
<p>number of stroke px to the left and right of head</p>
<div class='highlight'><pre> stroke_px: noteStruct.stroke_px || StaveNote.DEFAULT_LEDGER_LINE_OFFSET,
});
<span class="hljs-keyword">this</span>.calculateKeyProps();
<span class="hljs-keyword">this</span>.buildStem();</pre></div>
<p>Set the stem direction</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (noteStruct.auto_stem) {
<span class="hljs-keyword">this</span>.autoStem();
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">this</span>.setStemDirection(noteStruct.stem_direction);
}
<span class="hljs-keyword">this</span>.reset();
<span class="hljs-keyword">this</span>.buildFlag();
}
reset() {
<span class="hljs-keyword">super</span>.reset();</pre></div>
<p>Save prior noteHead styles & reapply them after making new noteheads.</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> noteHeadStyles = <span class="hljs-keyword">this</span>.note_heads.map(<span class="hljs-function"><span class="hljs-params">noteHead</span> =></span> noteHead.getStyle());
<span class="hljs-keyword">this</span>.buildNoteHeads();
<span class="hljs-keyword">this</span>.note_heads.forEach(<span class="hljs-function">(<span class="hljs-params">noteHead, index</span>) =></span> noteHead.setStyle(noteHeadStyles[index]));
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.stave) {
<span class="hljs-keyword">this</span>.note_heads.forEach(<span class="hljs-function"><span class="hljs-params">head</span> =></span> head.setStave(<span class="hljs-keyword">this</span>.stave));
}
<span class="hljs-keyword">this</span>.calcExtraPx();
}
setBeam(beam) {
<span class="hljs-keyword">this</span>.beam = beam;
<span class="hljs-keyword">this</span>.calcExtraPx();
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
getCategory() { <span class="hljs-keyword">return</span> StaveNote.CATEGORY; }</pre></div>
<p>Builds a <code>Stem</code> for the note</p>
<div class='highlight'><pre> buildStem() {
<span class="hljs-keyword">const</span> glyph = <span class="hljs-keyword">this</span>.getGlyph();
<span class="hljs-keyword">this</span>.setStem(<span class="hljs-keyword">new</span> Stem({
<span class="hljs-attr">stem_up_y_offset</span>: glyph.stem_up_y_offset,
<span class="hljs-attr">stem_down_y_offset</span>: glyph.stem_down_y_offset,
<span class="hljs-attr">hide</span>: !!<span class="hljs-keyword">this</span>.isRest(),
}));
}</pre></div>
<p>Builds a <code>NoteHead</code> for each key in the note</p>
<div class='highlight'><pre> buildNoteHeads() {
<span class="hljs-keyword">this</span>.note_heads = [];
<span class="hljs-keyword">const</span> stemDirection = <span class="hljs-keyword">this</span>.getStemDirection();
<span class="hljs-keyword">const</span> keys = <span class="hljs-keyword">this</span>.getKeys();
<span class="hljs-keyword">let</span> lastLine = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> lineDiff = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> displaced = <span class="hljs-literal">false</span>;</pre></div>
<p>Draw notes from bottom to top.</p>
<p>For down-stem notes, we draw from top to bottom.</p>
<div class='highlight'><pre> <span class="hljs-keyword">let</span> start;
<span class="hljs-keyword">let</span> end;
<span class="hljs-keyword">let</span> step;
<span class="hljs-keyword">if</span> (stemDirection === Stem.UP) {
start = <span class="hljs-number">0</span>;
end = keys.length;
step = <span class="hljs-number">1</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stemDirection === Stem.DOWN) {
start = keys.length - <span class="hljs-number">1</span>;
end = <span class="hljs-number">-1</span>;
step = <span class="hljs-number">-1</span>;
}
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = start; i !== end; i += step) {
<span class="hljs-keyword">const</span> noteProps = <span class="hljs-keyword">this</span>.keyProps[i];
<span class="hljs-keyword">const</span> line = noteProps.line;</pre></div>
<p>Keep track of last line with a note head, so that consecutive heads
are correctly displaced.</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (lastLine === <span class="hljs-literal">null</span>) {
lastLine = line;
} <span class="hljs-keyword">else</span> {
lineDiff = <span class="hljs-built_in">Math</span>.abs(lastLine - line);
<span class="hljs-keyword">if</span> (lineDiff === <span class="hljs-number">0</span> || lineDiff === <span class="hljs-number">0.5</span>) {
displaced = !displaced;
} <span class="hljs-keyword">else</span> {
displaced = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">this</span>.use_default_head_x = <span class="hljs-literal">true</span>;
}
}
lastLine = line;
<span class="hljs-keyword">const</span> notehead = <span class="hljs-keyword">new</span> NoteHead({
<span class="hljs-attr">duration</span>: <span class="hljs-keyword">this</span>.duration,
<span class="hljs-attr">note_type</span>: <span class="hljs-keyword">this</span>.noteType,
displaced,
<span class="hljs-attr">stem_direction</span>: stemDirection,
<span class="hljs-attr">custom_glyph_code</span>: noteProps.code,
<span class="hljs-attr">glyph_font_scale</span>: <span class="hljs-keyword">this</span>.render_options.glyph_font_scale,
<span class="hljs-attr">x_shift</span>: noteProps.shift_right,
<span class="hljs-attr">line</span>: noteProps.line,
});
<span class="hljs-keyword">this</span>.note_heads[i] = notehead;
}
}</pre></div>
<p>Automatically sets the stem direction based on the keys in the note</p>
<div class='highlight'><pre> autoStem() {</pre></div>
<p>Figure out optimal stem direction based on given notes</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.minLine = <span class="hljs-keyword">this</span>.keyProps[<span class="hljs-number">0</span>].line;
<span class="hljs-keyword">this</span>.maxLine = <span class="hljs-keyword">this</span>.keyProps[<span class="hljs-keyword">this</span>.keyProps.length - <span class="hljs-number">1</span>].line;
<span class="hljs-keyword">const</span> MIDDLE_LINE = <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> decider = (<span class="hljs-keyword">this</span>.minLine + <span class="hljs-keyword">this</span>.maxLine) / <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> stemDirection = decider < MIDDLE_LINE ? Stem.UP : Stem.DOWN;
<span class="hljs-keyword">this</span>.setStemDirection(stemDirection);
}</pre></div>
<p>Calculates and stores the properties for each key in the note</p>
<div class='highlight'><pre> calculateKeyProps() {
<span class="hljs-keyword">let</span> lastLine = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-keyword">this</span>.keys.length; ++i) {
<span class="hljs-keyword">const</span> key = <span class="hljs-keyword">this</span>.keys[i];</pre></div>
<p>All rests use the same position on the line.
if (this.glyph.rest) key = this.glyph.position;</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.glyph.rest) <span class="hljs-keyword">this</span>.glyph.position = key;
<span class="hljs-keyword">const</span> options = { <span class="hljs-attr">octave_shift</span>: <span class="hljs-keyword">this</span>.octave_shift || <span class="hljs-number">0</span> };
<span class="hljs-keyword">const</span> props = Flow.keyProperties(key, <span class="hljs-keyword">this</span>.clef, options);
<span class="hljs-keyword">if</span> (!props) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RuntimeError(<span class="hljs-string">'BadArguments'</span>, <span class="hljs-string">`Invalid key for note properties: <span class="hljs-subst">${key}</span>`</span>);
}</pre></div>
<p>Override line placement for default rests</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (props.key === <span class="hljs-string">'R'</span>) {
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.duration === <span class="hljs-string">'1'</span> || <span class="hljs-keyword">this</span>.duration === <span class="hljs-string">'w'</span>) {
props.line = <span class="hljs-number">4</span>;
} <span class="hljs-keyword">else</span> {
props.line = <span class="hljs-number">3</span>;
}
}</pre></div>
<p>Calculate displacement of this note</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> line = props.line;
<span class="hljs-keyword">if</span> (lastLine === <span class="hljs-literal">null</span>) {
lastLine = line;
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.abs(lastLine - line) === <span class="hljs-number">0.5</span>) {
<span class="hljs-keyword">this</span>.displaced = <span class="hljs-literal">true</span>;
props.displaced = <span class="hljs-literal">true</span>;</pre></div>
<p>Have to mark the previous note as
displaced as well, for modifier placement</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.keyProps.length > <span class="hljs-number">0</span>) {
<span class="hljs-keyword">this</span>.keyProps[i - <span class="hljs-number">1</span>].displaced = <span class="hljs-literal">true</span>;
}
}
}
lastLine = line;
<span class="hljs-keyword">this</span>.keyProps.push(props);
}</pre></div>
<p>Sort the notes from lowest line to highest line</p>
<div class='highlight'><pre> lastLine = -<span class="hljs-literal">Infinity</span>;
<span class="hljs-keyword">this</span>.keyProps.forEach(<span class="hljs-function"><span class="hljs-params">key</span> =></span> {
<span class="hljs-keyword">if</span> (key.line < lastLine) {
Vex.W(
<span class="hljs-string">'Unsorted keys in note will be sorted. '</span> +
<span class="hljs-string">'See https://github.com/0xfe/vexflow/issues/104 for details.'</span>
);
}
lastLine = key.line;
});
<span class="hljs-keyword">this</span>.keyProps.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =></span> a.line - b.line);
}</pre></div>
<p>Get the <code>BoundingBox</code> for the entire note</p>
<div class='highlight'><pre> getBoundingBox() {
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.preFormatted) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'UnformattedNote'</span>, <span class="hljs-string">"Can't call getBoundingBox on an unformatted note."</span>);
}
<span class="hljs-keyword">const</span> { <span class="hljs-attr">width</span>: w, modLeftPx, extraLeftPx } = <span class="hljs-keyword">this</span>.getMetrics();
<span class="hljs-keyword">const</span> x = <span class="hljs-keyword">this</span>.getAbsoluteX() - modLeftPx - extraLeftPx;
<span class="hljs-keyword">let</span> minY = <span class="hljs-number">0</span>;
<span class="hljs-keyword">let</span> maxY = <span class="hljs-number">0</span>;
<span class="hljs-keyword">const</span> halfLineSpacing = <span class="hljs-keyword">this</span>.getStave().getSpacingBetweenLines() / <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> lineSpacing = halfLineSpacing * <span class="hljs-number">2</span>;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.isRest()) {
<span class="hljs-keyword">const</span> y = <span class="hljs-keyword">this</span>.ys[<span class="hljs-number">0</span>];
<span class="hljs-keyword">const</span> frac = Flow.durationToFraction(<span class="hljs-keyword">this</span>.duration);
<span class="hljs-keyword">if</span> (frac.equals(<span class="hljs-number">1</span>) || frac.equals(<span class="hljs-number">2</span>)) {
minY = y - halfLineSpacing;
maxY = y + halfLineSpacing;
} <span class="hljs-keyword">else</span> {
minY = y - (<span class="hljs-keyword">this</span>.glyph.line_above * lineSpacing);
maxY = y + (<span class="hljs-keyword">this</span>.glyph.line_below * lineSpacing);
}
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.glyph.stem) {
<span class="hljs-keyword">const</span> ys = <span class="hljs-keyword">this</span>.getStemExtents();
ys.baseY += halfLineSpacing * <span class="hljs-keyword">this</span>.stem_direction;
minY = <span class="hljs-built_in">Math</span>.min(ys.topY, ys.baseY);
maxY = <span class="hljs-built_in">Math</span>.max(ys.topY, ys.baseY);
} <span class="hljs-keyword">else</span> {
minY = <span class="hljs-literal">null</span>;
maxY = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-keyword">this</span>.ys.length; ++i) {
<span class="hljs-keyword">const</span> yy = <span class="hljs-keyword">this</span>.ys[i];
<span class="hljs-keyword">if</span> (i === <span class="hljs-number">0</span>) {
minY = yy;
maxY = yy;
} <span class="hljs-keyword">else</span> {
minY = <span class="hljs-built_in">Math</span>.min(yy, minY);
maxY = <span class="hljs-built_in">Math</span>.max(yy, maxY);
}
}
minY -= halfLineSpacing;
maxY += halfLineSpacing;
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> BoundingBox(x, minY, w, maxY - minY);
}</pre></div>
<p>Gets the line number of the top or bottom note in the chord.
If <code>isTopNote</code> is <code>true</code> then get the top note</p>
<div class='highlight'><pre> getLineNumber(isTopNote) {
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.keyProps.length) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(
<span class="hljs-string">'NoKeyProps'</span>, <span class="hljs-string">"Can't get bottom note line, because note is not initialized properly."</span>
);
}
<span class="hljs-keyword">let</span> resultLine = <span class="hljs-keyword">this</span>.keyProps[<span class="hljs-number">0</span>].line;</pre></div>
<p>No precondition assumed for sortedness of keyProps array</p>
<div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-keyword">this</span>.keyProps.length; i++) {
<span class="hljs-keyword">const</span> thisLine = <span class="hljs-keyword">this</span>.keyProps[i].line;
<span class="hljs-keyword">if</span> (isTopNote) {
<span class="hljs-keyword">if</span> (thisLine > resultLine) resultLine = thisLine;
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">if</span> (thisLine < resultLine) resultLine = thisLine;
}
}
<span class="hljs-keyword">return</span> resultLine;
}</pre></div>
<p>Determine if current note is a rest</p>
<div class='highlight'><pre> isRest() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.glyph.rest; }</pre></div>
<p>Determine if the current note is a chord</p>
<div class='highlight'><pre> isChord() { <span class="hljs-keyword">return</span> !<span class="hljs-keyword">this</span>.isRest() && <span class="hljs-keyword">this</span>.keys.length > <span class="hljs-number">1</span>; }</pre></div>
<p>Determine if the <code>StaveNote</code> has a stem</p>
<div class='highlight'><pre> hasStem() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.glyph.stem; }
hasFlag() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.hasFlag() && !<span class="hljs-keyword">this</span>.isRest();
}
getStemX() {
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.noteType === <span class="hljs-string">'r'</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.getCenterGlyphX();
} <span class="hljs-keyword">else</span> {</pre></div>
<p>We adjust the origin of the stem because we want the stem left-aligned
with the notehead if stemmed-down, and right-aligned if stemmed-up</p>
<div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.getStemX() + getStemAdjustment(<span class="hljs-keyword">this</span>);
}
}</pre></div>
<p>Get the <code>y</code> coordinate for text placed on the top/bottom of a
note at a desired <code>text_line</code></p>
<div class='highlight'><pre> getYForTopText(textLine) {
<span class="hljs-keyword">const</span> extents = <span class="hljs-keyword">this</span>.getStemExtents();
<span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.min(
<span class="hljs-keyword">this</span>.stave.getYForTopText(textLine),
extents.topY - (<span class="hljs-keyword">this</span>.render_options.annotation_spacing * (textLine + <span class="hljs-number">1</span>))
);
}
getYForBottomText(textLine) {
<span class="hljs-keyword">const</span> extents = <span class="hljs-keyword">this</span>.getStemExtents();
<span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.max(
<span class="hljs-keyword">this</span>.stave.getYForTopText(textLine),
extents.baseY + (<span class="hljs-keyword">this</span>.render_options.annotation_spacing * (textLine))
);
}</pre></div>
<p>Sets the current note to the provided <code>stave</code>. This applies
<code>y</code> values to the <code>NoteHeads</code>.</p>
<div class='highlight'><pre> setStave(stave) {
<span class="hljs-keyword">super</span>.setStave(stave);
<span class="hljs-keyword">const</span> ys = <span class="hljs-keyword">this</span>.note_heads.map(<span class="hljs-function"><span class="hljs-params">notehead</span> =></span> {
notehead.setStave(stave);
<span class="hljs-keyword">return</span> notehead.getY();
});
<span class="hljs-keyword">this</span>.setYs(ys);
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.stem) {
<span class="hljs-keyword">const</span> { y_top, y_bottom } = <span class="hljs-keyword">this</span>.getNoteHeadBounds();
<span class="hljs-keyword">this</span>.stem.setYBounds(y_top, y_bottom);
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>Get the pitches in the note</p>
<div class='highlight'><pre> getKeys() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.keys; }</pre></div>
<p>Get the properties for all the keys in the note</p>
<div class='highlight'><pre> getKeyProps() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.keyProps;
}</pre></div>
<p>Check if note is shifted to the right</p>
<div class='highlight'><pre> isDisplaced() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.displaced;
}</pre></div>
<p>Sets whether shift note to the right. <code>displaced</code> is a <code>boolean</code></p>
<div class='highlight'><pre> setNoteDisplaced(displaced) {
<span class="hljs-keyword">this</span>.displaced = displaced;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>Get the starting <code>x</code> coordinate for a <code>StaveTie</code></p>
<div class='highlight'><pre> getTieRightX() {
<span class="hljs-keyword">let</span> tieStartX = <span class="hljs-keyword">this</span>.getAbsoluteX();
tieStartX += <span class="hljs-keyword">this</span>.getGlyphWidth() + <span class="hljs-keyword">this</span>.x_shift + <span class="hljs-keyword">this</span>.extraRightPx;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.modifierContext) tieStartX += <span class="hljs-keyword">this</span>.modifierContext.getExtraRightPx();
<span class="hljs-keyword">return</span> tieStartX;
}</pre></div>
<p>Get the ending <code>x</code> coordinate for a <code>StaveTie</code></p>
<div class='highlight'><pre> getTieLeftX() {
<span class="hljs-keyword">let</span> tieEndX = <span class="hljs-keyword">this</span>.getAbsoluteX();
tieEndX += <span class="hljs-keyword">this</span>.x_shift - <span class="hljs-keyword">this</span>.extraLeftPx;
<span class="hljs-keyword">return</span> tieEndX;
}</pre></div>
<p>Get the stave line on which to place a rest</p>
<div class='highlight'><pre> getLineForRest() {
<span class="hljs-keyword">let</span> restLine = <span class="hljs-keyword">this</span>.keyProps[<span class="hljs-number">0</span>].line;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.keyProps.length > <span class="hljs-number">1</span>) {
<span class="hljs-keyword">const</span> lastLine = <span class="hljs-keyword">this</span>.keyProps[<span class="hljs-keyword">this</span>.keyProps.length - <span class="hljs-number">1</span>].line;
<span class="hljs-keyword">const</span> top = <span class="hljs-built_in">Math</span>.max(restLine, lastLine);
<span class="hljs-keyword">const</span> bot = <span class="hljs-built_in">Math</span>.min(restLine, lastLine);
restLine = Vex.MidLine(top, bot);
}
<span class="hljs-keyword">return</span> restLine;
}</pre></div>
<p>Get the default <code>x</code> and <code>y</code> coordinates for the provided <code>position</code>
and key <code>index</code></p>
<div class='highlight'><pre> getModifierStartXY(position, index, options) {
options = options || {};
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.preFormatted) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'UnformattedNote'</span>, <span class="hljs-string">"Can't call GetModifierStartXY on an unformatted note"</span>);
}
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.ys.length === <span class="hljs-number">0</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoYValues'</span>, <span class="hljs-string">'No Y-Values calculated for this note.'</span>);
}
<span class="hljs-keyword">const</span> { ABOVE, BELOW, LEFT, RIGHT } = Modifier.Position;
<span class="hljs-keyword">let</span> x = <span class="hljs-number">0</span>;
<span class="hljs-keyword">if</span> (position === LEFT) {</pre></div>
<p>extra_left_px
FIXME: What are these magic numbers?</p>
<div class='highlight'><pre> x = <span class="hljs-number">-1</span> * <span class="hljs-number">2</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (position === RIGHT) {</pre></div>
<p>extra_right_px
FIXME: What is this magical +2?</p>
<div class='highlight'><pre> x = <span class="hljs-keyword">this</span>.getGlyphWidth() + <span class="hljs-keyword">this</span>.x_shift + <span class="hljs-number">2</span>;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.stem_direction === Stem.UP && <span class="hljs-keyword">this</span>.hasFlag() &&
(options.forceFlagRight || isInnerNoteIndex(<span class="hljs-keyword">this</span>, index))) {
x += <span class="hljs-keyword">this</span>.flag.getMetrics().width;
}
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (position === BELOW || position === ABOVE) {
x = <span class="hljs-keyword">this</span>.getGlyphWidth() / <span class="hljs-number">2</span>;
}
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">x</span>: <span class="hljs-keyword">this</span>.getAbsoluteX() + x,
<span class="hljs-attr">y</span>: <span class="hljs-keyword">this</span>.ys[index],
};
}</pre></div>
<p>Sets the style of the complete StaveNote, including all keys
and the stem.</p>
<div class='highlight'><pre> setStyle(style) {
<span class="hljs-keyword">super</span>.setStyle(style);
<span class="hljs-keyword">this</span>.note_heads.forEach(<span class="hljs-function"><span class="hljs-params">notehead</span> =></span> notehead.setStyle(style));
<span class="hljs-keyword">this</span>.stem.setStyle(style);
}
setStemStyle(style) {
<span class="hljs-keyword">const</span> stem = <span class="hljs-keyword">this</span>.getStem();
stem.setStyle(style);
}
getStemStyle() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.stem.getStyle(); }
setLedgerLineStyle(style) { <span class="hljs-keyword">this</span>.ledgerLineStyle = style; }
getLedgerLineStyle() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.ledgerLineStyle; }
setFlagStyle(style) { <span class="hljs-keyword">this</span>.flagStyle = style; }
getFlagStyle() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.flagStyle; }</pre></div>
<p>Sets the notehead at <code>index</code> to the provided coloring <code>style</code>.</p>
<p><code>style</code> is an <code>object</code> with the following properties: <code>shadowColor</code>,
<code>shadowBlur</code>, <code>fillStyle</code>, <code>strokeStyle</code></p>
<div class='highlight'><pre> setKeyStyle(index, style) {
<span class="hljs-keyword">this</span>.note_heads[index].setStyle(style);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
setKeyLine(index, line) {
<span class="hljs-keyword">this</span>.keyProps[index].line = line;
<span class="hljs-keyword">this</span>.reset();
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}
getKeyLine(index) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.keyProps[index].line;
}</pre></div>
<p>Add self to modifier context. <code>mContext</code> is the <code>ModifierContext</code>
to be added to.</p>
<div class='highlight'><pre> addToModifierContext(mContext) {
<span class="hljs-keyword">this</span>.setModifierContext(mContext);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-keyword">this</span>.modifiers.length; ++i) {
<span class="hljs-keyword">this</span>.modifierContext.addModifier(<span class="hljs-keyword">this</span>.modifiers[i]);
}
<span class="hljs-keyword">this</span>.modifierContext.addModifier(<span class="hljs-keyword">this</span>);
<span class="hljs-keyword">this</span>.setPreFormatted(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>Generic function to add modifiers to a note</p>
<p>Parameters:</p>
<ul>
<li><code>index</code>: The index of the key that we’re modifying</li>
<li><code>modifier</code>: The modifier to add</li>
</ul>
<div class='highlight'><pre> addModifier(index, modifier) {
modifier.setNote(<span class="hljs-keyword">this</span>);
modifier.setIndex(index);
<span class="hljs-keyword">this</span>.modifiers.push(modifier);
<span class="hljs-keyword">this</span>.setPreFormatted(<span class="hljs-literal">false</span>);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>Helper function to add an accidental to a key</p>
<div class='highlight'><pre> addAccidental(index, accidental) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.addModifier(index, accidental);
}</pre></div>
<p>Helper function to add an articulation to a key</p>
<div class='highlight'><pre> addArticulation(index, articulation) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.addModifier(index, articulation);
}</pre></div>
<p>Helper function to add an annotation to a key</p>
<div class='highlight'><pre> addAnnotation(index, annotation) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.addModifier(index, annotation);
}</pre></div>
<p>Helper function to add a dot on a specific key</p>
<div class='highlight'><pre> addDot(index) {
<span class="hljs-keyword">const</span> dot = <span class="hljs-keyword">new</span> Dot();
dot.setDotShiftY(<span class="hljs-keyword">this</span>.glyph.dot_shiftY);
<span class="hljs-keyword">this</span>.dots++;
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.addModifier(index, dot);
}</pre></div>
<p>Convenience method to add dot to all keys in note</p>
<div class='highlight'><pre> addDotToAll() {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-keyword">this</span>.keys.length; ++i) {
<span class="hljs-keyword">this</span>.addDot(i);
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
}</pre></div>
<p>Get all accidentals in the <code>ModifierContext</code></p>
<div class='highlight'><pre> getAccidentals() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.modifierContext.getModifiers(<span class="hljs-string">'accidentals'</span>);
}</pre></div>
<p>Get all dots in the <code>ModifierContext</code></p>
<div class='highlight'><pre> getDots() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.modifierContext.getModifiers(<span class="hljs-string">'dots'</span>);
}</pre></div>
<p>Get the width of the note if it is displaced. Used for <code>Voice</code>
formatting</p>
<div class='highlight'><pre> getVoiceShiftWidth() {</pre></div>
<p>TODO: may need to accomodate for dot here.</p>
<div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.getGlyphWidth() * (<span class="hljs-keyword">this</span>.displaced ? <span class="hljs-number">2</span> : <span class="hljs-number">1</span>);
}</pre></div>
<p>Calculates and sets the extra pixels to the left or right
if the note is displaced.</p>
<div class='highlight'><pre> calcExtraPx() {
<span class="hljs-keyword">this</span>.setExtraLeftPx(
<span class="hljs-keyword">this</span>.displaced && <span class="hljs-keyword">this</span>.stem_direction === Stem.DOWN
? <span class="hljs-keyword">this</span>.getGlyphWidth()
: <span class="hljs-number">0</span>
);</pre></div>
<p>For upstems with flags, the extra space is unnecessary, since it’s taken
up by the flag.</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.setExtraRightPx(
!<span class="hljs-keyword">this</span>.hasFlag() && <span class="hljs-keyword">this</span>.displaced && <span class="hljs-keyword">this</span>.stem_direction === Stem.UP
? <span class="hljs-keyword">this</span>.getGlyphWidth()
: <span class="hljs-number">0</span>
);
}</pre></div>
<p>Pre-render formatting</p>
<div class='highlight'><pre> preFormat() {
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.preFormatted) <span class="hljs-keyword">return</span>;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.modifierContext) <span class="hljs-keyword">this</span>.modifierContext.preFormat();
<span class="hljs-keyword">let</span> width = <span class="hljs-keyword">this</span>.getGlyphWidth() + <span class="hljs-keyword">this</span>.extraLeftPx + <span class="hljs-keyword">this</span>.extraRightPx;</pre></div>
<p>For upward flagged notes, the width of the flag needs to be added</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.glyph.flag && <span class="hljs-keyword">this</span>.beam === <span class="hljs-literal">null</span> && <span class="hljs-keyword">this</span>.stem_direction === Stem.UP) {
width += <span class="hljs-keyword">this</span>.getGlyphWidth();
}
<span class="hljs-keyword">this</span>.setWidth(width);
<span class="hljs-keyword">this</span>.setPreFormatted(<span class="hljs-literal">true</span>);
}
<span class="hljs-comment">/**
* @typedef {Object} noteHeadBounds
* @property {number} y_top the highest notehead bound
* @property {number} y_bottom the lowest notehead bound
* @property {number|Null} displaced_x the starting x for displaced noteheads
* @property {number|Null} non_displaced_x the starting x for non-displaced noteheads
* @property {number} highest_line the highest notehead line in traditional music line
* numbering (bottom line = 1, top line = 5)
* @property {number} lowest_line the lowest notehead line
* @property {number|false} highest_displaced_line the highest staff line number
* for a displaced notehead
* @property {number|false} lowest_displaced_line
* @property {number} highest_non_displaced_line
* @property {number} lowest_non_displaced_line
*/</span>
<span class="hljs-comment">/**
* Get the staff line and y value for the highest & lowest noteheads
* @returns {noteHeadBounds}
*/</span>
getNoteHeadBounds() {</pre></div>
<p>Top and bottom Y values for stem.</p>
<div class='highlight'><pre> <span class="hljs-keyword">let</span> yTop = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> yBottom = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> nonDisplacedX = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> displacedX = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">let</span> highestLine = <span class="hljs-keyword">this</span>.stave.getNumLines();
<span class="hljs-keyword">let</span> lowestLine = <span class="hljs-number">1</span>;
<span class="hljs-keyword">let</span> highestDisplacedLine = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> lowestDisplacedLine = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> highestNonDisplacedLine = highestLine;
<span class="hljs-keyword">let</span> lowestNonDisplacedLine = lowestLine;
<span class="hljs-keyword">this</span>.note_heads.forEach(<span class="hljs-function"><span class="hljs-params">notehead</span> =></span> {
<span class="hljs-keyword">const</span> line = notehead.getLine();
<span class="hljs-keyword">const</span> y = notehead.getY();
<span class="hljs-keyword">if</span> (yTop === <span class="hljs-literal">null</span> || y < yTop) {
yTop = y;
}
<span class="hljs-keyword">if</span> (yBottom === <span class="hljs-literal">null</span> || y > yBottom) {
yBottom = y;
}
<span class="hljs-keyword">if</span> (displacedX === <span class="hljs-literal">null</span> && notehead.isDisplaced()) {
displacedX = notehead.getAbsoluteX();
}
<span class="hljs-keyword">if</span> (nonDisplacedX === <span class="hljs-literal">null</span> && !notehead.isDisplaced()) {
nonDisplacedX = notehead.getAbsoluteX();
}
highestLine = line > highestLine ? line : highestLine;
lowestLine = line < lowestLine ? line : lowestLine;
<span class="hljs-keyword">if</span> (notehead.isDisplaced()) {
highestDisplacedLine = (highestDisplacedLine === <span class="hljs-literal">false</span>) ?
line : <span class="hljs-built_in">Math</span>.max(line, highestDisplacedLine);
lowestDisplacedLine = (lowestDisplacedLine === <span class="hljs-literal">false</span>) ?
line : <span class="hljs-built_in">Math</span>.min(line, lowestDisplacedLine);
} <span class="hljs-keyword">else</span> {
highestNonDisplacedLine = <span class="hljs-built_in">Math</span>.max(line, highestNonDisplacedLine);
lowestNonDisplacedLine = <span class="hljs-built_in">Math</span>.min(line, lowestNonDisplacedLine);
}
}, <span class="hljs-keyword">this</span>);
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">y_top</span>: yTop,
<span class="hljs-attr">y_bottom</span>: yBottom,
<span class="hljs-attr">displaced_x</span>: displacedX,
<span class="hljs-attr">non_displaced_x</span>: nonDisplacedX,
<span class="hljs-attr">highest_line</span>: highestLine,
<span class="hljs-attr">lowest_line</span>: lowestLine,
<span class="hljs-attr">highest_displaced_line</span>: highestDisplacedLine,
<span class="hljs-attr">lowest_displaced_line</span>: lowestDisplacedLine,
<span class="hljs-attr">highest_non_displaced_line</span>: highestNonDisplacedLine,
<span class="hljs-attr">lowest_non_displaced_line</span>: lowestNonDisplacedLine,
};
}</pre></div>
<p>Get the starting <code>x</code> coordinate for the noteheads</p>
<div class='highlight'><pre> getNoteHeadBeginX() {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.getAbsoluteX() + <span class="hljs-keyword">this</span>.x_shift;
}</pre></div>
<p>Get the ending <code>x</code> coordinate for the noteheads</p>
<div class='highlight'><pre> getNoteHeadEndX() {
<span class="hljs-keyword">const</span> xBegin = <span class="hljs-keyword">this</span>.getNoteHeadBeginX();
<span class="hljs-keyword">return</span> xBegin + <span class="hljs-keyword">this</span>.getGlyphWidth();
}</pre></div>
<p>Draw the ledger lines between the stave and the highest/lowest keys</p>
<div class='highlight'><pre> drawLedgerLines() {
<span class="hljs-keyword">const</span> {
stave, glyph,
<span class="hljs-attr">render_options</span>: { stroke_px },
<span class="hljs-attr">context</span>: ctx,
} = <span class="hljs-keyword">this</span>;
<span class="hljs-keyword">const</span> width = glyph.getWidth() + (stroke_px * <span class="hljs-number">2</span>);
<span class="hljs-keyword">const</span> doubleWidth = <span class="hljs-number">2</span> * (glyph.getWidth() + stroke_px) - (Stem.WIDTH / <span class="hljs-number">2</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.isRest()) <span class="hljs-keyword">return</span>;
<span class="hljs-keyword">if</span> (!ctx) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoCanvasContext'</span>, <span class="hljs-string">"Can't draw without a canvas context."</span>);
}
<span class="hljs-keyword">const</span> {
highest_line,
lowest_line,
highest_displaced_line,
highest_non_displaced_line,
lowest_displaced_line,
lowest_non_displaced_line,
displaced_x,
non_displaced_x,
} = <span class="hljs-keyword">this</span>.getNoteHeadBounds();
<span class="hljs-keyword">const</span> min_x = <span class="hljs-built_in">Math</span>.min(displaced_x, non_displaced_x);
<span class="hljs-keyword">const</span> drawLedgerLine = <span class="hljs-function">(<span class="hljs-params">y, normal, displaced</span>) =></span> {
<span class="hljs-keyword">let</span> x;
<span class="hljs-keyword">if</span> (displaced && normal) x = min_x - stroke_px;
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (normal) x = non_displaced_x - stroke_px;
<span class="hljs-keyword">else</span> x = displaced_x - stroke_px;
<span class="hljs-keyword">const</span> ledgerWidth = (normal && displaced) ? doubleWidth : width;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + ledgerWidth, y);
ctx.stroke();
};
<span class="hljs-keyword">const</span> style = <span class="hljs-built_in">Object</span>.assign({}, stave.getStyle() || {}, <span class="hljs-keyword">this</span>.getLedgerLineStyle() || {});
<span class="hljs-keyword">this</span>.applyStyle(ctx, style);</pre></div>
<p>Draw ledger lines below the staff:</p>
<div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> line = <span class="hljs-number">6</span>; line <= highest_line; ++line) {
<span class="hljs-keyword">const</span> normal = (non_displaced_x !== <span class="hljs-literal">null</span>) && (line <= highest_non_displaced_line);
<span class="hljs-keyword">const</span> displaced = (displaced_x !== <span class="hljs-literal">null</span>) && (line <= highest_displaced_line);
drawLedgerLine(stave.getYForNote(line), normal, displaced);
}</pre></div>
<p>Draw ledger lines above the staff:</p>
<div class='highlight'><pre> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> line = <span class="hljs-number">0</span>; line >= lowest_line; --line) {
<span class="hljs-keyword">const</span> normal = (non_displaced_x !== <span class="hljs-literal">null</span>) && (line >= lowest_non_displaced_line);
<span class="hljs-keyword">const</span> displaced = (displaced_x !== <span class="hljs-literal">null</span>) && (line >= lowest_displaced_line);
drawLedgerLine(stave.getYForNote(line), normal, displaced);
}
<span class="hljs-keyword">this</span>.restoreStyle(ctx, style);
}</pre></div>
<p>Draw all key modifiers</p>
<div class='highlight'><pre> drawModifiers() {
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.context) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoCanvasContext'</span>, <span class="hljs-string">"Can't draw without a canvas context."</span>);
}
<span class="hljs-keyword">const</span> ctx = <span class="hljs-keyword">this</span>.context;
ctx.openGroup(<span class="hljs-string">'modifiers'</span>);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-keyword">this</span>.modifiers.length; i++) {
<span class="hljs-keyword">const</span> modifier = <span class="hljs-keyword">this</span>.modifiers[i];
<span class="hljs-keyword">const</span> notehead = <span class="hljs-keyword">this</span>.note_heads[modifier.getIndex()];
<span class="hljs-keyword">const</span> noteheadStyle = notehead.getStyle();
notehead.applyStyle(ctx, noteheadStyle);
modifier.setContext(ctx);
modifier.drawWithStyle();
notehead.restoreStyle(ctx, noteheadStyle);
}
ctx.closeGroup();
}</pre></div>
<p>Draw the flag for the note</p>
<div class='highlight'><pre> drawFlag() {
<span class="hljs-keyword">const</span> { stem, beam, <span class="hljs-attr">context</span>: ctx } = <span class="hljs-keyword">this</span>;
<span class="hljs-keyword">if</span> (!ctx) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoCanvasContext'</span>, <span class="hljs-string">"Can't draw without a canvas context."</span>);
}
<span class="hljs-keyword">const</span> shouldRenderFlag = beam === <span class="hljs-literal">null</span>;
<span class="hljs-keyword">const</span> glyph = <span class="hljs-keyword">this</span>.getGlyph();
<span class="hljs-keyword">if</span> (glyph.flag && shouldRenderFlag) {
<span class="hljs-keyword">const</span> { y_top, y_bottom } = <span class="hljs-keyword">this</span>.getNoteHeadBounds();
<span class="hljs-keyword">const</span> noteStemHeight = stem.getHeight();
<span class="hljs-keyword">const</span> flagX = <span class="hljs-keyword">this</span>.getStemX();</pre></div>
<p>FIXME: What’s with the magic +/- 2</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> flagY = <span class="hljs-keyword">this</span>.getStemDirection() === Stem.DOWN</pre></div>
<p>Down stems have flags on the left</p>
<div class='highlight'><pre> ? y_top - noteStemHeight + <span class="hljs-number">2</span></pre></div>
<p>Up stems have flags on the eft.</p>
<div class='highlight'><pre> : y_bottom - noteStemHeight - <span class="hljs-number">2</span>;</pre></div>
<p>Draw the Flag</p>
<div class='highlight'><pre> ctx.openGroup(<span class="hljs-string">'flag'</span>, <span class="hljs-literal">null</span>, { <span class="hljs-attr">pointerBBox</span>: <span class="hljs-literal">true</span> });
<span class="hljs-keyword">this</span>.applyStyle(ctx, <span class="hljs-keyword">this</span>.getFlagStyle() || <span class="hljs-literal">false</span>);
<span class="hljs-keyword">this</span>.flag.render(ctx, flagX, flagY);
<span class="hljs-keyword">this</span>.restoreStyle(ctx, <span class="hljs-keyword">this</span>.getFlagStyle() || <span class="hljs-literal">false</span>);
ctx.closeGroup();
}
}</pre></div>
<p>Draw the NoteHeads</p>
<div class='highlight'><pre> drawNoteHeads() {
<span class="hljs-keyword">this</span>.note_heads.forEach(<span class="hljs-function"><span class="hljs-params">notehead</span> =></span> {
<span class="hljs-keyword">this</span>.context.openGroup(<span class="hljs-string">'notehead'</span>, <span class="hljs-literal">null</span>, { <span class="hljs-attr">pointerBBox</span>: <span class="hljs-literal">true</span> });
notehead.setContext(<span class="hljs-keyword">this</span>.context).draw();
<span class="hljs-keyword">this</span>.context.closeGroup();
});
}</pre></div>
<p>Render the stem onto the canvas</p>
<div class='highlight'><pre> drawStem(stemStruct) {</pre></div>
<p>GCR TODO: I can’t find any context in which this is called with the stemStruct
argument in the codebase or tests. Nor can I find a case where super.drawStem
is called at all. Perhaps these should be removed?</p>
<div class='highlight'><pre> <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.context) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoCanvasContext'</span>, <span class="hljs-string">"Can't draw without a canvas context."</span>);
}
<span class="hljs-keyword">if</span> (stemStruct) {
<span class="hljs-keyword">this</span>.setStem(<span class="hljs-keyword">new</span> Stem(stemStruct));
}
<span class="hljs-keyword">this</span>.context.openGroup(<span class="hljs-string">'stem'</span>, <span class="hljs-literal">null</span>, { <span class="hljs-attr">pointerBBox</span>: <span class="hljs-literal">true</span> });
<span class="hljs-keyword">this</span>.stem.setContext(<span class="hljs-keyword">this</span>.context).draw();
<span class="hljs-keyword">this</span>.context.closeGroup();
}</pre></div>
<p>Draws all the <code>StaveNote</code> parts. This is the main drawing method.</p>
<div class='highlight'><pre> draw() {
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.context) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoCanvasContext'</span>, <span class="hljs-string">"Can't draw without a canvas context."</span>);
}
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.stave) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoStave'</span>, <span class="hljs-string">"Can't draw without a stave."</span>);
}
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.ys.length === <span class="hljs-number">0</span>) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoYValues'</span>, <span class="hljs-string">"Can't draw note without Y values."</span>);
}
<span class="hljs-keyword">const</span> xBegin = <span class="hljs-keyword">this</span>.getNoteHeadBeginX();
<span class="hljs-keyword">const</span> shouldRenderStem = <span class="hljs-keyword">this</span>.hasStem() && !<span class="hljs-keyword">this</span>.beam;</pre></div>
<p>Format note head x positions</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.note_heads.forEach(<span class="hljs-function"><span class="hljs-params">notehead</span> =></span> notehead.setX(xBegin));</pre></div>
<p>Format stem x positions</p>
<div class='highlight'><pre> <span class="hljs-keyword">const</span> stemX = <span class="hljs-keyword">this</span>.getStemX();
<span class="hljs-keyword">this</span>.stem.setNoteHeadXBounds(stemX, stemX);
L(<span class="hljs-string">'Rendering '</span>, <span class="hljs-keyword">this</span>.isChord() ? <span class="hljs-string">'chord :'</span> : <span class="hljs-string">'note :'</span>, <span class="hljs-keyword">this</span>.keys);</pre></div>
<p>Draw each part of the note</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.drawLedgerLines();</pre></div>
<p>Apply the overall style – may be contradicted by local settings:</p>
<div class='highlight'><pre> <span class="hljs-keyword">this</span>.applyStyle();
<span class="hljs-keyword">this</span>.setAttribute(<span class="hljs-string">'el'</span>, <span class="hljs-keyword">this</span>.context.openGroup(<span class="hljs-string">'stavenote'</span>, <span class="hljs-keyword">this</span>.getAttribute(<span class="hljs-string">'id'</span>)));
<span class="hljs-keyword">this</span>.context.openGroup(<span class="hljs-string">'note'</span>, <span class="hljs-literal">null</span>, { <span class="hljs-attr">pointerBBox</span>: <span class="hljs-literal">true</span> });
<span class="hljs-keyword">if</span> (shouldRenderStem) <span class="hljs-keyword">this</span>.drawStem();
<span class="hljs-keyword">this</span>.drawNoteHeads();
<span class="hljs-keyword">this</span>.drawFlag();
<span class="hljs-keyword">this</span>.context.closeGroup();
<span class="hljs-keyword">this</span>.drawModifiers();
<span class="hljs-keyword">this</span>.context.closeGroup();
<span class="hljs-keyword">this</span>.restoreStyle();
<span class="hljs-keyword">this</span>.setRendered();
}
}</pre></div>
<div class="fleur">h</div>
</div>
</div>
</body>
</html>