UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature

1,412 lines 55.7 kB
<!DOCTYPE html>

<html>
<head>
  <title>accidental.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>accidental.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.
@author Mohit Cheppudira
@author Greg Ristow (modifications)</p>
<h2 id="description">Description</h2>
<p>This file implements accidentals as modifiers that can be attached to
notes. Support is included for both western and microtonal accidentals.</p>
<p>See <code>tests/accidental_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> { Fraction } <span class="hljs-keyword">from</span> <span class="hljs-string">'./fraction'</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> { Music } <span class="hljs-keyword">from</span> <span class="hljs-string">'./music'</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> { Glyph } <span class="hljs-keyword">from</span> <span class="hljs-string">'./glyph'</span>;</pre></div>
        
      
        
        <p>To enable logging for this class. Set <code>Vex.Flow.Accidental.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> (Accidental.DEBUG) Vex.L(<span class="hljs-string">'Vex.Flow.Accidental'</span>, args); }

<span class="hljs-keyword">const</span> getGlyphWidth = <span class="hljs-function"><span class="hljs-params">glyph</span> =&gt;</span> glyph.getMetrics().width;</pre></div>
        
      
        
        <p>An <code>Accidental</code> inherits from <code>Modifier</code>, and is formatted within a
<code>ModifierContext</code>.</p>

        
          <div class='highlight'><pre><span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Accidental</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Modifier</span> </span>{
  <span class="hljs-keyword">static</span> get CATEGORY() { <span class="hljs-keyword">return</span> <span class="hljs-string">'accidentals'</span>; }</pre></div>
        
      
        
        <p>Arrange accidentals inside a ModifierContext.</p>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">static</span> format(accidentals, state) {
    <span class="hljs-keyword">const</span> noteheadAccidentalPadding = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">const</span> leftShift = state.left_shift + noteheadAccidentalPadding;
    <span class="hljs-keyword">const</span> accidentalSpacing = <span class="hljs-number">3</span>;</pre></div>
        
      
        
        <p>If there are no accidentals, we needn’t format their positions</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (!accidentals || accidentals.length === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">const</span> accList = [];
    <span class="hljs-keyword">let</span> prevNote = <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">let</span> shiftL = <span class="hljs-number">0</span>;</pre></div>
        
      
        
        <p>First determine the accidentals’ Y positions from the note.keys</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">let</span> propsTemp;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; accidentals.length; ++i) {
      <span class="hljs-keyword">const</span> acc = accidentals[i];
      <span class="hljs-keyword">const</span> note = acc.getNote();
      <span class="hljs-keyword">const</span> stave = note.getStave();
      <span class="hljs-keyword">const</span> props = note.getKeyProps()[acc.getIndex()];
      <span class="hljs-keyword">if</span> (note !== prevNote) {</pre></div>
        
      
        
        <p>Iterate through all notes to get the displaced pixels</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> n = <span class="hljs-number">0</span>; n &lt; note.keys.length; ++n) {
          propsTemp = note.getKeyProps()[n];
          shiftL = propsTemp.displaced ? note.getExtraLeftPx() : shiftL;
        }
        prevNote = note;
      }
      <span class="hljs-keyword">if</span> (stave !== <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">const</span> lineSpace = stave.options.spacing_between_lines_px;
        <span class="hljs-keyword">const</span> y = stave.getYForLine(props.line);
        <span class="hljs-keyword">const</span> accLine = <span class="hljs-built_in">Math</span>.round(y / lineSpace * <span class="hljs-number">2</span>) / <span class="hljs-number">2</span>;
        accList.push({ y, <span class="hljs-attr">line</span>: accLine, <span class="hljs-attr">shift</span>: shiftL, acc, lineSpace });
      } <span class="hljs-keyword">else</span> {
        accList.push({ <span class="hljs-attr">line</span>: props.line, <span class="hljs-attr">shift</span>: shiftL, acc });
      }
    }</pre></div>
        
      
        
        <p>Sort accidentals by line number.</p>

        
          <div class='highlight'><pre>    accList.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> b.line - a.line);</pre></div>
        
      
        
        <p>FIXME: Confusing name. Each object in this array has a property called <code>line</code>.
So if this is a list of lines, you end up with: <code>line.line</code> which is very awkward.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> lineList = [];</pre></div>
        
      
        
        <p>amount by which all accidentals must be shifted right or left for
