UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature

1,294 lines (799 loc) 56.2 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="glyphnote.html"> glyphnote.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="repeatnote.html"> repeatnote.js </a> </li> <li> <a class="source" href="smufl.html"> smufl.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">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) { shiftL = <span class="hljs-built_in">Math</span>.max(note.getLeftDisplacedHeadPx(), 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</s