UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature

1,319 lines 50.5 kB
<!DOCTYPE html>

<html>
<head>
  <title>tabnote.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>tabnote.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>The file implements notes for Tablature notation. This consists of one or
more fret positions, and can either be drawn with or without stems.</p>
<p>See <code>tests/tabnote_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> { Modifier } <span class="hljs-keyword">from</span> <span class="hljs-string">'./modifier'</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> { StemmableNote } <span class="hljs-keyword">from</span> <span class="hljs-string">'./stemmablenote'</span>;
<span class="hljs-keyword">import</span> { Dot } <span class="hljs-keyword">from</span> <span class="hljs-string">'./dot'</span>;
<span class="hljs-keyword">import</span> { Glyph } <span class="hljs-keyword">from</span> <span class="hljs-string">'./glyph'</span>;</pre></div>
        
      
        
        <p>Gets the unused strings grouped together if consecutive.</p>
<p>Parameters:</p>
<ul>
<li>num_lines - The number of lines</li>
<li>strings_used - An array of numbers representing which strings have fret positions</li>
</ul>

        
          <div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUnusedStringGroups</span>(<span class="hljs-params">num_lines, strings_used</span>) </span>{
  <span class="hljs-keyword">const</span> stem_through = [];
  <span class="hljs-keyword">let</span> group = [];
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> string = <span class="hljs-number">1</span>; string &lt;= num_lines; string++) {
    <span class="hljs-keyword">const</span> is_used = strings_used.indexOf(string) &gt; <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (!is_used) {
      group.push(string);
    } <span class="hljs-keyword">else</span> {
      stem_through.push(group);
      group = [];
    }
  }
  <span class="hljs-keyword">if</span> (group.length &gt; <span class="hljs-number">0</span>) stem_through.push(group);

  <span class="hljs-keyword">return</span> stem_through;
}</pre></div>
        
      
        
        <p>Gets groups of points that outline the partial stem lines
between fret positions</p>
<p>Parameters:</p>
<ul>
<li>stem_Y - The <code>y</code> coordinate the stem is located on</li>
<li>unused_strings - An array of groups of unused strings</li>
<li>stave - The stave to use for reference</li>
<li>stem_direction - The direction of the stem</li>
</ul>

        
          <div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPartialStemLines</span>(<span class="hljs-params">stem_y, unused_strings, stave, stem_direction</span>) </span>{
  <span class="hljs-keyword">const</span> up_stem = stem_direction !== <span class="hljs-number">1</span>;
  <span class="hljs-keyword">const</span> down_stem = stem_direction !== <span class="hljs-number">-1</span>;

  <span class="hljs-keyword">const</span> line_spacing = stave.getSpacingBetweenLines();
  <span class="hljs-keyword">const</span> total_lines = stave.getNumLines();

  <span class="hljs-keyword">const</span> stem_lines = [];

  unused_strings.forEach(<span class="hljs-function"><span class="hljs-params">strings</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> containsLastString = strings.indexOf(total_lines) &gt; <span class="hljs-number">-1</span>;
    <span class="hljs-keyword">const</span> containsFirstString =  strings.indexOf(<span class="hljs-number">1</span>) &gt; <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> ((up_stem &amp;&amp; containsFirstString) ||
       (down_stem &amp;&amp; containsLastString)) {
      <span class="hljs-keyword">return</span>;
    }</pre></div>
        
      
        
        <p>If there’s only one string in the group, push a duplicate value.
We do this because we need 2 strings to convert into upper/lower y
values.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (strings.length === <span class="hljs-number">1</span>) {
      strings.push(strings[<span class="hljs-number">0</span>]);
    }

    <span class="hljs-keyword">const</span> line_ys = [];</pre></div>
        
      
        
        <p>Iterate through each group string and store it’s y position</p>

        
          <div class='highlight'><pre>    strings.forEach(<span class="hljs-function">(<span class="hljs-params">string, index, strings</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> isTopBound = string === <span class="hljs-number">1</span>;
      <span class="hljs-keyword">const</span> isBottomBound = string === total_lines;</pre></div>
        
      
        
        <p>Get the y value for the appropriate staff line,
we adjust for a 0 index array, since string numbers are index 1</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">let</span> y = stave.getYForLine(string - <span class="hljs-number">1</span>);</pre></div>
        
      
        
        <p>Unless the string is the first or last, add padding to each side
of the line</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (index === <span class="hljs-number">0</span> &amp;&amp; !isTopBound) {
        y -= line_spacing / <span class="hljs-number">2</span> - <span class="hljs-number">1</span>;
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index === strings.length - <span class="hljs-number">1</span> &amp;&amp; !isBottomBound) {
        y += line_spacing / <span class="hljs-number">2</span> - <span class="hljs-number">1</span>;
      }</pre></div>
        
      
        
        <p>Store the y value</p>

        
          <div class='highlight'><pre>      line_ys.push(y);</pre></div>
        
      
        
        <p>Store a subsequent y value connecting this group to the main
stem above/below the stave if it’s the top/bottom string</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (stem_direction === <span class="hljs-number">1</span> &amp;&amp; isTopBound) {
        line_ys.push(stem_y - <span class="hljs-number">2</span>);
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stem_direction === <span class="hljs-number">-1</span> &amp;&amp; isBottomBound) {
        line_ys.push(stem_y + <span class="hljs-number">2</span>);
      }
    });</pre></div>
        
      
        
        <p>Add the sorted y values to the</p>

        
          <div class='highlight'><pre>    stem_lines.push(line_ys.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b));
  });

  <span class="hljs-keyword">return</span> stem_lines;
}