stem flipping, notehead shifting concerns.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">let</span> accShift = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> previousLine = <span class="hljs-literal">null</span>;</pre></div>
        
      
        
        <p>Create an array of unique line numbers (lineList) from accList</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 &lt; accList.length; i++) {
      <span class="hljs-keyword">const</span> acc = accList[i];</pre></div>
        
      
        
        <p>if this is the first line, or a new line, add a lineList</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (previousLine === <span class="hljs-literal">null</span> || previousLine !== acc.line) {
        lineList.push({
          <span class="hljs-attr">line</span>: acc.line,
          <span class="hljs-attr">flatLine</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">dblSharpLine</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-attr">numAcc</span>: <span class="hljs-number">0</span>,
          <span class="hljs-attr">width</span>: <span class="hljs-number">0</span>,
        });
      }</pre></div>
        
      
        
        <p>if this accidental is not a flat, the accidental needs 3.0 lines lower
clearance instead of 2.5 lines for b or bb.
FIXME: Naming could use work. acc.acc is very awkward</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (acc.acc.type !== <span class="hljs-string">'b'</span> &amp;&amp; acc.acc.type !== <span class="hljs-string">'bb'</span>) {
        lineList[lineList.length - <span class="hljs-number">1</span>].flatLine = <span class="hljs-literal">false</span>;
      }</pre></div>
        
      
        
        <p>if this accidental is not a double sharp, the accidental needs 3.0 lines above</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (acc.acc.type !== <span class="hljs-string">'##'</span>) {
        lineList[lineList.length - <span class="hljs-number">1</span>].dblSharpLine = <span class="hljs-literal">false</span>;
      }</pre></div>
        
      
        
        <p>Track how many accidentals are on this line:</p>

        
          <div class='highlight'><pre>      lineList[lineList.length - <span class="hljs-number">1</span>].numAcc++;</pre></div>
        
      
        
        <p>Track the total x_offset needed for this line which will be needed
for formatting lines w/ multiple accidentals:</p>

        
      
        
        <p>width = accidental width + universal spacing between accidentals</p>

        
          <div class='highlight'><pre>      lineList[lineList.length - <span class="hljs-number">1</span>].width += acc.acc.getWidth() + accidentalSpacing;</pre></div>
        
      
        
        <p>if this accShift is larger, use it to keep first column accidentals in the same line</p>

        
          <div class='highlight'><pre>      accShift = acc.shift &gt; accShift ? acc.shift : accShift;

      previousLine = acc.line;
    }</pre></div>
        
      
        
        <h3 id="place-accidentals-in-columns">Place Accidentals in Columns</h3>
<p>Default to a classic triangular layout (middle accidental farthest left),
but follow exceptions as outlined in G. Read’s <em>Music Notation</em> and
Elaine Gould’s <em>Behind Bars</em>.</p>
<p>Additionally, this implements different vertical collision rules for
flats (only need 2.5 lines clearance below) and double sharps (only
need 2.5 lines of clearance above or below).</p>
<p>Classic layouts and exception patterns are found in the ‘tables.js’
in ‘Vex.Flow.accidentalColumnsTable’</p>
<p>Beyond 6 vertical accidentals, default to the parallel ascending lines approach,
using as few columns as possible for the verticle structure.</p>
<p>TODO (?): Allow column to be specified for an accidental at run-time?</p>

        
          <div class='highlight'><pre>
    <span class="hljs-keyword">let</span> totalColumns = <span class="hljs-number">0</span>;</pre></div>
        
      
        
        <p>establish the boundaries for a group of notes with clashing accidentals:</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 &lt; lineList.length; i++) {
      <span class="hljs-keyword">let</span> noFurtherConflicts = <span class="hljs-literal">false</span>;
      <span class="hljs-keyword">const</span> groupStart = i;
      <span class="hljs-keyword">let</span> groupEnd = i;

      <span class="hljs-keyword">while</span> (groupEnd + <span class="hljs-number">1</span> &lt; lineList.length &amp;&amp; !noFurtherConflicts) {</pre></div>
        
      
        
        <p>if this note conflicts with the next:</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.checkCollision(lineList[groupEnd], lineList[groupEnd + <span class="hljs-number">1</span>])) {</pre></div>
        
      
        
        <p>include the next note in the group:</p>

        
          <div class='highlight'><pre>          groupEnd++;
        } <span class="hljs-keyword">else</span> {
          noFurtherConflicts = <span class="hljs-literal">true</span>;
        }
      }</pre></div>
        
      
        
        <p>Gets an a line from the <code>lineList</code>, relative to the current group</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> getGroupLine = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> lineList[groupStart + index];
      <span class="hljs-keyword">const</span> getGroupLines = <span class="hljs-function">(<span class="hljs-params">indexes</span>) =&gt;</span> indexes.map(getGroupLine);
      <span class="hljs-keyword">const</span> lineDifference = <span class="hljs-function">(<span class="hljs-params">indexA, indexB</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> [a, b] = getGroupLines([indexA, indexB]).map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> item.line);
        <span class="hljs-keyword">return</span> a - b;
      };

      <span class="hljs-keyword">const</span> notColliding = <span class="hljs-function">(<span class="hljs-params">...indexPairs</span>) =&gt;</span>
        indexPairs
          .map(getGroupLines)
          .every(<span class="hljs-function"><span class="hljs-params">lines</span> =&gt;</span> !<span class="hljs-keyword">this</span>.checkCollision(...lines));</pre></div>
        
      
        
        <p>Set columns for the lines in this group:</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> groupLength = groupEnd - groupStart + <span class="hljs-number">1</span>;</pre></div>
        
      
        
        <p>Set the accidental column for each line of the group</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">let</span> endCase = <span class="hljs-keyword">this</span>.checkCollision(lineList[groupStart], lineList[groupEnd]) ? <span class="hljs-string">'a'</span> : <span class="hljs-string">'b'</span>;

      <span class="hljs-keyword">switch</span> (groupLength) {
        <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
          <span class="hljs-keyword">if</span> (endCase === <span class="hljs-string">'a'</span> &amp;&amp; lineDifference(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>) === <span class="hljs-number">0.5</span> &amp;&amp; lineDifference(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>) !== <span class="hljs-number">0.5</span>) {
            endCase = <span class="hljs-string">'second_on_bottom'</span>;
          }
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-number">4</span>:
          <span class="hljs-keyword">if</span> (notColliding([<span class="hljs-number">0</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">3</span>])) {
            endCase = <span class="hljs-string">'spaced_out_tetrachord'</span>;
          }
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-number">5</span>:
          <span class="hljs-keyword">if</span> (endCase === <span class="hljs-string">'b'</span> &amp;&amp; notColliding([<span class="hljs-number">1</span>, <span class="hljs-number">3</span>])) {
            endCase = <span class="hljs-string">'spaced_out_pentachord'</span>;
            <span class="hljs-keyword">if</span> (notColliding([<span class="hljs-number">0</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>])) {
              endCase = <span class="hljs-string">'very_spaced_out_pentachord'</span>;
            }
          }
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-number">6</span>:
          <span class="hljs-keyword">if</span> (notColliding([<span class="hljs-number">0</span>, <span class="hljs-number">3</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">4</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">5</span>])) {
            endCase = <span class="hljs-string">'spaced_out_hexachord'</span>;
          }
          <span class="hljs-keyword">if</span> (notColliding([<span class="hljs-number">0</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">3</span>], [<span class="hljs-number">3</span>, <span class="hljs-number">5</span>])) {
            endCase = <span class="hljs-string">'very_spaced_out_hexachord'</span>;
          }
          <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">default</span>:
          <span class="hljs-keyword">break</span>;
      }

      <span class="hljs-keyword">let</span> groupMember;
      <span class="hljs-keyword">let</span> column;</pre></div>
        
      
        
        <p>If the group contains more than seven members, use ascending parallel lines
of accidentals, using as few columns as possible while avoiding collisions.</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (groupLength &gt;= <span class="hljs-number">7</span>) {</pre></div>
        
      
        
        <p>First, determine how many columns to use:</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">let</span> patternLength = <span class="hljs-number">2</span>;
        <span class="hljs-keyword">let</span> collisionDetected = <span class="hljs-literal">true</span>;
        <span class="hljs-keyword">while</span> (collisionDetected === <span class="hljs-literal">true</span>) {
          collisionDetected = <span class="hljs-literal">false</span>;
          <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> line = <span class="hljs-number">0</span>; line + patternLength &lt; lineList.length; line++) {
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.checkCollision(lineList[line], lineList[line + patternLength])) {
              collisionDetected = <span class="hljs-literal">true</span>;
              patternLength++;
              <span class="hljs-keyword">break</span>;
            }
          }
        }</pre></div>
        
      
        
        <p>Then, assign a column to each line of accidentals</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">for</span> (groupMember = i; groupMember &lt;= groupEnd; groupMember++) {
          column = ((groupMember - i) % patternLength) + <span class="hljs-number">1</span>;
          lineList[groupMember].column = column;
          totalColumns = (totalColumns &gt; column) ? totalColumns : column;
        }</pre></div>
        
      
        
        <p>Otherwise, if the group contains fewer than seven members, use the layouts from
the accidentalsColumnsTable housed in tables.js.</p>

        
          <div class='highlight'><pre>      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">for</span> (groupMember = i; groupMember &lt;= groupEnd; groupMember++) {
          column = Flow.accidentalColumnsTable[groupLength][endCase][groupMember - i];
          lineList[groupMember].column = column;
          totalColumns = (totalColumns &gt; column) ? totalColumns : column;
        }
      }</pre></div>
        
      
        
        <p>Increment i to the last note that was set, so that if a lower set of notes