<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TabNote</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">'tabnotes'</span>; }</pre></div>
        
      
        
        <p>Initialize the TabNote with a <code>tab_struct</code> full of properties
and whether to <code>draw_stem</code> when rendering the note</p>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">constructor</span>(tab_struct, draw_stem) {
    <span class="hljs-keyword">super</span>(tab_struct);
    <span class="hljs-keyword">this</span>.setAttribute(<span class="hljs-string">'type'</span>, <span class="hljs-string">'TabNote'</span>);

    <span class="hljs-keyword">this</span>.ghost = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Renders parenthesis around notes</span></pre></div>
        
      
        
        <p>Note properties</p>
<p>The fret positions in the note. An array of <code>{ str: X, fret: X }</code></p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.positions = tab_struct.positions;</pre></div>
        
      
        
        <p>Render Options</p>

        
          <div class='highlight'><pre>    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: Flow.DEFAULT_TABLATURE_FONT_SCALE,</pre></div>
        
      
        
        <p>Flag to draw a stem</p>

        
          <div class='highlight'><pre>      draw_stem,</pre></div>
        
      
        
        <p>Flag to draw dot modifiers</p>

        
          <div class='highlight'><pre>      draw_dots: draw_stem,</pre></div>
        
      
        
        <p>Flag to extend the main stem through the stave and fret positions</p>

        
          <div class='highlight'><pre>      draw_stem_through_stave: <span class="hljs-literal">false</span>,</pre></div>
        
      
        
        <p>vertical shift from stave line</p>

        
          <div class='highlight'><pre>      y_shift: <span class="hljs-number">0</span>,</pre></div>
        
      
        
        <p>normal glyph scale</p>

        
          <div class='highlight'><pre>      scale: <span class="hljs-number">1.0</span>,</pre></div>
        
      
        
        <p>default tablature font</p>

        
          <div class='highlight'><pre>      font: <span class="hljs-string">'10pt Arial'</span>,
    });

    <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(tab_struct)}</span>`</span>
      );
    }

    <span class="hljs-keyword">this</span>.buildStem();

    <span class="hljs-keyword">if</span> (tab_struct.stem_direction) {
      <span class="hljs-keyword">this</span>.setStemDirection(tab_struct.stem_direction);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">this</span>.setStemDirection(Stem.UP);
    }</pre></div>
        
      
        
        <p>Renders parenthesis around notes</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.ghost = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">this</span>.updateWidth();
  }

  reset() {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.stave) <span class="hljs-keyword">this</span>.setStave(<span class="hljs-keyword">this</span>.stave);
  }</pre></div>
        
      
        
        <p>The ModifierContext category</p>

        
          <div class='highlight'><pre>  getCategory() { <span class="hljs-keyword">return</span> TabNote.CATEGORY; }</pre></div>
        
      
        
        <p>Set as ghost <code>TabNote</code>, surrounds the fret positions with parenthesis.
Often used for indicating frets that are being bent to</p>

        
          <div class='highlight'><pre>  setGhost(ghost) {
    <span class="hljs-keyword">this</span>.ghost = ghost;
    <span class="hljs-keyword">this</span>.updateWidth();
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
  }</pre></div>
        
      
        
        <p>Determine if the note has a stem</p>

        
          <div class='highlight'><pre>  hasStem() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.render_options.draw_stem; }</pre></div>
        
      
        
        <p>Get the default stem extension for the note</p>

        
          <div class='highlight'><pre>  getStemExtension() {
    <span class="hljs-keyword">const</span> glyph = <span class="hljs-keyword">this</span>.getGlyph();

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.stem_extension_override != <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.stem_extension_override;
    }

    <span class="hljs-keyword">if</span> (glyph) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.getStemDirection() === <span class="hljs-number">1</span>
        ? glyph.tabnote_stem_up_extension
        : glyph.tabnote_stem_down_extension;
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
  }</pre></div>
        
      
        
        <p>Add a dot to the note</p>

        
          <div class='highlight'><pre>  addDot() {
    <span class="hljs-keyword">const</span> dot = <span class="hljs-keyword">new</span> Dot();
    <span class="hljs-keyword">this</span>.dots += <span class="hljs-number">1</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.addModifier(dot, <span class="hljs-number">0</span>);
  }</pre></div>
        
      
        
        <p>Calculate and store the width of the note</p>

        
          <div class='highlight'><pre>  updateWidth() {
    <span class="hljs-keyword">this</span>.glyphs = [];
    <span class="hljs-keyword">this</span>.width = <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 &lt; <span class="hljs-keyword">this</span>.positions.length; ++i) {
      <span class="hljs-keyword">let</span> fret = <span class="hljs-keyword">this</span>.positions[i].fret;
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.ghost) fret = <span class="hljs-string">'('</span> + fret + <span class="hljs-string">')'</span>;
      <span class="hljs-keyword">const</span> glyph = Flow.tabToGlyph(fret, <span class="hljs-keyword">this</span>.render_options.scale);
      <span class="hljs-keyword">this</span>.glyphs.push(glyph);
      <span class="hljs-keyword">this</span>.width = <span class="hljs-built_in">Math</span>.max(glyph.getWidth(), <span class="hljs-keyword">this</span>.width);
    }</pre></div>
        
      
        
        <p>For some reason we associate a notehead glyph with a TabNote, and this
glyph is used for certain width calculations. Of course, this is totally
incorrect since a notehead is a poor approximation for the dimensions of
a fret number which can have multiple digits. As a result, we must
overwrite getWidth() to return the correct width</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.glyph.getWidth = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> <span class="hljs-keyword">this</span>.width;
  }</pre></div>
        
      
        
        <p>Set the <code>stave</code> to the note</p>

        
          <div class='highlight'><pre>  setStave(stave) {
    <span class="hljs-keyword">super</span>.setStave(stave);
    <span class="hljs-keyword">this</span>.context = stave.context;</pre></div>
        
      
        
        <p>Calculate the fret number width based on font used</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">let</span> i;
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.context) {
      <span class="hljs-keyword">const</span> ctx = <span class="hljs-keyword">this</span>.context;
      <span class="hljs-keyword">this</span>.width = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-keyword">this</span>.glyphs.length; ++i) {
        <span class="hljs-keyword">const</span> glyph = <span class="hljs-keyword">this</span>.glyphs[i];
        <span class="hljs-keyword">const</span> text = <span class="hljs-string">''</span> + glyph.text;
        <span class="hljs-keyword">if</span> (text.toUpperCase() !== <span class="hljs-string">'X'</span>) {
          ctx.save();
          ctx.setRawFont(<span class="hljs-keyword">this</span>.render_options.font);
          glyph.width = ctx.measureText(text).width;
          ctx.restore();
          glyph.getWidth = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> glyph.width;
        }
        <span class="hljs-keyword">this</span>.width = <span class="hljs-built_in">Math</span>.max(glyph.getWidth(), <span class="hljs-keyword">this</span>.width);
      }
      <span class="hljs-keyword">this</span>.glyph.getWidth = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> <span class="hljs-keyword">this</span>.width;
    }</pre></div>
        
      
        
        <p>we subtract 1 from <code>line</code> because getYForLine expects a 0-based index,
while the position.str is a 1-based index</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> ys = <span class="hljs-keyword">this</span>.positions.map(<span class="hljs-function">(<span class="hljs-params">{ str: line }</span>) =&gt;</span> stave.getYForLine(line - <span class="hljs-number">1</span>));

    <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">this</span>.stem.setYBounds(<span class="hljs-keyword">this</span>.getStemY(), <span class="hljs-keyword">this</span>.getStemY());
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
  }</pre></div>
        
      
        
        <p>Get the fret positions for the note</p>

        
          <div class='highlight'><pre>  getPositions() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.positions; }</pre></div>
        
      
        
        <p>Add self to the provided modifier context <code>mc</code></p>

        
          <div class='highlight'><pre>  addToModifierContext(mc) {
    <span class="hljs-keyword">this</span>.setModifierContext(mc);
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <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>.preFormatted = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
  }</pre></div>
        
      
        
        <p>Get the <code>x</code> coordinate to the right of the note</p>

        
          <div class='highlight'><pre>  getTieRightX() {
    <span class="hljs-keyword">let</span> tieStartX = <span class="hljs-keyword">this</span>.getAbsoluteX();
    <span class="hljs-keyword">const</span> note_glyph_width = <span class="hljs-keyword">this</span>.glyph.getWidth();
    tieStartX += note_glyph_width / <span class="hljs-number">2</span>;
    tieStartX += (-<span class="hljs-keyword">this</span>.width / <span class="hljs-number">2</span>) + <span class="hljs-keyword">this</span>.width + <span class="hljs-number">2</span>;

    <span class="hljs-keyword">return</span> tieStartX;
  }</pre></div>
        
      
        
        <p>Get the <code>x</code> coordinate to the left of the note</p>

        
          <div class='highlight'><pre>  getTieLeftX() {
    <span class="hljs-keyword">let</span> tieEndX = <span class="hljs-keyword">this</span>.getAbsoluteX();
    <span class="hljs-keyword">const</span> note_glyph_width = <span class="hljs-keyword">this</span>.glyph.getWidth();
    tieEndX += note_glyph_width / <span class="hljs-number">2</span>;
    tieEndX -= (<span class="hljs-keyword">this</span>.width / <span class="hljs-number">2</span>) + <span class="hljs-number">2</span>;

    <span class="hljs-keyword">return</span> tieEndX;
  }</pre></div>
        
      
        
        <p>Get the default <code>x</code> and <code>y</code> coordinates for a modifier at a specific
<code>position</code> at a fret position <code>index</code></p>

        
          <div class='highlight'><pre>  getModifierStartXY(position, index) {
    <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">let</span> x = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">if</span> (position === Modifier.Position.LEFT) {
      x = <span class="hljs-number">-1</span> * <span class="hljs-number">2</span>;  <span class="hljs-comment">// extra_left_px</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (position === Modifier.Position.RIGHT) {
      x = <span class="hljs-keyword">this</span>.width + <span class="hljs-number">2</span>; <span class="hljs-comment">// extra_right_px</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (position === Modifier.Position.BELOW || position === Modifier.Position.ABOVE) {
      <span class="hljs-keyword">const</span> note_glyph_width = <span class="hljs-keyword">this</span>.glyph.getWidth();
      x = note_glyph_width / <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>Get the default line for rest</p>

        
          <div class='highlight'><pre>  getLineForRest() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.positions[<span class="hljs-number">0</span>].str; }</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();</pre></div>
        
      
        
        <p>width is already set during init()</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.setPreFormatted(<span class="hljs-literal">true</span>);
  }</pre></div>
        
      
        
        <p>Get the x position for the stem</p>

        
          <div class='highlight'><pre>  getStemX() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.getCenterGlyphX(); }</pre></div>
        
      
        
        <p>Get the y position for the stem</p>

        
          <div class='highlight'><pre>  getStemY() {
    <span class="hljs-keyword">const</span> num_lines = <span class="hljs-keyword">this</span>.stave.getNumLines();</pre></div>
        
      
        
        <p>The decimal staff line amounts provide optimal spacing between the
fret number and the stem</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> stemUpLine = <span class="hljs-number">-0.5</span>;
    <span class="hljs-keyword">const</span> stemDownLine = num_lines - <span class="hljs-number">0.5</span>;
    <span class="hljs-keyword">const</span> stemStartLine = Stem.UP === <span class="hljs-keyword">this</span>.stem_direction ? stemUpLine : stemDownLine;

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.stave.getYForLine(stemStartLine);
  }</pre></div>
        
      
        
        <p>Get the stem extents for the tabnote</p>

        
          <div class='highlight'><pre>  getStemExtents() {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.stem.getExtents();
  }</pre></div>
        
      
        
        <p>Draw the fal onto the context</p>

        
          <div class='highlight'><pre>  drawFlag() {
    <span class="hljs-keyword">const</span> {
      beam, glyph, context, stem, stem_direction,
      <span class="hljs-attr">render_options</span>: { draw_stem, glyph_font_scale },
    } = <span class="hljs-keyword">this</span>;

    <span class="hljs-keyword">const</span> shouldDrawFlag = beam == <span class="hljs-literal">null</span> &amp;&amp; draw_stem;</pre></div>
        
      
        
        <p>Now it’s the flag’s turn.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (glyph.flag &amp;&amp; shouldDrawFlag) {
      <span class="hljs-keyword">const</span> flag_x = <span class="hljs-keyword">this</span>.getStemX() + <span class="hljs-number">1</span>;
      <span class="hljs-keyword">const</span> flag_y = <span class="hljs-keyword">this</span>.getStemY() - stem.getHeight();

      <span class="hljs-keyword">const</span> flag_code = stem_direction === Stem.DOWN
        ? glyph.code_flag_downstem <span class="hljs-comment">// Down stems have flags on the left.</span>
        : glyph.code_flag_upstem;</pre></div>
        
      
        
        <p>Draw the Flag</p>

        
          <div class='highlight'><pre>      Glyph.renderGlyph(context, flag_x, flag_y, glyph_font_scale, flag_code);
    }
  }</pre></div>
        
      
        
        <p>Render the modifiers onto the context</p>

        
          <div class='highlight'><pre>  drawModifiers() {</pre></div>
        
      
        
        <p>Draw the modifiers</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.modifiers.forEach(<span class="hljs-function">(<span class="hljs-params">modifier</span>) =&gt;</span> {</pre></div>
        
      
        
        <p>Only draw the dots if enabled</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (modifier.getCategory() === <span class="hljs-string">'dots'</span> &amp;&amp; !<span class="hljs-keyword">this</span>.render_options.draw_dots) <span class="hljs-keyword">return</span>;

      modifier.setContext(<span class="hljs-keyword">this</span>.context);
      modifier.drawWithStyle();
    });
  }</pre></div>
        
      
        
        <p>Render the stem extension through the fret positions</p>

        
          <div class='highlight'><pre>  drawStemThrough() {
    <span class="hljs-keyword">const</span> stem_x = <span class="hljs-keyword">this</span>.getStemX();
    <span class="hljs-keyword">const</span> stem_y = <span class="hljs-keyword">this</span>.getStemY();
    <span class="hljs-keyword">const</span> ctx = <span class="hljs-keyword">this</span>.context;

    <span class="hljs-keyword">const</span> stem_through = <span class="hljs-keyword">this</span>.render_options.draw_stem_through_stave;
    <span class="hljs-keyword">const</span> draw_stem = <span class="hljs-keyword">this</span>.render_options.draw_stem;
    <span class="hljs-keyword">if</span> (draw_stem &amp;&amp; stem_through) {
      <span class="hljs-keyword">const</span> total_lines = <span class="hljs-keyword">this</span>.stave.getNumLines();
      <span class="hljs-keyword">const</span> strings_used = <span class="hljs-keyword">this</span>.positions.map(<span class="hljs-function"><span class="hljs-params">position</span> =&gt;</span> position.str);

      <span class="hljs-keyword">const</span> unused_strings = getUnusedStringGroups(total_lines, strings_used);
      <span class="hljs-keyword">const</span> stem_lines = getPartialStemLines(
        stem_y,
        unused_strings,
        <span class="hljs-keyword">this</span>.getStave(),
        <span class="hljs-keyword">this</span>.getStemDirection()
      );

      ctx.save();
      ctx.setLineWidth(Stem.WIDTH);
      stem_lines.forEach(<span class="hljs-function"><span class="hljs-params">bounds</span> =&gt;</span> {
        <span class="hljs-keyword">if</span> (bounds.length === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>;

        ctx.beginPath();
        ctx.moveTo(stem_x, bounds[<span class="hljs-number">0</span>]);
        ctx.lineTo(stem_x, bounds[bounds.length - <span class="hljs-number">1</span>]);
        ctx.stroke();
        ctx.closePath();
      });
      ctx.restore();
    }
  }</pre></div>
        
      
        
        <p>Render the fret positions onto the context</p>

        
          <div class='highlight'><pre>  drawPositions() {
    <span class="hljs-keyword">const</span> ctx = <span class="hljs-keyword">this</span>.context;
    <span class="hljs-keyword">const</span> x = <span class="hljs-keyword">this</span>.getAbsoluteX();
    <span class="hljs-keyword">const</span> ys = <span class="hljs-keyword">this</span>.ys;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-keyword">this</span>.positions.length; ++i) {
      <span class="hljs-keyword">const</span> y = ys[i] + <span class="hljs-keyword">this</span>.render_options.y_shift;
      <span class="hljs-keyword">const</span> glyph = <span class="hljs-keyword">this</span>.glyphs[i];</pre></div>
        
      
        
        <p>Center the fret text beneath the notation note head</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> note_glyph_width = <span class="hljs-keyword">this</span>.glyph.getWidth();
      <span class="hljs-keyword">const</span> tab_x = x + (note_glyph_width / <span class="hljs-number">2</span>) - (glyph.getWidth() / <span class="hljs-number">2</span>);</pre></div>
        
      
        
        <p>FIXME: Magic numbers.</p>

        
          <div class='highlight'><pre>      ctx.clearRect(tab_x - <span class="hljs-number">2</span>, y - <span class="hljs-number">3</span>, glyph.getWidth() + <span class="hljs-number">4</span>, <span class="hljs-number">6</span>);

      <span class="hljs-keyword">if</span> (glyph.code) {
        Glyph.renderGlyph(ctx, tab_x, y,
          <span class="hljs-keyword">this</span>.render_options.glyph_font_scale * <span class="hljs-keyword">this</span>.render_options.scale,
          glyph.code);
      } <span class="hljs-keyword">else</span> {
        ctx.save();
        ctx.setRawFont(<span class="hljs-keyword">this</span>.render_options.font);
        <span class="hljs-keyword">const</span> text = glyph.text.toString();
        ctx.fillText(text, tab_x, y + <span class="hljs-number">5</span> * <span class="hljs-keyword">this</span>.render_options.scale);
        ctx.restore();
      }
    }
  }</pre></div>
        
      
        
        <p>The main rendering function for the entire note</p>

        
          <div class='highlight'><pre>  draw() {
    <span class="hljs-keyword">this</span>.checkContext();

    <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">this</span>.setRendered();
    <span class="hljs-keyword">const</span> render_stem = <span class="hljs-keyword">this</span>.beam == <span class="hljs-literal">null</span> &amp;&amp; <span class="hljs-keyword">this</span>.render_options.draw_stem;

    <span class="hljs-keyword">this</span>.drawPositions();
    <span class="hljs-keyword">this</span>.drawStemThrough();

    <span class="hljs-keyword">const</span> stem_x = <span class="hljs-keyword">this</span>.getStemX();

    <span class="hljs-keyword">this</span>.stem.setNoteHeadXBounds(stem_x, stem_x);

    <span class="hljs-keyword">if</span> (render_stem) {
      <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();
    }

    <span class="hljs-keyword">this</span>.drawFlag();
    <span class="hljs-keyword">this</span>.drawModifiers();
  }
}</pre></div>
        
      
      <div class="fleur">h</div>
    </div>
  </div>
</body>
</html>