does not conflict at all with this group, it can have its own classic shape.</p>

        
          <div class='highlight'><pre>      i = groupEnd;
    }</pre></div>
        
      
        
        <h3 id="convert-columns-to-x_offsets">Convert Columns to x_offsets</h3>
<p>This keeps columns aligned, even if they have different accidentals within them
which sometimes results in a larger x_offset than is an accidental might need
to preserve the symmetry of the accidental shape.</p>
<p>Neither A.C. Vinci nor G. Read address this, and it typically only happens in
music with complex chord clusters.</p>
<p>TODO (?): Optionally allow closer compression of accidentals, instead of forcing
parallel columns.</p>

        
      
        
        <p>track each column’s max width, which will be used as initial shift of later columns:</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> columnWidths = [];
    <span class="hljs-keyword">const</span> columnXOffsets = [];
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt;= totalColumns; i++) {
      columnWidths[i] = <span class="hljs-number">0</span>;
      columnXOffsets[i] = <span class="hljs-number">0</span>;
    }

    columnWidths[<span class="hljs-number">0</span>] = accShift + leftShift;
    columnXOffsets[<span class="hljs-number">0</span>] = accShift + leftShift;</pre></div>
        
      
        
        <p>Fill columnWidths with widest needed x-space;
this is what keeps the columns parallel.</p>

        
          <div class='highlight'><pre>    lineList.forEach(<span class="hljs-function"><span class="hljs-params">line</span> =&gt;</span> {
      <span class="hljs-keyword">if</span> (line.width &gt; columnWidths[line.column]) columnWidths[line.column] = line.width;
    });

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">1</span>; i &lt; columnWidths.length; i++) {</pre></div>
        
      
        
        <p>this column’s offset = this column’s width + previous column’s offset</p>

        
          <div class='highlight'><pre>      columnXOffsets[i] = columnWidths[i] + columnXOffsets[i - <span class="hljs-number">1</span>];
    }

    <span class="hljs-keyword">const</span> totalShift = columnXOffsets[columnXOffsets.length - <span class="hljs-number">1</span>];</pre></div>
        
      
        
        <p>Set the xShift for each accidental according to column offsets:</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">let</span> accCount = <span class="hljs-number">0</span>;
    lineList.forEach(<span class="hljs-function"><span class="hljs-params">line</span> =&gt;</span> {
      <span class="hljs-keyword">let</span> lineWidth = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">const</span> lastAccOnLine = accCount + line.numAcc;</pre></div>
        
      
        
        <p>handle all of the accidentals on a given line:</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">for</span> (accCount; accCount &lt; lastAccOnLine; accCount++) {
        <span class="hljs-keyword">const</span> xShift = (columnXOffsets[line.column - <span class="hljs-number">1</span>] + lineWidth);
        accList[accCount].acc.setXShift(xShift);</pre></div>
        
      
        
        <p>keep track of the width of accidentals we’ve added so far, so that when
we loop, we add space for them.</p>

        
          <div class='highlight'><pre>        lineWidth += accList[accCount].acc.getWidth() + accidentalSpacing;
        L(<span class="hljs-string">'Line, accCount, shift: '</span>, line.line, accCount, xShift);
      }
    });</pre></div>
        
      
        
        <p>update the overall layout with the full width of the accidental shapes:</p>

        
          <div class='highlight'><pre>    state.left_shift += totalShift;
  }</pre></div>
        
      
        
        <p>Helper function to determine whether two lines of accidentals collide vertically</p>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">static</span> checkCollision(line1, line2) {
    <span class="hljs-keyword">let</span> clearance = line2.line - line1.line;
    <span class="hljs-keyword">let</span> clearanceRequired = <span class="hljs-number">3</span>;</pre></div>
        
      
        
        <p>But less clearance is required for certain accidentals: b, bb and ##.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (clearance &gt; <span class="hljs-number">0</span>) { <span class="hljs-comment">// then line 2 is on top</span>
      clearanceRequired = (line2.flatLine || line2.dblSharpLine) ? <span class="hljs-number">2.5</span> : <span class="hljs-number">3.0</span>;
      <span class="hljs-keyword">if</span> (line1.dblSharpLine) clearance -= <span class="hljs-number">0.5</span>;
    } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// line 1 is on top</span>
      clearanceRequired = (line1.flatLine || line1.dblSharpLine) ? <span class="hljs-number">2.5</span> : <span class="hljs-number">3.0</span>;
      <span class="hljs-keyword">if</span> (line2.dblSharpLine) clearance -= <span class="hljs-number">0.5</span>;
    }
    <span class="hljs-keyword">const</span> collision = <span class="hljs-built_in">Math</span>.abs(clearance) &lt; clearanceRequired;
    L(<span class="hljs-string">'Line_1, Line_2, Collision: '</span>, line1.line, line2.line, collision);
    <span class="hljs-keyword">return</span> collision;
  }</pre></div>
        
      
        
        <p>Use this method to automatically apply accidentals to a set of <code>voices</code>.
The accidentals will be remembered between all the voices provided.
Optionally, you can also provide an initial <code>keySignature</code>.</p>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">static</span> applyAccidentals(voices, keySignature) {
    <span class="hljs-keyword">const</span> tickPositions = [];
    <span class="hljs-keyword">const</span> tickNoteMap = {};</pre></div>
        
      
        
        <p>Sort the tickables in each voice by their tick position in the voice</p>

        
          <div class='highlight'><pre>    voices.forEach(<span class="hljs-function"><span class="hljs-params">voice</span> =&gt;</span> {
      <span class="hljs-keyword">const</span> tickPosition = <span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);
      <span class="hljs-keyword">const</span> notes = voice.getTickables();
      notes.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> {
        <span class="hljs-keyword">if</span> (note.shouldIgnoreTicks()) <span class="hljs-keyword">return</span>;

        <span class="hljs-keyword">const</span> notesAtPosition = tickNoteMap[tickPosition.value()];

        <span class="hljs-keyword">if</span> (!notesAtPosition) {
          tickPositions.push(tickPosition.value());
          tickNoteMap[tickPosition.value()] = [note];
        } <span class="hljs-keyword">else</span> {
          notesAtPosition.push(note);
        }

        tickPosition.add(note.getTicks());
      });
    });

    <span class="hljs-keyword">const</span> music = <span class="hljs-keyword">new</span> Music();</pre></div>
        
      
        
        <p>Default key signature is C major</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (!keySignature) keySignature = <span class="hljs-string">'C'</span>;</pre></div>
        
      
        
        <p>Get the scale map, which represents the current state of each pitch</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> scaleMap = music.createScaleMap(keySignature);

    tickPositions.forEach(<span class="hljs-function"><span class="hljs-params">tick</span> =&gt;</span> {
      <span class="hljs-keyword">const</span> notes = tickNoteMap[tick];</pre></div>
        
      
        
        <p>Array to store all pitches that modified accidental states
at this tick position</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> modifiedPitches = [];

      <span class="hljs-keyword">const</span> processNote = <span class="hljs-function">(<span class="hljs-params">note</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (note.isRest() || note.shouldIgnoreTicks()) <span class="hljs-keyword">return</span>;</pre></div>
        
      
        
        <p>Go through each key and determine if an accidental should be
applied</p>

        
          <div class='highlight'><pre>        note.keys.forEach(<span class="hljs-function">(<span class="hljs-params">keyString, keyIndex</span>) =&gt;</span> {
          <span class="hljs-keyword">const</span> key = music.getNoteParts(keyString.split(<span class="hljs-string">'/'</span>)[<span class="hljs-number">0</span>]);</pre></div>
        
      
        
        <p>Force a natural for every key without an accidental</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">const</span> accidentalString = key.accidental || <span class="hljs-string">'n'</span>;
          <span class="hljs-keyword">const</span> pitch = key.root + accidentalString;</pre></div>
        
      
        
        <p>Determine if the current pitch has the same accidental
as the scale state</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">const</span> sameAccidental = scaleMap[key.root] === pitch;</pre></div>
        
      
        
        <p>Determine if an identical pitch in the chord already
modified the accidental state</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">const</span> previouslyModified = modifiedPitches.indexOf(pitch) &gt; <span class="hljs-number">-1</span>;</pre></div>
        
      
        
        <p>Add the accidental to the StaveNote</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">if</span> (!sameAccidental || (sameAccidental &amp;&amp; previouslyModified)) {</pre></div>
        
      
        
        <p>Modify the scale map so that the root pitch has an
updated state</p>

        
          <div class='highlight'><pre>            scaleMap[key.root] = pitch;</pre></div>
        
      
        
        <p>Create the accidental</p>

        
          <div class='highlight'><pre>            <span class="hljs-keyword">const</span> accidental = <span class="hljs-keyword">new</span> Accidental(accidentalString);</pre></div>
        
      
        
        <p>Attach the accidental to the StaveNote</p>

        
          <div class='highlight'><pre>            note.addAccidental(keyIndex, accidental);</pre></div>
        
      
        
        <p>Add the pitch to list of pitches that modified accidentals</p>

        
          <div class='highlight'><pre>            modifiedPitches.push(pitch);
          }
        });</pre></div>
        
      
        
        <p>process grace notes</p>

        
          <div class='highlight'><pre>        note.getModifiers().forEach(<span class="hljs-function"><span class="hljs-params">modifier</span> =&gt;</span> {
          <span class="hljs-keyword">if</span> (modifier.getCategory() === <span class="hljs-string">'gracenotegroups'</span>) {
            modifier.getGraceNotes().forEach(processNote);
          }
        });
      };

      notes.forEach(processNote);
    });
  }</pre></div>
        
      
        
        <p>Create accidental. <code>type</code> can be a value from the
<code>Vex.Flow.accidentalCodes.accidentals</code> table in <code>tables.js</code>. For
example: <code>#</code>, <code>##</code>, <code>b</code>, <code>n</code>, etc.</p>

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

    L(<span class="hljs-string">'New accidental: '</span>, type);

    <span class="hljs-keyword">this</span>.note = <span class="hljs-literal">null</span>;</pre></div>
        
      
        
        <p>The <code>index</code> points to a specific note in a chord.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.index = <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">this</span>.type = type;
    <span class="hljs-keyword">this</span>.position = Modifier.Position.LEFT;

    <span class="hljs-keyword">this</span>.render_options = {</pre></div>
        
      
        
        <p>Font size for glyphs</p>

        
          <div class='highlight'><pre>      font_scale: <span class="hljs-number">38</span>,</pre></div>
        
      
        
        <p>Length of stroke across heads above or below the stave.</p>

        
          <div class='highlight'><pre>      stroke_px: <span class="hljs-number">3</span>,</pre></div>
        
      
        
        <p>Padding between accidental and parentheses on each side</p>

        
          <div class='highlight'><pre>      parenLeftPadding: <span class="hljs-number">2</span>,
      <span class="hljs-attr">parenRightPadding</span>: <span class="hljs-number">2</span>,
    };

    <span class="hljs-keyword">this</span>.accidental = Flow.accidentalCodes(<span class="hljs-keyword">this</span>.type);
    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.accidental) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'ArgumentError'</span>, <span class="hljs-string">`Unknown accidental type: <span class="hljs-subst">${type}</span>`</span>);
    }</pre></div>
        
      
        
        <p>Cautionary accidentals have parentheses around them</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.cautionary = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">this</span>.parenLeft = <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">this</span>.parenRight = <span class="hljs-literal">null</span>;

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

  reset() {
    <span class="hljs-keyword">const</span> fontScale = <span class="hljs-keyword">this</span>.render_options.font_scale;
    <span class="hljs-keyword">this</span>.glyph = <span class="hljs-keyword">new</span> Glyph(<span class="hljs-keyword">this</span>.accidental.code, fontScale);
    <span class="hljs-keyword">this</span>.glyph.setOriginX(<span class="hljs-number">1.0</span>);

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.cautionary) {
      <span class="hljs-keyword">this</span>.parenLeft = <span class="hljs-keyword">new</span> Glyph(Flow.accidentalCodes(<span class="hljs-string">'{'</span>).code, fontScale);
      <span class="hljs-keyword">this</span>.parenRight = <span class="hljs-keyword">new</span> Glyph(Flow.accidentalCodes(<span class="hljs-string">'}'</span>).code, fontScale);
      <span class="hljs-keyword">this</span>.parenLeft.setOriginX(<span class="hljs-number">1.0</span>);
      <span class="hljs-keyword">this</span>.parenRight.setOriginX(<span class="hljs-number">1.0</span>);
    }
  }

  getCategory() { <span class="hljs-keyword">return</span> Accidental.CATEGORY; }

  getWidth() {
    <span class="hljs-keyword">const</span> parenWidth = <span class="hljs-keyword">this</span>.cautionary
      ? (
        getGlyphWidth(<span class="hljs-keyword">this</span>.parenLeft) +
        getGlyphWidth(<span class="hljs-keyword">this</span>.parenRight) +
        <span class="hljs-keyword">this</span>.render_options.parenLeftPadding +
        <span class="hljs-keyword">this</span>.render_options.parenRightPadding
      )
      : <span class="hljs-number">0</span>;

    <span class="hljs-keyword">return</span> getGlyphWidth(<span class="hljs-keyword">this</span>.glyph) + parenWidth;
  }</pre></div>
        
      
        
        <p>Attach this accidental to <code>note</code>, which must be a <code>StaveNote</code>.</p>

        
          <div class='highlight'><pre>  setNote(note) {
    <span class="hljs-keyword">if</span> (!note) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'ArgumentError'</span>, <span class="hljs-string">`Bad note value: <span class="hljs-subst">${note}</span>`</span>);
    }

    <span class="hljs-keyword">this</span>.note = note;</pre></div>
        
      
        
        <p>Accidentals attached to grace notes are rendered smaller.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.note.getCategory() === <span class="hljs-string">'gracenotes'</span>) {
      <span class="hljs-keyword">this</span>.render_options.font_scale = <span class="hljs-number">25</span>;
      <span class="hljs-keyword">this</span>.reset();
    }
  }</pre></div>
        
      
        
        <p>If called, draws parenthesis around accidental.</p>

        
          <div class='highlight'><pre>  setAsCautionary() {
    <span class="hljs-keyword">this</span>.cautionary = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">this</span>.render_options.font_scale = <span class="hljs-number">28</span>;
    <span class="hljs-keyword">this</span>.reset();
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
  }</pre></div>
        
      
        
        <p>Render accidental onto canvas.</p>

        
          <div class='highlight'><pre>  draw() {
    <span class="hljs-keyword">const</span> {
      context,
      type, position, note, index, cautionary,
      x_shift, y_shift,
      glyph, parenLeft, parenRight,
      <span class="hljs-attr">render_options</span>: { parenLeftPadding, parenRightPadding },
    } = <span class="hljs-keyword">this</span>;

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

    <span class="hljs-keyword">if</span> (!(note &amp;&amp; (index != <span class="hljs-literal">null</span>))) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RERR(<span class="hljs-string">'NoAttachedNote'</span>, <span class="hljs-string">"Can't draw accidental without a note and index."</span>);
    }</pre></div>
        
      
        
        <p>Figure out the start <code>x</code> and <code>y</code> coordinates for note and index.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> start = note.getModifierStartXY(position, index);
    <span class="hljs-keyword">let</span> accX = start.x + x_shift;
    <span class="hljs-keyword">const</span> accY = start.y + y_shift;
    L(<span class="hljs-string">'Rendering: '</span>, type, accX, accY);

    <span class="hljs-keyword">if</span> (!cautionary) {
      glyph.render(context, accX, accY);
    } <span class="hljs-keyword">else</span> {</pre></div>
        
      
        
        <p>Render the accidental in parentheses.</p>

        
          <div class='highlight'><pre>      parenRight.render(context, accX, accY);
      accX -= getGlyphWidth(parenRight);
      accX -= parenRightPadding;
      accX -= <span class="hljs-keyword">this</span>.accidental.parenRightPaddingAdjustment;
      glyph.render(context, accX, accY);
      accX -= getGlyphWidth(glyph);
      accX -= parenLeftPadding;
      parenLeft.render(context, accX, accY);
    }

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